Android Webview Image Anchor

The problem: calling webview.hitTestResult on an anchor tag that has an image as a child element returns a hitTestResult with the type of SRC_IMAGE_ANCHOR_TYPE and the hitTestResult.extra is the src of the image and not the url of the anchor tag.

In order to get the url of the anchor tag it is necessary to call an additional method on the WebView. The function to call is requestFocusNodeHref. This function takes a Message as its only parameter.

The following stackoverflow answer suggest how this can be used in order to achieve the desired result - How to get link-URL in Android WebView with HitTestResult for a linked image (and not the image-URL) with Longclick

In the answer the following code snip-it is provided (converted to Kotlin, and tweaked slightly):

mWebview.setOnLongClickListener {
        val result = mWebview.hitTestResult
        if (result.type == HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
            val msg = mHandler.obtainMessage()
            mWebview.requestFocusNodeHref(msg)
        }
        false
}

and the Handler to get the URL:

val mHandler = object : Handler() {
    override fun handleMessage(msg: Message) {
        // Get link-URL.
        val url = msg.data.getString("url")

        // Do something with it.
        if (url != null) { ... }
    }
}

However, the problem with this approach is that in order to ensure no leaks the Handler needs to be declared as a static and any references back to the outer class need to use WeakReference.

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected….

See ADT 20 Preview 3 for info on when this Lint rule was added.

There is an alternative way to use the Handler and Message to mitigate this.

webView.setOnLongClickListener {
    val result = webView.hitTestResult
    if (result.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
        val handler = Handler()
        val message = handler.obtainMessage()

        webView.requestFocusNodeHref(message)
        val url = message.data.getString("url")

        // Do something with url, return true as touch has been handled
        true
    } else {
        false
    }
}

If you also want to get the src of the image this is available in the message bundle as src message.data.getString("src"). Additionally, title and alt are also available.

This flow was taken from the WebKitHitTestTest test which has the following test case to cover getting the anchor tag from a SRC_IMAGE_ANCHOR_TYPE hit target - srcImgeAnchorTypeTestBody

private void srcImgeAnchorTypeTestBody(boolean byTouch) throws Throwable {
        String fullImageSrc = "http://foo.bar/nonexistent.jpg";
        String page = CommonResources.makeHtmlPageFrom("", "<a class=\"full_view\" href=\""
                + HREF + "\"onclick=\"return false;\"><img class=\"full_view\" src=\""
                + fullImageSrc + "\"></a>");
        setServerResponseAndLoad(page);
        simulateInput(byTouch);
        pollForHitTestDataOnUiThread(HitTestResult.SRC_IMAGE_ANCHOR_TYPE, fullImageSrc);
        pollForHrefAndImageSrcOnUiThread(HREF, null, fullImageSrc);
}