B4A Library WebViewAssetLoader - the demo

here is a demonstration of the WebViewAssetLoader class API.

questions about webview's failing to load local resources under sdk30 are starting to pop up occasionally. they are, of course, beaten down by a chorus of "don't use webview!" i'm not here to judge.

what it boils down to is google wants you to use the https:// scheme to load local resources into webviews. this is a problem if you don't run a secure server on the same device as the app.

to allow you to use the https:// scheme, google has created a dummy secure url. with the webviewassetloader api, you keep your local files in dirassets or dirinternal, like always, but load them using the dummy url instead of the file:// scheme, which is what google is turning off. (yes, i know it's possible to turn it on - for the moment - and i am also aware of a number of other considerations and questions, but for the purposes of the demo, i'm haven't addressed them here. i previously referred to some workarounds in this post. i have no doubt they will be phased out once everyone is onboard with the assetloader.)

to run the demo, you need to:
1) unzip the attached archive.
2) copy/move the .jar and .xml files to your additional libraries folder.
3) open your sdk manager and look for "webkit". you're looking for "androidx.webkit:webkit (Google Maven) Version 1.4.0". download it, if applicable. this is the assetloader api.
4) build and run the demo. (b4a11, sdk30, right?)

if you are among those encountering the net::ERR_ACCESS_DENIED problem as a result of trying to load a webview with file://, the demo should work for you. if you look closely at my test file, you will see how you can even load local resources from within the test file. (and if you keep sub-folders in dirinternal, you can load from them as well. i didn't do that for the demo.)

if the demo doesn't run correctly, and you've run it on a recent device targeting sdk30, i'll try to help. i've tested it on 2 devices running android 11, targeting sdk30 and built with b4a 11.

the api overrides aspects of the webviewwrapper in our b4a core library. and it overrides some aspects of android's own webview, so i would assume that will change as google catches up with what may be unintended behavior and people start complaining loud enough. for the moment, google seems intent on shutting down vulnerabilities first.
 

Attachments

  • webviewload.zip
    48.3 KB · Views: 478

Javier Alonso

Member
Licensed User
Longtime User
Great, drgottjr! This is precisely what I have been looking for.

I have a couple of questions. First, I host the page in File.DirInternal, not the assets folder. How do I access that folder? (maybe https://appassets.androidplatform.net/files/atest.html?)
Second, I am using WebViewExtras to inject and execute some javascript in the pages. Will it work after adding the gassetsloader library?

Thanks in advance!
 

drgottjr

Expert
Licensed User
Longtime User
sorry, i realized i should have included the dummy url for file.dirinternal after i uploaded the library.
here's the url:
B4X:
        ' if your files are in dirinternal, use this:
        ' webview.LoadUrl("https://appassets.androidplatform.net/public_data/atest.html")

        ' if your files are in 1 or more sub-folders in dirinternal, just add the sub-folder to the base url.
        ' eg, if your images are kept in dirinternal/images, load them like this:
        ' webview.LoadUrl("https://appassets.androidplatform.net/public_data/images/animage.jpg")

it's already supported in the library, so no change there. why google chose "public_data" as the name for a folder which clearly isn't public, i can't say. they're all billionaires, so they must be doing something right.

and, for whatever reason, the last time i looked, there was no dummy secure url for what we call "safe" direxternal. you would think after doing everything it could to make us use such a location, google would have included it in the assetloader api. i'll check again.

if you had nothing better to do, you might try sticking a file in safedirexternal and see if the assetloader finds it when you use the dummy secure url for file.dirinternal. i'll probably try myself; you're the first one who's shown any interest.

as i think i mentioned early on, there are some issues with this api. they may never affect your app. feel report to report back any bizarre behavior. my hope was that google will ultimately see them and update the api. the loader part does what it says it does. but the api overrides some of webview's and its affiliates' behavior. no sense in getting into it all now.

as for javascript injection, i added webviewextras to the b4a example and set the javascript interface. in the pagefinished sub, i added a simple alert in the example:

B4X:
' in create:
dim webviewextras as webviewextras
webviewextras.addWebChromeClient(webview, "cc")
webviewextras.addJavascriptInterface(webview, "B4A")

webviewextras.addWebChromeClient     <--- NO NO NO NO NO NO NO  THIS CONFLICTS WITH THE ASSET LOADER (and vice versa)

later:
Sub gal_PageFinished (Url As String)
    Log("finished loading: " & Url)
    dim javascript as string = "javascript:alert('hello from the webviewassetloader');"
    webviewextras.executejavascript( webview, javascript )
End Sub

it worked fine, so javascript injection seems to be ok. NOTE: DO NOT ADD webviewclient IN WEBVIEWEXTRAS. webviewextras and the webviewassetloader will nullify each other in this respect. i added webviewextras chomeclient, apparently without incident.
 
Last edited:

Javier Alonso

Member
Licensed User
Longtime User
Thank you, drgottjr! I managed to set it up following your instructions. I am using you library together with informatix's Webreader (actually a webview with a handful of interesting properties to read epubs) and there is only one thing missing: I should need to expose the webviewclient->shouldOverrideUrlLoading event in the gal library, because putting both libraries together, the event in the Webreader library is not working.

Would it be possible that you include this event in your library? I need to catch the event of the user clicking on a link inside the ebook...

Thanks in advance. I promise to start learning pure java asap ;-)
 

