Android Question Interact with webview

TheRealMatze

Active Member
Licensed User
Hi,
we decide to use more functionality from webview, because the main components are already working in html. So, i try to interact between webview and b4x.
I try the example which pulls a webview function from the activity. that works. But more important is the other way, pushing from the webview to the activity.
I found a article here that says the easiest way for b4a and b4i is the "OverrideUrl" from webview. I´ve tried it - it does nothing :/

Can anyone tell me what my mistake is?

B4X:
#Region  Project Attributes
    #ApplicationLabel: B4A Example
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals

End Sub

Sub Globals
    Private WebView1 As WebView
    Dim we As WebViewExtras
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("1")
    we.addJavascriptInterface(WebView1,"B4A")
    WebView1.LoadUrl(WebViewAssetFile("index.html"))
End Sub

Sub TheContentIs(content As String)
    Msgbox(content,"Content From JS")
End Sub

Sub WebViewAssetFile (FileName As String) As String
   #if B4J
     Return File.GetUri(File.DirAssets, FileName)
   #Else If B4A
    Dim jo As JavaObject
    jo.InitializeStatic("anywheresoftware.b4a.objects.streams.File")
    If jo.GetField("virtualAssetsFolder") = Null Then
        Return "file:///android_asset/" & FileName.ToLowerCase
    Else
        Return "file://" & File.Combine(jo.GetField("virtualAssetsFolder"), _
       jo.RunMethod("getUnpackedVirtualAssetFile", Array As Object(FileName)))
    End If
   #Else If B4i
     Return $"file://${File.Combine(File.DirAssets, FileName)}"$
   #End If
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Button1_Click
    we.executeJavascript(WebView1,"javascript: doCallBack();")
End Sub

Sub Webview1_OverrideUrl (Url As String)
    Msgbox(Url,"Called")
    If Url.Contains ("#") Then
    Msgbox(Url,"Contains Hash")
    End If
End Sub

In the html is a button

HTML:
<button onclick="changeHash('#nice',this)">Push me</button>

</body>
<script>
function changeHash(h,t){
window.location.href=h
t.innerHTML=h;
}
</script>

Webview1_OverrideUrl will not called...

Regards
Matze
 

drgottjr

Expert
Licensed User
Longtime User
it's not clear what you mean by "pushing from the webview to the activity".
what are we pushing?

there is an issue with window.location.href and webviewclient's shouldoverrideurlloading().
i want to test a couple things. in the meantime, if you like, do this:
make the button an "<a href=...>" tag and point it to https://www.b4x.com. click on the
link. that should trigger webview's OverrideUrl event (at least on b4a. technically, webviews
on b4j and ios are more akin to webkit. b4a's is closer to android's interpretation of webkit.)

don't forget that OverrideUrl is expecting you to return true (to override) or false (to let continue).
it's a clever way of letting you deal with something in b4a that you would have to do in java
directly otherwise.

if OverridUrl fires (which i think it should), then the problem would seem to be window.location.href.
you can research that if you like.

also, to debug webview, you need to add chromeclient to your webviewextras.
 
Upvote 0

TheRealMatze

Active Member
Licensed User
Ok, it needs a url...

<button onclick="changeHash('https://www.test.de#nice',this)">Push me</button>
<a href="file://index.html#test">yes!</a>

works a little bit more. But after the second msgbox the app crashes in the simulator and on the phone.

Sub Webview1_OverrideUrl (Url As String)
Msgbox(Url,"Called")
If Url.Contains ("#") Then
Msgbox(Url,"Contains Hash")
End If
Return True
End Sub

But by the way - the webview is working under b4i also, correct?
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
But after the second msgbox the app crashes in the simulator and on the phone.
MsgBox is deprecated and should be replaced by async methods.
I guess it can be problmatic to use these days.
 
Last edited:
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
Ok, it needs a url...

<button onclick="changeHash('https://www.test.de#nice',this)">Push me</button>
<a href="file://index.html#test">yes!</a>

works a little bit more. But after the second msgbox the app crashes in the simulator and on the phone.

Sub Webview1_OverrideUrl (Url As String)
Msgbox(Url,"Called")
If Url.Contains ("#") Then
Msgbox(Url,"Contains Hash")
End If
Return True
End Sub

But by the way - the webview is working under b4i also, correct?

as donmanfred says, async msgbox, especially when you've got background threads all tied up waiting for you.
also don't use msgbox(async) for debugging. use log.
and you shouldn't blindly use true, even when practicing. a little disicipline, right? if you want to override (given some condition) then return true else return false. one less thing to wonder about later.
you'll have to run your code on those other platforms to see what gives...
 
Upvote 0

TheRealMatze

