B4A Library WebViewExtras

Hi all.

WebViewExtras is my latest library.
It's a much updated version of JSInterface.

WebViewExtras exposes more of the available native Android WebView methods to your B4A application:

addJavascriptInterface(webView1 As WebView, interfaceName As String)

Add a javascript interface to webView1, methods of the interface can be accessed using javascript with the interfaceName as the javascript namespace.

The interface contains just a single overloaded method CallSub().
The CallSub method signatures are:

CallSub(subName As String, callUIThread As boolean)
CallSub(subName As String, callUIThread As boolean, parameter1 As String)
CallSub(subName As String, callUIThread As boolean, parameter1 As String, parameter2 As String)
CallSub(subName As String, callUIThread As boolean, parameter1 As String, parameter2 As String, parameter3 As String)


So if you have added the interface to your webView with the interfaceName of "B4A" then you can write javascript such as:

B4X:
B4A.CallSub('MySubName', true)

The callUIThread parameter is an important update - it's not available with JSInterface.

Does the Sub called by your javascript modify your activity UI?
If the answer is yes then you need to pass boolean true as callUIThread otherwise you can pass false.
If you pass false and then the Sub tries to modify your activity UI you will get an exception.

Does your javascript require a return value from your Sub?
If the answer is yes then the Sub MUST NOT modify the activity UI.
If CallSub is excuted with callUIThread set to true then no values will be returned from your Sub to the javascript.

You will need to structure your B4A code so that Subs that return values to javascript do not modify the activity UI.

addWebChromeClient(webView1 As WebView, EventName As String)

Add a WebChromeClient to webView1.

The default B4A WebView has no WebChromeClient.
A WebChromeClient handles many things, the WebChromeClient that WebViewExtras adds to your WebView enables:

Version 1.30 of WebViewExtras requires that an additional EventName parameter is passed to the addWebChromeClient method, see this post: http://www.b4x.com/forum/additional-libraries-official-updates/12453-webviewextras-2.html#post102448

clearCache(webView1 As WebView, includeDiskFiles As boolean)

Clear the WebView cache.
Note that the cache is per-application, so this will clear the cache for all WebViews used in an application.

boolean includeDiskFiles - If false, only the RAM cache is cleared.

executeJavascript(webView1 As WebView, javascriptStatement As String)

Executes a string of one or more javascript statements in webView1.
javascriptStatement - A string of one or more (semi-colon seperated) javascript statements.

flingScroll(webView1 As WebView, vx As Int, vy As Int)

flingScroll is a poorly documented method of the WebView.
It's included in WebViewExtras as it may be useful but i can find no documentation for it or it's parameters.

vx and vy do not seem to be pixel values - i suspect they are velocity values for the kinetic/fling scroll.

pageDown(webView1 As WebView, scrollToBottom As boolean)

Scroll the contents of webView1 down by half the page size.

scrollToBottom - If true then webView1 will be scrolled to the bottom of the page.

Returns a Boolean value to indicate the success or failure of the scroll.

pageUp(webView1 As WebView, scrollToTop As boolean)

Scroll the contents of webView1 up by half the page size.

scrollToTop - If true then webView1 will be scrolled to the top of the page.

Returns a Boolean value to indicate the success or failure of the scroll.

zoomIn(webView1 As WebView)

Perform zoom in on webView1.

Returns a Boolean value to indicate the success or failure of the zoom.

zoomOut(webView1 As WebView)

Perform zoom out on webView1.

Returns a Boolean value to indicate the success or failure of the zoom.

Up to date documentation/reference for this library can be found here: http://www.b4x.com/forum/additional-libraries-official-updates/12453-webviewextras-3.html#post106486.

Library and demo code is attached to this post.

The demo is a bit brief - sorry but i don't have time to write demo code for all the new methods.
The demo displays two WebViews - the top WebView has a JavascriptInterface and WebChromeClient whereas the lower WebView has neither - it is the default B4A WebView.

Martin.

