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,518
  • v2.20.zip
    41.1 KB · Views: 840
Last edited by a moderator:

LBGroup

New Member
Licensed User
Longtime User
Hello, I'm in the process of porting over a web app that we use internally at our company from using the SureFox platform (basically locks our tablets in a browser mode locked on one web page) to writing a custom app that just wraps our internal web site in a WebView. So far all was going well until the file uploads wouldn't work. I found this library, installed it, and that brought back file uploads. The problem is that now it only allows files to be uploaded from Gallery or the Photos app. There isn't an option for Camera. In SureFox (or just the regular browser) Camera is an option. What do you advise?

Hardware: Nexus 7 2014 with 4.3 Jellybean.
 

DonManfred

Expert
Licensed User
Longtime User
Hello Does anybody have an idea to do some file upload on kitkat ?
I´m not sure if this issue is related to webviewextras.
Maybe you should create a new thread for it. Maybe the issue is related to webviewextras, maybe to the b4a webview itself.
 

oceanwanderlust

Member
Licensed User
Longtime User
My app loads a webpage, then uses JavaScript to inspect the state of the page or occasionally automatically push a button on the webpage. I'm using a timer to automatically reload the webpage every few minutes. The program runs fine for a while, but then eventually I quit receiving _PageFinished events even though Timer3 keeps reloading the page. Why do I suddenly stop receiving _PageFinished messages? Am I reloading the page correctly?

joe

EDIT: removing code because unrelated to solution
 
Last edited:

warwound

Expert
Licensed User
Longtime User
What does your ProcessHTML sub do?
Does it modify the user interface (views etc)?

Is that causing a problem?
 

oceanwanderlust

Member
Licensed User
Longtime User
ProcessHTML checks if the page is properly displaying one of two webpages, if not, it starts yet another Timer2 to try to reload the page again quickly and maybe fiddle with the wifi. My problem is that eventually _PageFinished is not called so ProcessHTML never gets called, even though Timer3 IS being called and is supposed to be reloading WebView1 every few minutes.

EDIT:

1) removing code because unrelated to solution

Summary/Solution: As my app demonstrated after many Reloads, WebView simply fails to call PageFinished. I had already injected javascript into the page, so Warwound's solution to use "javascript Location reload() method to reload" reliably reloads it infinitely.
 
Last edited:

warwound

Expert
Licensed User
Longtime User
Hmm...

Well if your ProcessHTML Sub causes any part of the user interface to be modified then you need to change your javascript:

B4X:
Javascript="B4A.CallSub('ProcessHTML', true, document.documentElement.outerHTML)"

I've changed the second parameter to true from false.
That's worth trying - it won't take long to change and might be a quick fix.

Otherwise i'm not sure where to look next.
You could try using the javascript Location reload() method to reload the webpage instead of using the WebView Reload method.

I suspect there is no error in your code and that the WebView is just failing after the webpage has been reloaded many times.
 

Swissmade

Well-Known Member
Licensed User
Longtime User
Please place your Code in Code Insert so this will be better to read.
Like see below.

B4X:
Sub ProcessHTML(FlingableWebView1 As FlingableWebView, Html As String)
If Html.Contains(ACTIVE_STATE_STRING) = True Then
Timer1.Enabled = False
Else If Html.Contains(INACTIVE_STATE_STRING) = True Then
If Timer1.Enabled = False Then
Timer1.Enabled = True    'this timer does a JavaScript Click() on the page in about an hour...
End If
Else
Log("PAGE ERROR")
Timer2.Enabled=True  'this timer tries to refresh the webpage in ~10 seconds and also fiddles with the wifi
End If
End Sub



Thanks
 

oceanwanderlust

Member
Licensed User
Longtime User
Why am I getting a runtime "signature does not match expected signature" for HandleDoorbellWebview_ReceivedError? I think this might be an interesting message to catch, and also shows that I might be misunderstanding something about webview vs webviewclient events.

joe
 

warwound

Expert
Licensed User
Longtime User
Depending on the version of WebViewExtras you are using the event passes different parameters.

With WebViewExtras2 the Sub parameters are:

B4X:
ReceivedError(ErrorCode As int, Description As String, FailingUrl As String)

A version i created for the FlingableWebView has parameters:

B4X:
ReceivedError(FlingableWebView1 As FlingableWebView, ErrorCode As int, Description As String, FailingUrl As String)

The solution is to use the b4a IDE autocomplete to see what parameters are required for the version you are using.

In your activity module start a new line.
Type Sub then space and then press the TAB key.
Select DefaultWebViewClient from the drop down liat and then select the ReceivedError event.
The IDE will create the Sub with the parameters required.
 

