How I Almost Put an iframe into a WebView

Listening to JavaScript events on 3rd party webpages

Snap Click by Foundry is licenced under Creative Commons CC0

Here’s a situation. You need to load a third party URL into a view, and listen to events on the page. That shouldn’t be too hard, right? Android has WebViews we can use, and a way to interface with JavaScript events. We’ll assume we are already in an Activity with a WebView instantiated. Let’s start building it.

private fun loadWebView(webView: WebView, url: String) = with(webView) {
  loadUrl(url)
}

We already know we’ll need JavaScript enabled so we have something to listen to.

private fun loadWebView(webView: WebView, url: String) = with(webView) {
  settings.javaScriptEnabled = true
    
  loadUrl(url)
}

That was easy. Next, we need to listen to the JavaScript events on the WebView. We will include a method to our WebAppInterface that we can call from the JavaScript on the page. Here’s how the Android docs guide us to do that.

public class WebAppInterface {
  @JavascriptInterface
  fun handleMyEvent(jsonString: String) {
    val data: JSONObject = JSONObject(jsonString)
    // Do stuff with event data
  }
}
private fun loadWebView(webView: WebView, url: String) = with(webView) {
  settings.javaScriptEnabled = true
  addJavascriptInterface(WebAppInterface(), "MyJSInterface")
  
  loadUrl(url)
}

Then, on the webpage we’re loading, we add this script:

<script type="text/javascript">
  function HandleMyEvents(event) {
    MyJSInterface.handleMyEvent(JSON.stringify(event.data));
  }

  window.addEventListener("message", HandleMyEvents, false);
</script>

Not too difficult. But here’s where the issue comes in. In this example we don’t have access to the JavaScript on the webpage to make it call the Android web interface. How could we add our own JavaScript to listen to the events? This is how I almost used an iframe. By loading HTML into the WebView with an iframe, and some custom JavaScript, I could hook into those events and forward it to the interface.

private fun loadWebView(webView: WebView, url: String) = with(webView) {
  addJavascriptInterface(WebAppInterface(), "MyJSInterface")

  loadData(htmlString(url), "text/html", "utf-8")
}

htmlString() would look like:

private fun htmlString(url: String): String {
  return """
    <iframe src='$url'></iframe>
    $listenerScript
  """
}

And the listener script as described above:

private val listenerScript: String
  get() = """
    <script type='text/javascript'>
      function HandleMyEvents(event) { MyJSInterface.handleMyEvent(JSON.stringify(event.data)); }
      window.addEventListener('message', HandleMyEvents, false);
    </script>
  """
	

But do we really want to use an iframe? No, not usually.

Thankfully, I came across a better solution (of course I stumbled on this when I was looking for something else). So intead of using an iframe we can inject the JavaScript right into the WebView! Before we load in the URL we first set the WebChromeClient.

private fun loadWebView(webView: WebView, url: String) = with(webView) {
  settings.javaScriptEnabled = true
  addJavascriptInterface(WebAppInterface(), "MyJSInterface")
  
  setWebChromeClient(WebChromeClient())
  
  loadUrl(url)
}

Then, we also need to set the WebViewClient. We will override the onPageFinished() method of the WebViewClient to load our JavaScript. The JavaScript is the same as above.

private fun loadWebView(webView: WebView, url: String) = with(webView) {
  settings.javaScriptEnabled = true
  addJavascriptInterface(WebAppInterface(), "MyJSInterface")
  
  setWebChromeClient(WebChromeClient())
  setWebViewClient(object : WebViewClient() {
    override fun onPageFinished(view: WebView, url: String) {
      view.loadUrl(listenerScript)
    }
  })
  
  loadUrl(url)
}

And there we go! We can now listen to those events in our Android code. I don’t often use WebViews, and it would not have been fun to combine it with an iframe, so I’m glad I was able to find that solution.

Photo of Victoria Gonda

Growing up in Illinois, Victoria moved to Michigan after graduating from Hope College where she studied Computer Science and Dance Production. She is passionate about using technology to improve the lives of others, and enjoys the challenges she encounters while working on mobile and web applications.

She is also interested in dance technology, especially as it applies to the lighting and projection experience in dance performances. Away from technology, she enjoys her small library of books, and spending time in the theater.

Comments:


Post a Comment

(optional)
(optional — will be included as a link.)