drgottjr

Expert
Licensed User
Longtime User
i'm not familiar with webreader. looks like there is a conflict between it and the assetloader library. it depends on what webreader does. if it sets its own webviewclient, it will kill mine. and vice versa (it depends on which library you add last. that's the library which prevails.)

**************************THIS SUPERCEDES THE REPLY THAT USED TO BE HERE. **************************
shouldoverrideurlloading() works. it defaults to false, which means the requested page loads. if you have the overrideurl sub set up in your b4a project, you can control whether or not the requested page loads. i went through version 0.02 again and did a number of tests relating to shouldoverride. in any case,
i've attached an update.

if you had the misfortune to read my earlier post (and which i am now basically overwriting), i apologize.

there is still the main issue of conflict between webview-related libraries. a webview can only have 1 webviewclient. if you load a couple webview-related libraries which happen to set their own webviewclient, the last library to load is the one that gets the webviewclient. the other libraries are effectively nullified, at least as far as webviewclient methods are concerned. if some feature you used no longer works (and you want to bring it to my attention),
i need to see code you used to add any webview-releated libraries to the app.

another edit:
i took a longer look at webreader. it extends our standard webview (wrapper), which has its own webviewclient (for methods like overruleurl), so the situation is as i've explained: only 1 webviewclient per webview. the others don't support the assetloader api; mine does. whichever library you load last gets the webviewclient.
 

Attachments

  • gassetloader.zip
    6.6 KB · Views: 328
Last edited:

tagwato

Member
Licensed User
Longtime User
Wonderful! Thank you for this Demo!
I'm working to migrate/elevate the targetSdkVersion of my app to 31 (maybe33), because Google set as deadline August/2023.
My app uses a bunch of html files as a "Help" mechanism, and shows them to the users via a WebView component.
To load thoses files, the webview.LoadUrl("file:///....") mechanism was until now used, but it stopped working when I tried to compile the app with SDK >= 30.
The reason is because there are subdirectories, one for each language the app is ported to.
It seems that webview.LoadUrl("file:///....") does not work well anymore, if the URL points to subdirectories inside the assets folder.
So, to shorten the story, I am very glad I found this "Demo", because it seems to suit my needs very well.
I was able to load my html pages like this:
B4X:
theWebview.LoadUrl("https://appassets.androidplatform.net/assets/subfolder%5Cen%5Chelp.html")
Just for curiousity, I saw that the file separator at the subdirectory level has to be encoded as "%5C", which is the backslash symbol "\", i.e. the file separator on Windows Systems...
... but...
our apps are going to run inside Android systems, which are a kind of Linux, where the file separator is "/", encoded for URL as "%2F"...
...and...
if we encode the file separators of the URL at the subdirectory level with "/" or with "%2F" it doesn't work.
Just curious: why? :)