Croïd

Active Member
Licensed User
Longtime User
Please ! how can we insert the web text "class" in a EditText ?

For example : WebView1.LoadUrl("https://www.b4x.com/index.html")

WebViewExtras1.executeJavascript(WebView1, "B4A.CallSub('Javascript', true,document.title)")

Edittext Show = <title>B4X - The simple way to develop native Android &amp; iOS apps</title>


It is possible read : <span class="title">Latest Versions</span>
 
Last edited:

FJS

Active Member
Licensed User
Longtime User
Hello everybody,

I am trying to use the following html code:

<script type="text/javascript" src="http://ad.leadboltads.net/show_app_ad.js?section_id=xxxxxxxxx"></script>

As you can see, the idea is showing ads,

with a webview like:

WebView1.LoadHtml("<script type="text/javascript" src="http://ad.leadboltads.net/show_app_ad.js?section_id=xxxxxxxxx"></script>")

however it has a syntax error... and only the text apears:(

Please, any expert could you tell me how white right the information?

Thank you!
 

warwound

Expert
Licensed User
Longtime User
WebView1.LoadHtml("<script type="text/javascript" src="http://ad.leadboltads.net/show_app_ad.js?section_id=xxxxxxxxx"></script>")

Doesn't the IDE give you a syntax error on that line with those double quotes within a string?
I'd try something like this:

B4X:
WebView1.LoadHtml("<script type='text/javascript' src='http://ad.leadboltads.net/show_app_ad.js?section_id=xxxxxxxxx'></script>")

If that still fails then you probably need to pass a valid page of HTML to the LoadHtml method instead of a single <script> tag:

B4X:
<!DOCTYPE html>
<html>
<head>
<title></title>
<script type='text/javascript' src='http://ad.leadboltads.net/show_app_ad.js?section_id=xxxxxxxxx'></script>
</head>
<body>
</body>
</html>
 

Derek Johnson

Active Member
Licensed User
Longtime User
Please ! how can we insert the web text "class" in a EditText ?

For example : WebView1.LoadUrl("https://www.b4x.com/index.html")

WebViewExtras1.executeJavascript(WebView1, "B4A.CallSub('Javascript', true,document.title)")

Edittext Show = <title>B4X - The simple way to develop native Android &amp; iOS apps</title>


It is possible read : <span class="title">Latest Versions</span>

Something like:

B4X:
Sub Webview1_PageFinished (Url As String)
    '...
    Get1stElementByClass("title")

End Sub

Sub Get1stElementByClass(id As String)

Dim js As String=$"
    var elems = document.getElementsByClassName('${id}');
    if (elems.length>0) {
        B4A.CallSub('j_result', true, elems[0].innerHTML )
    } else {
        B4A.CallSub('j_result', true, '' )
    }
"$
    WebViewExtras1.executeJavascript(WebView1,js)
End Sub


Sub j_result(result As String)
    Log("Result was " & result )
    'edittext1.text=result

End Sub

I've corrected my original post having checked it out with an example.
 
Last edited:

Croïd

Active Member
Licensed User
Longtime User
Something like:

B4X:
Sub Webview1_PageFinished (Url As String)
    '...
    Get1stElementByClass("title")

End Sub

Sub Get1stElementByClass(id As String)

Dim js As String=$"
    var elems = document.getElementsByClassName(${id})
    If (elems.length>0) {
        B4A.CallSub('j_result', true, elems[0].innerHTML )
    } else {
        B4A.CallSub('j_result', true, '' )
    }
"$
    WebViewExtras1.executeJavascript(WebView1,js)
End Sub


Sub j_result(result As String)
    Log("Result was " & result )
    'edittext1.text=result

End Sub

Many Thanks Derek
 

Derek Johnson

Active Member
Licensed User
Longtime User
Derek I need to put additional ?

Sorry missing a couple of quote marks in the Javascript. This code works now.

B4X:
Sub WebView1_PageFinished (Url As String)

'find  <span class="title">Latest Versions</span>
    Get1stElementByClass("title")
End Sub


Sub Get1stElementByClass(id As String)
Dim js As String=$"
    var elems = document.getElementsByClassName('${id}');
    if (elems.length>0) {
        B4A.CallSub('j_result', true, elems[0].innerText )
    } else {
        B4A.CallSub('j_result', true, 'No element found' )
    }
"$
    WebViewExtras1.executeJavascript(WebView1,js)
End Sub

Sub j_result(result As String)
   Log("Result was " & result )
    EditText1.text= result
End Sub
 
Top