Installation for web view

Android (Only javascript)

To use channel talk plugin, some settings required
This solution cannot use push notification.
If you want to use push notification, use following solution Android with SDK

Allow new tab

Channel Web SDK try to open new tab when user enter chat.
Android web view do not allow new tab on default. So we need to write a logic to allow new tabs

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([email protected])

        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
    }
}

File upload

If you are using Android's WebView, users will not be able to launch a file chooser when they click the attachment button to send files. The problem is that WebView does not implement default file choosing behavior, so we must manually define how we handle the users' actions. Along with our simple solution and snippets, you will only take a minimal effort on integrating Channel Web SDK with your mobile application. We highly recommend using our Android SDK for those of you looking for effortless and straightforward experiences on mobile devices.

First, allow WebView to access files and set WebChromeClient to get notified when the user wants to open the file chooser.

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 --
        }
}

Second, copy our code of WebViewAttachmentModule class that is responsible for handling file chooser callbacks. Note that the snippet is provided to help you write code faster.

🚧

Make sure to return true from onShowFileChooser

The WebChromeClient does not allow filePathCallback to be invoked if onShowFileChooser returns false. As the module always calls the callback when an intent for a file chooser is created, make sure to call getIntentChooser only if onShowFileChooser returns true.

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()
    }
}

Sample code

This is sample code. Do not use as is

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 {
                // set options

                javaScriptEnabled = true
                domStorageEnabled = true
                useWideViewPort = true
                allowFileAccess = true

                setSupportMultipleWindows(true)
                javaScriptCanOpenWindowsAutomatically = true
            }

            webChromeClient = object : WebChromeClient() {
                // Handle file upload
                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,
                    )
                    return true
                }
              
                // Handle new tab
                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([email protected])

                    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
                }
            }
        }

        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)
    }
}

Android with SDK

If you want to use webview only showing content and use channel talk plugin to chat and marketing.
This solution can use push notification.

❗️

About on site channel talk script

If you already use channel plugin javascript sdk,
Hide button and popup of turn off existing javascript sdk.

Installation and push notification is in here
Installation Push Notification

We recommend to use setPage to notify us current url.
We use this information to use event page, support bot targeting

class WebViewActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
 
        ...

        webview.apply {
            settings.apply {
                // set options
            }

            webViewClient = object : WebViewClient() {
                override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
                    // This line is important to notify us current page
                    ChannelIO.setPage(url)
                }
            }
        }

        webview.loadUrl(url)
    }
}

IOS

Please consider this option as the chat window may create an unintended UI in webview.

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate {
  
  var webView: WKWebView!
  var url: String = ""
  override func loadView() {
    let config = WKWebViewConfiguration()
    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)
  }
  
  // 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
  }
}

If you use webview with native modal style(like present(webview)), you can experience crash when click clip button in chat room.
Then you need to add this code.
Also this link can help you.

First, Add this code on Webview controller

import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate, UIPopoverPresentationControllerDelegate, UIGestureRecognizerDelegate {
  
  var webView: WKWebView!
  var url: String = ""
  var lastTapPosition: CGPoint = CGPoint(x: 0, y: 0)
  
  override func loadView() {
    let config = WKWebViewConfiguration()
    webView = WKWebView(frame: .zero, configuration: config)
    webView.uiDelegate = self
    view = webView
    
    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(webViewTapped(_:)))
    tapGesture.delegate = self
    webView.addGestureRecognizer(tapGesture)
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    let myURL = URL(string:self.url)
    let myRequest = URLRequest(url: myURL!)
    webView.load(myRequest)
  }
  
  // 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
  }
  
  // gesture handling
  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
  }
  
  @objc
  func webViewTapped(_ sender: UITapGestureRecognizer) {
    self.lastTapPosition = sender.location(in: self.view)
  }
  
  override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
    setUIDocumentMenuViewControllerSoureViewsIfNeeded(viewControllerToPresent)
    super.present(viewControllerToPresent, animated: flag, completion: completion)
  }

  func setUIDocumentMenuViewControllerSoureViewsIfNeeded(_ viewControllerToPresent: UIViewController) {
    if #available(iOS 13, *), viewControllerToPresent is UIDocumentMenuViewController && UIDevice.current.userInterfaceIdiom == .phone {
      // Prevent the app from crashing if the WKWebView decides to present a UIDocumentMenuViewController while it self is presented modally.
      viewControllerToPresent.popoverPresentationController?.sourceView = webView
      viewControllerToPresent.popoverPresentationController?.sourceRect = CGRect(origin: self.lastTapPosition, size: CGSize(width: 0, height: 0))
    }
  }
}

Second, Add this code on present called Controller

....

@IBAction func openWebViewController() {
    //get text from text field
    let viewController = ViewController()
    viewController.url = self.textField.text ?? ""
    self.present(viewController, animated: true)
//    self.navigationController?.pushViewController(viewController, animated: true)
  }

....


override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
    if let webVC = viewControllerToPresent as? ViewController {
      webVC.setUIDocumentMenuViewControllerSoureViewsIfNeeded(viewControllerToPresent)
    }
    super.present(viewControllerToPresent, animated: flag, completion: completion)
  }