PS - the "%5C" encoding must also be used inside the HTML files, when referencing images and resources located inside subdirs, like this:
HTML:
<img alt="The Main Screen" src="subfolder%5Cen%5Cmainscreen.png" />
 
Last edited:

drgottjr

Expert
Licensed User
Longtime User
this was a project that i put together when webviewassetloader was new.
(so long ago; i should actually be dead by now).

i suppose it still works, but you might want to search the forum for more
recents projects. they are all done by forum member ivica golubovic and
may be more useful. they are certainly more recent. i believe he has a very
new webview-related library. (look, here's one:
https://www.b4x.com/android/forum/threads/ultimatewebviewassetloader.144244/)

as to the encoded "\" folder separator symbol, you are certainly welcome to
try the library without it. there is a reason why it's used, but this was so long
ago that i no longer remember what it was.

i think it has something to do with b4a's ide functioning on a windows system,
and the way the assets folder is constructed (on a windows computer). in any
case it can't hurt to use the normal ("/") separator. it will either work or it won't.
then you'll know.

if the library suits your needs, enjoy. i, too, use webview for my help screens.
 

tagwato

Member
Licensed User
Longtime User
(...)
i suppose it still works, but you might want to search the forum for more
recents projects. they are all done by forum member ivica golubovic and
may be more useful. they are certainly more recent. i believe he has a very
new webview-related library. (look, here's one:
https://www.b4x.com/android/forum/threads/ultimatewebviewassetloader.144244/)
(...)
Tanks, @drgottjr.
Just tried the UltimateWebViewAssetLoader library (and its successor WebkitWebViewAssetLoader), developed by @Ivica Golubovic .
They also worked !
But I saw that the name of the Assets directory used is "/asset/", not "/assets/", which is the default name adopted in the example provided by Google/Android developers (https://developer.android.com/reference/androidx/webkit/WebViewAssetLoader), and also by many other developers.
It's not a problem, because that name is kind of a "virtual" name for the subfolder internally used by the WebView object.
But, developers must be careful - when using these 2 libraries.
Load the HTML file this way:
B4X:
theWebview.LoadUrl("https://appassets.androidplatform.net/asset/<<some-html-file-name>>")
not this way:
B4X:
theWebview.LoadUrl("https://appassets.androidplatform.net/assets/<<some-html-file-name>>")
Or load the HTML file like this (better way):
B4X:
theWebview.LoadUrl(AssetLoader.AssetPahtURL & <<some-html-file-name>>)
 

Ivica Golubovic

Active Member
Licensed User
Tanks, @drgottjr.
Just tried the UltimateWebViewAssetLoader library (and its successor WebkitWebViewAssetLoader), developed by @Ivica Golubovic .
They also worked !
But I saw that the name of the Assets directory used is "/asset/", not "/assets/", which is the default name adopted in the example provided by Google/Android developers (https://developer.android.com/reference/androidx/webkit/WebViewAssetLoader), and also by many other developers.
It's not a problem, because that name is kind of a "virtual" name for the subfolder internally used by the WebView object.
But, developers must be careful - when using these 2 libraries.
Load the HTML file this way:
B4X:
theWebview.LoadUrl("https://appassets.androidplatform.net/asset/<<some-html-file-name>>")
not this way:
B4X:
theWebview.LoadUrl("https://appassets.androidplatform.net/assets/<<some-html-file-name>>")
Or load the HTML file like this (better way):
B4X:
theWebview.LoadUrl(AssetLoader.AssetPahtURL & <<some-html-file-name>>)
The best way to use some B4X library is to follow the instructions for that library, not from the java library site. Yes, WebViewAssetLoader_demo and WebkitWebViewAssetLoader use "asset" instead of the official "assets". This is to allow people to switch from one library to another without making too many changes that can cause additional problems in some cases. WebViewAssetLoader_demo is the first library, and I followed the guidelines from the older library in WebkitWebViewAssetLoader to avoid additional inconveniences when switching from one library to another. Basically, the URL address is virtual and can be defined by the programmer and is only used so that the compiler knows which part of the storage is being referred to. The best way to use WebkitWebViewAssetLoader is your third example. Cheers.
 
Top