Edit by Erel:
- There is a security issue related to AddJavascriptInterface in older versions of Android. See this link: https://www.b4x.com/android/forum/t...ascriptinterface-vulnerability.85032/#content
- v2.20 is attached. This is the latest version.
 

Attachments

  • WebViewExtras_v1_42.zip
    7.8 KB · Views: 9,532
  • v2.20.zip
    41.1 KB · Views: 854
Last edited by a moderator:

andrewj

Active Member
Licensed User
Longtime User
Hi Martin,
Thanks, that looks hopeful. I should be able to insert this into my existing long-click handling. I'll have a play later and report back.
Andrew
 

andrewj

Active Member
Licensed User
Longtime User
Dear Martin,

This is absolutely brilliant! With a couple of lines of code I've solved both the image download problem and the fact that I had a conflict between my normal long press processing and the text selection capability. So that others might benefit, here's the core code of my LongPress handler:

B4X:
    Dim wbv As FlingableWebView
    wbv = wbvWeb(tabTabs.CurrentTab)
    'Log("Long Press on Web View")
    Dim oHit As HitTestResult
    oHit = wbv.GetHitTestResult
    If oHit.GetType = wbv.WebKitConstants.HitTestResultType.IMAGE_TYPE Then
        msDeferURL = oHit.GetExtra
        msURLDesc = msDeferURL.SubString(msDeferURL.LastIndexOf("/") + 1)
        lstMenu.Initialize("lstMenu")
        lstMenu.Color = Colors.DarkGray
        lstMenu.AddSingleLine("Download Image")
        Activity.AddView(lstMenu, mlX, mlY, 200dip, 50dip)
        If lstMenu.Left > (100%x - 200dip) Then lstMenu.Left = 100%x - 200dip
        If lstMenu.Top > 100%y - 50dip Then lstMenu.Top = 100%y - 50dip
        lstMenu.Visible = True
    Else If oHit.GetType = wbv.WebKitConstants.HitTestResultType.UNKNOWN_TYPE Then
        Log("Text")                    ' Do nothing, enables standard select/copy capability
    Else                            ' Normal long-click processing via timer
        mbLongPress = True
        mlX = X
        mlY = Y
        mView = wbv
          timDelay.Enabled = True        ' Will send a simulated click back to the same location after 300ms
    End If

If the hit test returns IMAGE_TYPE then GetExtra returns its URL. I save these in two variables I was already using for download management and pop up a menu allowing the user to confirm the download. If he selects that, then the normal download processing runs for the image.

If the hit test returns UNKNOWN_TYPE then I just ignore it, and the web view goes into text selection mode of its own accord.

