안드로이드(JavaScript)
안드로이드 환경에서 JavaScript만을 사용해 채널톡 SDK를 사용하기 위해서는 아래와 같은 설정이 필요합니다.
JavaScript SDK만 사용하는 경우, 푸시 알림 기능을 지원하지 않습니다.
푸시 알림 기능을 사용하려면 안드로이드(SDK)를 참고합니다.
새 탭 허용하기
안드로이드 웹뷰는 기본적으로 새 탭을 허용하지 않기 때문에, 이를 허용하기 위한 코드를 추가로 작성해야 합니다.
webChromeClient = object : WebChromeClient() {
override fun onCreateWindow(
view: WebView?,
isDialog: Boolean,
isUserGesture: Boolean,
resultMsg: Message?
): Boolean {
// intercept new window
val transport = resultMsg?.obj as? WebView.WebViewTransport
val newWebView = WebView(this@WebViewActivity)
view?.addView(newWebView)
transport?.webView = newWebView
resultMsg?.sendToTarget()
newWebView.webViewClient = object : WebViewClient() {
// handle url navigating and create new activity
override fun shouldOverrideUrlLoading(
view: WebView,
url: String
): Boolean {
return when {
url.startsWith("tel:") -> {
val intent = Intent(Intent.ACTION_DIAL, Uri.parse(url))
startActivity(intent)
true
}
url.startsWith("mailto:") -> {
val intent = Intent(Intent.ACTION_SENDTO, Uri.parse(url))
startActivity(intent)
true
}
else -> {
Intent(view.context, WebViewActivity::class.java)
.apply { putExtra("url", url) }
.also { intent -> startActivity(intent) }
true
}
}
}
}
return true
}
}
파일 업로드
안드로이드 웹뷰는 기본적으로 파일 선택 동작을 구현해놓지 않기 때문에, 이를 구현하는 코드를 추가로 작성해야 합니다.
모바일 환경에서 조금 더 쉽고 직관적으로 채널톡 SDK를 활용하기를 원한다면 안드로이드 SDK를 사용할 것을 권장합니다.
- 웹뷰가 파일에 접근하는 것을 허용해준 후, 유저가 파일 선택기를 열고자 하는 순간을 감지할 수 있도록
WebChromeClient
를 설정합니다.
yourWebViewInstance.apply {
settings.apply {
// -- your other WebView options --
// add this line to make WebView to access file system
allowFileAccess = true
}
webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
// -- snip --
}
}
- 아래
WebViewAttachmentModule
클래스를 복사해 사용합니다.WebViewAttachmentModule
는 파일 선택기 콜백을 동작시킵니다.
onShowFileChooser
는 true를 반환(return)해야 합니다.
WebChromeClient
는onShowFileChooser
가false
를 반환하는 경우filePathCallback
이 호출되는 것을 허용하지 않습니다.getIntentChooser
는 항상filePathCallback
을 호출하기 때문에onShowFileChooser
가true
를 반환할 때만getIntentChooser
를 호출해야 합니다.
class WebViewAttachmentModule {
private var filePathCallback: ValueCallback<Array<Uri>>? = null
fun getIntentChooser(filePathCallback: ValueCallback<Array<Uri>>, acceptTypes: Array<String>): Intent {
// init filePathCallback
this.filePathCallback?.onReceiveValue(null)
this.filePathCallback = filePathCallback
return Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, getMimeTypesFromAcceptTypes(acceptTypes))
}
}
// handle onActivityResult process in activity
fun handleResult(requestCode: Int, resultCode: Int, data: Intent?) {
val uri = data?.data
if (requestCode == WEB_VIEW_FILE_UPLOAD_REQUEST_CODE && resultCode == AppCompatActivity.RESULT_OK && uri != null) {
filePathCallback?.onReceiveValue(arrayOf(uri))
} else {
filePathCallback?.onReceiveValue(null)
}
filePathCallback = null
}
private fun getMimeTypesFromAcceptTypes(acceptTypes: Array<String>): Array<String> {
return (acceptTypes
.mapNotNull { acceptType ->
when {
// Seperate case of extensions and MIME Type
acceptType.startsWith(".") -> MimeTypeMap.getSingleton().getMimeTypeFromExtension(acceptType.substring(1))
else -> acceptType
}
}
// setting "*/*" when acceptType is not exist
.filter { it.isNotBlank() }
.takeIf { it.isNotEmpty() }
?: listOf("*/*"))
.toTypedArray()
}
}
예시 코드
참고 코드이므로 상황에 맞게 변경해 사용하는 것을 추천합니다.
class WebViewActivity : AppCompatActivity() {
private val attachmentModule = WebViewAttachmentModule()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_web_view)
webview.webViewClient = WebViewClient()
val url = intent.getStringExtra("url") ?: SOME_DEFAULT_URL
webview.apply {
settings.apply {
// 웹뷰 설정
javaScriptEnabled = true
domStorageEnabled = true
useWideViewPort = true
allowFileAccess = true
setSupportMultipleWindows(true)
javaScriptCanOpenWindowsAutomatically = true
}
webChromeClient = object : WebChromeClient() {
// 파일 업로드 처리
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
startActivityForResult(
attachmentModule.getIntentChooser(
filePathCallback,
fileChooserParams.acceptTypes,
),
Const.WEB_VIEW_FILE_UPLOAD_REQUEST_CODE,
) // 여기서 true를 반환할 때만 getIntentChooser()를 호출해야 합니다.
return true
}
override fun onCreateWindow(
view: WebView?,
isDialog: Boolean,
isUserGesture: Boolean,
resultMsg: Message?
): Boolean {
// 새로운 윈도우 관련 처리
val transport = resultMsg?.obj as? WebView.WebViewTransport
val newWebView = WebView(this@WebViewActivity)
view?.addView(newWebView)
transport?.webView = newWebView
resultMsg?.sendToTarget()
newWebView.webViewClient = object : WebViewClient() {
// URL 이동 관련 처리
override fun shouldOverrideUrlLoading(
view: WebView,
url: String
): Boolean {
return when {
url.startsWith("tel:") -> {
val intent = Intent(Intent.ACTION_DIAL, Uri.parse(url))
startActivity(intent)
true
}
url.startsWith("mailto:") -> {
val intent = Intent(Intent.ACTION_SENDTO, Uri.parse(url))
startActivity(intent)
true
}
else -> {
Intent(view.context, WebViewActivity::class.java)
.apply { putExtra("url", url) }
.also { intent -> startActivity(intent) }
true
}
}
}
}
return true
}
}
}
webview.loadUrl(url)
}
// handle file upload
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
attachmentModule.handleResult(requestCode, resultCode, data)
super.onActivityResult(requestCode, resultCode, data)
}
}
안드로이드(SDK)
웹뷰 안에 JavaScript SDK를 내장시키는 방식을 사용하지 않고 안드로이드 SDK를 사용하는 경우 아래와 같은 방법을 사용할 수 있습니다. 안드로이드 SDK를 사용할 경우, 푸시 알림 기능을 함께 사용할 수 있습니다. 안드로이드 SDK 시작하기와 푸시 알림을 참고합니다.
채널톡 SDK의 UI는 하나만 표시해야 합니다.
이미 채널톡 JavaScript SDK와 Android SDK를 동시에 사용하고 있다면 JavaScript SDK Boot Option의 hideChannelButtonOnBoot와 hidePopup=true로 설정하여 채널톡 버튼과 팝업을 숨겨야 합니다. 안드로이드 SDK와 JavaScript SDK를 동시에 사용하는 경우에는 같은 UI가 화면에 중복 표시될 수 있습니다.
안드로이드 SDK가 현재 URL 정보를 알 수 있도록, setPage
를 사용하기를 권장합니다. 안드로이드 SDK는 Page 정보를 통해 필요한 이벤트를 발생시키거나, 서포트봇을 동작시킵니다.
class WebViewActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
webview.apply {
settings.apply {
// 옵션 설정
}
webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
// 현재 페이지를 SDK가 알 수 있도록 합니다
ChannelIO.setPage(url)
}
}
}
webview.loadUrl(url)
}
}
iOS
이 단락에서는 iOS WebView 환경에서 모바일 웹에 설치된 JavaScript SDK를 사용하는 방법을 서술합니다.
JavaScript만 사용하는 경우, 푸시 알림 기능을 지원하지 않습니다.
푸시 알림 기능을 사용하려면 ChannelIO iOS SDK를 참고하세요.
아래 예시는 임의의 WebView ViewController에 url이 YOUR_WEB_URL
인 페이지를 로드하는 예제입니다.
import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
var webView: WKWebView!
var url: String = "YOUR_WEB_URL"
override func loadView() {
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true // # 1. modify to true inlineMediaPlay option
webView = WKWebView(frame: .zero, configuration: config)
webView.uiDelegate = self
view = webView }
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string:self.url)
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
// #2. target blank handleing
func webView(
_ webView: WKWebView,
createWebViewWith configuration: WKWebViewConfiguration,
for navigationAction: WKNavigationAction,
windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.targetFrame == nil {
webView.load(navigationAction.request)
}
return nil
}
}
필요한 추가 처리
위에 기술한 코드 예시에 이미 각각 #1, #2 표시가 되어 있습니다.
# 1. allowsInlineMediaPlayback
iOS WKWebView의 allowsInlineMediaPlayback은 HTML5 video를 인라인으로 재생할 것인지, 전체화면으로 재생할 것인지에 대한 설정값입니다. default는 false
입니다. 따라서 SDK 내부에 비디오가 있는 경우에 자동으로 전체화면으로 재생합니다.
이를 수정하기 위해 allowsInlineMediaPlayback
을 true 로 설정합니다.
# 2. target blank handling
채널톡 JavaScript SDK 링크의 속성은 모두 target="_blank"
으로, 해당 링크를 새탭으로 열게 됩니다. 그러나, iOS의 WKWebView는 새 탭에 대한 처리가 되어있지 않아 이에 대한 처리가 필요합니다.
해당 처리는, target="_blank"
에 대한 대한 액션인 경우 URL을 받아와, WebView에 URL을 로드하는 방식으로 해결합니다.