Active Member
Licensed User
Ok, no msgbox. Done. The return-value - yes, but without any condition to test...

I have tried different options. One was a success, but i don´t understand what i do with it.

I want to see an when the crash begins, so i included a sleep(1000) into overrideUrl. The compiler dont like that, because in this case the return-value is not allowed. So, i removed the return - and it stop crashing... After that the event is fired. So far so good, but i don´t think that is correct.

Sub Webview1_OverrideUrl (Url As String)
If Url.Contains ("#") Then
Button1.Text="With Hash " & Url
Else
Button1.Text="Nope, only " & Url
End If

Sleep(1)
End Sub
Only updating the hash is not working anymore...

Using webviewextras instead will solve my problems (under android). That works without any error, but i´m not sure if i can adopt it to ios.
When i add webviewextra to b4i i cant compile (webviewextra.h not found) - but even if i try to use the webview i run in trouble...
Sub Process_Globals
'These global variables will be declared once when the application starts.
'Public variables can be accessed from all modules.
Public App As Application
Public NavControl As NavigationController
Private Page1 As Page
Private xui As XUI
Private Button1 As Button
Private WebView1 As WebView
'Dim we As WebViewExtras
End Sub

Private Sub Application_Start (Nav As NavigationController)
NavControl = Nav
Page1.Initialize("Page1")
Page1.RootPanel.LoadLayout("Page1")
NavControl.ShowPage(Page1)
'we.addJavascriptInterface(WebView1,"activity")
'Url(File.Combine(File.DirAssets, "index.html"))

WebView1.LoadHtml("This is HTML")
End Sub

I see no way to load a local html-file, that´s bad bad not essential. LoadHtml is working on both plattforms, so the Html will be hardcoded into the app. But the missing WebViewExtra is a problem. In ios Webview1_OverrideUrl works on hashtag...

So for me it looks like

ios can´t load local html files
ios cant use webviewextra
android cant use overrideurl

Is this correct?

Matze
 
Upvote 0

roumei

Active Member
Licensed User
I didn't follow the whole thread, so just a thought: Why don't you use Javascript for the interaction between your B4X app and the webview? The Javascript approach is a little different for Android and iOS but it definitely works.
I also tried Override_URL but found that it didn't fire in the speed I needed for my app.
 
Upvote 0

TheRealMatze

Active Member
Licensed User
maybe i search for the wrong keywords, but i can´t find anything else then webviewextra or overrideurl...
Do you have a example?
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
That looks good, but how can i call the sub from javascript?
It is described in the library thread
 
Upvote 0

roumei

Active Member
Licensed User
Probably better to start a new thread for the non-B4A things.
It's in the B4I example. The B4I button demonstrates the JavaScript code (postMessage):
B4X:
WebView1.EvaluateJavaScript("window.webkit.messageHandlers.callback.postMessage('postMessage successful " & DateTime.Now & "');")
 
Upvote 0

roumei

Active Member
Licensed User
I don't know. I just do it like this:
B4X:
        function SendMessage(ID, msg)
        {
        try{B4A.CallSub(ID, true, msg);}catch(err){}
        try{window.webkit.messageHandlers.callback.postMessage(ID + '@' + msg);}catch(err){}
        }
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
i asked you early on what you were "pushing". i suspected it
was javascript injection, but my approach is to try to answer
the question(s) posed by the poster.

i don't know how you latched onto overrideurl() in the first
place (as it relates to your app), but it seems to me that
you don't understand how overrideurl works since, mainly,
you continue to use it incorrectly. it may not even be necessary,
but it's what you asked about. in any case, you should
understand how the webviewclient method shouldoverrideurlloading()
works and its relation to b4a's overrideurl().

javascript injection is a whole other matter, and, possibly what
you're after. i'm guessing you're dropping overrideurl() at this point,
but to say that it doesn't work is not accurate.
 
Upvote 0

TheRealMatze