Otherwise (which typically means I'm clicking on a link) I send a delayed click back to the same point via a timer, when then triggers a pop-up menu relevant to the link.

Thanks for the help.
Andrew
 

andrewj

Active Member
Licensed User
Longtime User
I've also managed to get rid of the very clumsy handling I had to pop-up the context menu on a link, similar to the image example above. I'm wondering if it might be worth codifying this as part of your library.

Andrew
 

warwound

Expert
Licensed User
Longtime User
I've also managed to get rid of the very clumsy handling I had to pop-up the context menu on a link, similar to the image example above. I'm wondering if it might be worth codifying this as part of your library.

Andrew

Exactly what code are you thinking it'd be good to add to the library?

Martin.
 

andrewj

Active Member
Licensed User
Longtime User
Hi Martin,

I was vaguely thinking of extending the FlingableWebView interface so that you automatically provide the HitTestResult in the appropriate events, but on second thoughts that's probably overkill. However I think it would be a good idea to extend your examples with how to interact with images and drive drop-down menus, as these are common questions. I can share my completed code with you if you like.

Andrew
 

Siam

Active Member
Licensed User
Longtime User
Hello,
i have a problem with Android 4.4.2 since this last update the FileUpload dosn't work :( i have checked the webviewextras2 but here is the same problem

on android 4.1.2 Work's it fine here i see in the log from webviewextras2 :

B4X:
running waiting messages (1)
ResultArrived
** Activity (main) Resume **
openFileChooser Jelly Bean
AcceptType: */*
Capture: filesystem
** Activity (main) Pause, UserClosed = false **
and a filechoser will open.

With the same code on KitKat no filechoser will open and in the log nothing happens.


lg

Andy
 

warwound

Expert
Licensed User
Longtime User
@Siam

Have a look at this page: http://developer.android.com/guide/webapps/migrating.html.

Note: If your targetSdkVersion is set to "18" or lower, WebView operates in "quirks mode" in order to avoid some of the behavior changes described below, as closely as possible—while still providing your app the performance and web standards upgrades. Beware, though, that single and narrow column layouts and default zoom levels are not supported at all on Android 4.4, and there may be other behavioral differences that have not been identified, so be sure to test your app on Android 4.4 or higher even if you keep your targetSdkVersion set to "18" or lower.

Open your project and then open the Manifest Editor, what is the project's targetSdk set to?
If it's set to 19 then try changing it to 18 or lower and see if that fixes the file download problem.

If that doesn't help then it seems as though you're out of luck and will have to await a fix from Google...

Martin.
 

andrewj

Active Member
Licensed User
Longtime User
Hi Martin,

Do you have any idea how to set the browser so that the viewport is the same width as the screen. I have a problem that in the default mode pages which are properly "responsive" (like my own web site) fit neatly to the page, but many others don't, and can't be scrolled in to fit. If I add wbvWeb(i).
.SetDefaultZoom("FAR") then the non-responsive pages fit properly, but others (like my web site) think they are trying to display on a large desktop and don't display correctly. I've tried setting wbvWeb(i).GetSettings.SetUseWideViewPort both ways but without success.
Any ideas?
Thanks
Andrew
 

warwound

Expert
Licensed User
Longtime User
Hi again.

Have a read here: http://developer.android.com/refere...tZoom(android.webkit.WebSettings.ZoomDensity):
This setting is not recommended for use in new applications. If the WebView is utilized to display mobile-oriented pages, the desired effect can be achieved by adjusting 'width' and 'initial-scale' attributes of page's 'meta viewport' tag. For pages lacking the tag, setInitialScale(int) and setUseWideViewPort(boolean) can be used.
There's no simple solution for such a problem.
If you have control over the webpage them setting an html meta viewport attribute in the webpage source is the ultimate solution.
Setting such an attribute will work with all modern browsers - it's compatible with iOS as well as Android.
But when you have no control over the webpage being viewed then it seems as though trial and error with the various different settings and testing on different versions of android are all you can do.

Note that SetInitialScale is a WebView method and SetUseWideViewPort is a WebSettings method.

Martin.
 

andrewj

Active Member
Licensed User
Longtime User
Hi,
Further thought: if there's a simple Javascript query I can run to get the page/viewport width in units I can compare with the webview width then I can manage this in the page complete event. Is that possible?

Thanks
Andrew
 

warwound

Expert
Licensed User
Longtime User
I'm not sure exactly what document properties you'd need to retrieve using javascript.
There's an interesting page here (well worth a read): http://developer.android.com/guide/webapps/targeting.html.

I was thinking the best solution when a webpage has no viewport meta tag would be to use javascript to set a viewport meta tag.
https://www.google.co.uk/search?q=javascript+set+viewport+width&ie=UTF-8&oe=UTF-8.

Web development is a nightmare of conflicting standards - you might find an article that seems to contain a solution but find the solution doesn't work in an android WebView.
Worse still you might find that the 'all new Chromium based WebView' in KitKat means there simply is no solution.
http://developer.android.com/guide/webapps/migrating.html
Lots of complaints on the web about the lack of support for "single and narrow column layouts and default zoom levels" with the response from Google seeming to be 'tough - we've changed the WebView and we're not going to make the new WebView backwards compatible'!

Martin.
 

andrewj

Active Member
Licensed User
Longtime User
Hi Martin,

Solution is actually very simple. In the PageFinished event I just set a simple Javascript request for document.width, and then use this handler code:
B4X:
    If IsNumber(PageWidth) Then
        Dim iPageWidth As Int = pagewidth
        If iPageWidth > wbv.Width Then
            wbv.GetSettings.SetDefaultZoom("FAR")
        Else
            wbv.GetSettings.SetDefaultZoom("MEDIUM")
        End If
    End If
I need to test with a few more web pages, but so far so good.

Thanks for the help
Andrew
 

andrewj

Active Member
Licensed User
Longtime User
Hi Martin,
Have you tested the FavIcon handling in your library? I'm loading a site which definitely has a favicon set, but the event wbvWeb_ReceivedIcon(wbv As FlingableWebView, Icon As Bitmap) never fires, and if I try to retrieve the icon directly with GetFavIcon() the result is un-initialised.

Any ideas?
Thanks
Andrew
 

warwound

Expert
Licensed User
Longtime User
Hi Martin,
Have you tested the FavIcon handling in your library? I'm loading a site which definitely has a favicon set, but the event wbvWeb_ReceivedIcon(wbv As FlingableWebView, Icon As Bitmap) never fires, and if I try to retrieve the icon directly with GetFavIcon() the result is un-initialised.

Any ideas?
Thanks
Andrew

No this was never tested - i'm busy this afternoon but will put a test project together in the morning and see what i can establish.

Martin.
 

warwound

Expert
Licensed User
Longtime User
@andrewj

Tucked away in the WebKit API is a class WebIconDatabase and this class must be used to enable favicon functionality.

I've updated the AJWebKit FlingableWebView so that it opens the WebIconDatabase and now all favicons are stored in File.DirInternal in a folder named 'icons'.
Tested on Gingerbread and KitKat and the 'ReceivedIcon' event is now raised and an initialized Bitmap passed.

(I tested using the URL www.favicon.co.uk).

Martin.
 

Attachments

  • AJWebKit-20140402.zip
    53 KB · Views: 461

andrewj

Active Member
Licensed User
Longtime User
Hi Martin,
Perfect (although it seems to work more reliably with the event rather than GetFavicon for some reason).
Thanks very much
Andrew
 

DonManfred

Expert
Licensed User
Longtime User
Hallo

i´m using webview to show a html-code i get from this url


It works. But when i want to use webviewextras it shows only a blank page.

the webview is loaded to a panel with panel.loadlayout() and is part of that layout... name: www

after loading that layout i wanted to use

B4X:
wvxtra.addJavascriptInterface(www, "B4A")

but it seems not to work.

Any hints what´s wrong here?

That´s the html-code which will be loaded with above LoadURL-command

<!DOCTYPE html>
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>ImageLoader</title>
<script type='text/javascript' src='jquery.min.js'></script>
<script type="text/javascript" src="jquery-ui.js"></script>
<script type='text/javascript'>
//<![CDATA[
$(window).load(function(){
$(window).resize(
function(){
//do something when window resizes like:
var $winH = $(this).height();
var $winW = $(this).width();

$('#img').width($winW); //set width = width of window - 100 px
$('#img').height($winH); //set height = height of window - 100 px
$('#image img').width(($winW - 0)/3);
$('#image img').height('auto');
}
);
$(window).resize();
});//]]>
</script>
</head>
<body>
<center>
<div id='image'>
<a href=''>
<img src='http://www.prcamels.ae/images/news/npic09191114.jpg' id='image1'>
</a>
</div>
</center>
</body>
</html>
 

warwound

Expert
Licensed User
Longtime User
Try adding:

B4X:
wvxtra.addWebChromeClient(www, "")

Now compile and run your project again and look at the log.
Do you see any errors reported?
(The WebChromeClient redirect WebView console error messages to the android log).

Otherwise if you can upload a sample project that shows what you're trying to do i'll try and debug it.

Martin.
 
Top