In general, embedding a WebView inside native Android app is not so difficult. However, if you want to intercept interactions inside the WebView, it can be very tricky.
Did you ever try intercepting download for like a pdf file? It's quite common that creating object URL with Blob data and assign it to <a> tag, and performing click.
When it's triggered in Android's WebView, nothing happens. We have to hook the download and open or save the file in Android's code.
Download a file in the public "Download" folder
In Android, when we instantiate a WebView, we need to pass JavaScript Interface so that the Android app can communicate with WebView inside JS files. I will show the implementation of the interface later. The setDownloadListener() allows us to intercept the download inside the WebView. In the above example, when the link.click() is performed, the listener is invoked.
And, this is how the JavaScript Interface looks (detail is following).
When the link is clicked, it injects JS doing:
Fetch Blob data from the given URL
Convert the Blob to data URL using FileReader
Extract base64 from the data URL
Pass the base64 data to the JavaScript Interface calling receiveBase64()
FileReader can also get ArrayBuffer from Blob but unfortunately, JavaScript's ArrayBuffer and Kotlin's ByteArray are not compatible. Also, as noted in the official doc, to get base64 string, we remove the metadata:
Note: The blob's result cannot be directly decoded as Base64 without first removing the Data-URL declaration preceding the Base64-encoded data. To retrieve only the Base64 encoded string, first remove data:/;base64, from the result.
Please note that to write data in the public directory, you have to grant permission in manifest.
[Optional] Open the file after download
To open a file inside public directory, we need several steps. At first, create res/xml/file_paths.xml containing paths you want to read or write. In this case, I add external-path because that's where public Download folder is located.
Next, add <provider> node inside <application>. FileProvider converts file to content that we can show to the user on the app.
At last, update JavaScript Interface to have context and open the content through Intent.