Active Member
Licensed User
I havn´t "say" it doesn´t work. In my test it will not, and i asked general if my determination is correct or not. What i see is, that overrideurl fires when a complete url is called, but not only a page-link (http://test.me works but #jumpthere not).
I havn´t seen shouldoverrideurlloading before, i just search for it in the b4a-webview section in the documentation, but there is no information about it. it´s difficult to understand the relationship between parts without knowing their existence.

At the end of the day i only need a way to interact both directions. And the combination of jscallback and webviewextras looking good for this task.
 
Upvote 0

Ivica Golubovic

Active Member
Licensed User
Hi,
we decide to use more functionality from webview, because the main components are already working in html. So, i try to interact between webview and b4x.
I try the example which pulls a webview function from the activity. that works. But more important is the other way, pushing from the webview to the activity.
I found a article here that says the easiest way for b4a and b4i is the "OverrideUrl" from webview. I´ve tried it - it does nothing :/

Can anyone tell me what my mistake is?

B4X:
#Region  Project Attributes
    #ApplicationLabel: B4A Example
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals

End Sub

Sub Globals
    Private WebView1 As WebView
    Dim we As WebViewExtras
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("1")
    we.addJavascriptInterface(WebView1,"B4A")
    WebView1.LoadUrl(WebViewAssetFile("index.html"))
End Sub

Sub TheContentIs(content As String)
    Msgbox(content,"Content From JS")
End Sub

Sub WebViewAssetFile (FileName As String) As String
   #if B4J
     Return File.GetUri(File.DirAssets, FileName)
   #Else If B4A
    Dim jo As JavaObject
    jo.InitializeStatic("anywheresoftware.b4a.objects.streams.File")
    If jo.GetField("virtualAssetsFolder") = Null Then
        Return "file:///android_asset/" & FileName.ToLowerCase
    Else
        Return "file://" & File.Combine(jo.GetField("virtualAssetsFolder"), _
       jo.RunMethod("getUnpackedVirtualAssetFile", Array As Object(FileName)))
    End If
   #Else If B4i
     Return $"file://${File.Combine(File.DirAssets, FileName)}"$
   #End If
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Button1_Click
    we.executeJavascript(WebView1,"javascript: doCallBack();")
End Sub

Sub Webview1_OverrideUrl (Url As String)
    Msgbox(Url,"Called")
    If Url.Contains ("#") Then
    Msgbox(Url,"Contains Hash")
    End If
End Sub

In the html is a button

HTML:
<button onclick="changeHash('#nice',this)">Push me</button>

</body>
<script>
function changeHash(h,t){
window.location.href=h
t.innerHTML=h;
}
</script>

Webview1_OverrideUrl will not called...

Regards
Matze
If I understood you correctly, when you click on 'Button1' should the javascript be executed and the response sent by the javascript interface be captured?

Ok, I will try to help you...
First, download library from this link: https://www.b4x.com/android/forum/threads/ultimatewebview-custom-view.135666/#post-860033

Merge WebView with UltimateWebView as in the code below.
Example::
Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("Main") 'Use your activity name
    
    UltimateWebView1.Initialize(Me, "UltimateWebView1") 'Initializes UltimateWebView
    UltimateWebView1.WebView=WebView1 'Merge your WebView with UltimateWebView
    
    UltimateWebView1.SetWebViewClient
    UltimateWebView1.SetWebChromeClient
    UltimateWebView1.Settings.JavaScriptEnabled=True 'Very important for JavaScripts
    UltimateWebView1.Settings.AllowContentAccess=True
    UltimateWebView1.Settings.AllowFileAccess=True
    UltimateWebView1.Settings.AppCacheEnabled=True
    UltimateWebView1.Settings.CacheMode=UltimateWebView1.Settings.Constants.CACHEMODE_LOAD_DEFAULT
    UltimateWebView1.Settings.JavaScriptCanOpenWindowsAutomatically=True
    UltimateWebView1.Settings.DisplayZoomControls=False
    UltimateWebView1.Settings.DomStorageEnabled=True
    UltimateWebView1.Settings.MediaPlaybackRequiresUserGesture=False
    UltimateWebView1.Settings.AllowFileAccessFromFileURLs=True
    UltimateWebView1.Settings.AllowUniversalAccessFromFileURLs=True
    UltimateWebView1.Settings.GeolocationEnabled=True
    UltimateWebView1.SetDownloadListener
    
    UltimateWebView1.CookieManager.AcceptCookies=True
    UltimateWebView1.CookieManager.AcceptThirdPartyCookies=True
    UltimateWebView1.CookieManager.AcceptFileSchemeCookies=True
    UltimateWebView1.CookieManager.Flush

    UltimateWebView1.LoadUrl(WebViewAssetFile("index.html"))
End Sub

Second use this code in your Button1_Click Sub (For synchronized result):
Example::
Sub Button1_Click
    UltimateWebView1.ExecuteJavaScript2([Your JavaScript Code])
    Wait For UltimateWebView1_ExecuteJavaScriptResult (Result As String)
    If Result<>"" Then
        If Result.Contains ("#") Then
            Log("Result Contains Hash")
        End If
    End If
End Sub

For Asynchronized result:
Example::
Sub Button1_Click
    UltimateWebView1.ExecuteJavaScript2([Your JavaScript Code])
End Sub

'Result Event:
Sub UltimateWebView1_ExecuteJavaScriptResult (Result As String)
    If Result<>"" Then
        If Result.Contains ("#") Then
            Log("Result Contains Hash")
        End If
    End If
End Sub
 
Last edited:
Upvote 0
Top