Hi all, it is possibile to raise an event in different module than a calling module ?
I search to explain better, I wrote a class in java that add a WebChromeClient to a webview and add some funtionalities (as main goal a simple JS console).
Next I compiled this class to a B4A library, created xml and jar files and put in B4A Addictional libraries.
After this I started a new B4A project, then created a class that manage UI (for a JS console), this class use a WebChromeClient class to receive callbacks,
then send these to the Main.
When all worked I've compiled together as library directly from B4A IDE (Compile as library).
Substantially I have this:
Main > JSConsole Class > WebChromeClient
The main initialize the JSConsole class and this class initialize and use a WebChromeClient, the class JSConsole in the middle acts as bridge from the WebChromeClient and the Main.
In a WebChromeClient I actually use onConsoleMessage event to trap consoles messages, then I use RaiseEvent to send to JSConsole class and this send it back to the Main with CallSub().
All this is slow, sometime it miss console messages, I even collected messages using StringBuilder, this help but not too mutch.
I even changed the original CallSubDelayed in the JSConsole class, with CallSub, the callback function receive from a WebChromeClient and send to the Main,
this helped too.
The main problem is that I'm not sure this is the right way to do it....
What I want is to raise events from the WebChromeClient directly to the Main (or a calling module in general) without involve JSConsole class in the middle that only manage UI, so even reduce calls to functions.
Because I even implemented Javascript Interface with the command addJavascriptInterface (like WebViewExtras and UltimateWebView) with even return callbacks and multiple arguments I need to call a function directly to the Main that is exposed to the end user.
Is that possible and if yes how ?
I've attached some relevant code.
Many thanks
I search to explain better, I wrote a class in java that add a WebChromeClient to a webview and add some funtionalities (as main goal a simple JS console).
Next I compiled this class to a B4A library, created xml and jar files and put in B4A Addictional libraries.
After this I started a new B4A project, then created a class that manage UI (for a JS console), this class use a WebChromeClient class to receive callbacks,
then send these to the Main.
When all worked I've compiled together as library directly from B4A IDE (Compile as library).
Substantially I have this:
Main > JSConsole Class > WebChromeClient
The main initialize the JSConsole class and this class initialize and use a WebChromeClient, the class JSConsole in the middle acts as bridge from the WebChromeClient and the Main.
In a WebChromeClient I actually use onConsoleMessage event to trap consoles messages, then I use RaiseEvent to send to JSConsole class and this send it back to the Main with CallSub().
All this is slow, sometime it miss console messages, I even collected messages using StringBuilder, this help but not too mutch.
I even changed the original CallSubDelayed in the JSConsole class, with CallSub, the callback function receive from a WebChromeClient and send to the Main,
this helped too.
The main problem is that I'm not sure this is the right way to do it....
What I want is to raise events from the WebChromeClient directly to the Main (or a calling module in general) without involve JSConsole class in the middle that only manage UI, so even reduce calls to functions.
Because I even implemented Javascript Interface with the command addJavascriptInterface (like WebViewExtras and UltimateWebView) with even return callbacks and multiple arguments I need to call a function directly to the Main that is exposed to the end user.
Is that possible and if yes how ?
I've attached some relevant code.
Many thanks
Some relevant parts of B4AWebChromeClient wrapper:
public void Initialize(final BA mBA, final Object CallbackModule, WebView webview, String event) {
ba = mBA;
this.webview = webview;
this.eventName = event.toLowerCase(BA.cul);
isInitialized = true;
// BA.Log("isInitialized: " + isInitialized);
this.webview.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onConsoleMessage(ConsoleMessage cm) {
//if (ba.subExists(eventName + "_message")) {
// BA.Log(System.currentTimeMillis() + " JAVA: " + cm.message());
ba.raiseEvent(this, eventName + "_message", new Object[] {cm.message(), cm.sourceId(), (int)cm.lineNumber(), cm.messageLevel().toString()});
//}
return true;
}
@Override
public void onProgressChanged(WebView webview, int newProgress) {
if (webview.getTitle() == null || webview.getOriginalUrl() == null) return;
//if (ba.subExists(eventName + "_progress")) {
//BA.Log("Raise Event: " + fullEventName2);
ba.raiseEvent(this, eventName + "_progress", newProgress, webview.getTitle(), webview.getOriginalUrl());
//} //else {
//BA.Log("CALLER Event Sub do not exists: " + eventName + "_progress");
//}
}
});
}
//........................
//........................
public void AddJavascriptInterface(String InterfaceName) {
this.webview.getSettings().setJavaScriptEnabled(true);
this.webview.addJavascriptInterface(new Object() {
private int mTaskId = 0;
@JavascriptInterface
public Object CallSub(String SubName, boolean pCallUIThread) {
String lSubName = SubName.toLowerCase();
BA.Log("Call a B4A sub without arguments: " + lSubName);
if (ba.subExists(lSubName)) {
if (pCallUIThread) {
BA.Log("Expecting return from " + SubName);
return (Object)ba.raiseEventFromDifferentThread(this, this, mTaskId++, lSubName, false, new Object[0]);
} else {
BA.Log("DO NOT Expecting return from " + SubName);
return (Object)ba.raiseEvent(this, lSubName, new Object[0]);
}
}
BA.Log("JavascriptInterface error: " + SubName + " does not exist");
return "JavascriptInterface error: " + SubName + " does not exist";
}
@JavascriptInterface
public Object CallSub(String SubName, boolean pCallUIThread, Object parameter1) {
String lSubName = SubName.toLowerCase();
BA.Log("Call a B4A sub with one argument: " + lSubName);
if (ba.subExists(lSubName)) {
Object[] parameter = { parameter1 };
if (pCallUIThread) {
BA.Log("Expecting return from " + SubName);
return (Object)ba.raiseEventFromDifferentThread(this, this, mTaskId++, lSubName, false, parameter);
} else {
BA.Log("DO NOT Expecting return from " + SubName);
return (Object)ba.raiseEvent(this, lSubName, parameter);
}
}
//........................
//........................
public void ExecuteJavascript (String Script) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){ // KITKAT API 19
this.webview.evaluateJavascript(Script, null); // new ValueCallback<String>() {
} else {
this.webview.loadUrl("javascript:" + Script);
}
}
Some relevant parts of JSConsole class:
Private Sub Class_Globals
Private WebChromeClient As B4AWebChromeClient
End Sub
Public Sub Initialize(Parent As Panel, Module As Object, EventName As String, WebView As WebView, Height As Int, Duration As Int)
'.................
WebChromeClient.Initialize(WebView, "ChromeClient")
'.................
End Sub
Public Sub AddJavascriptInterface(InterfaceName As String)
WebChromeClient.AddJavascriptInterface(InterfaceName)
End Sub
Public Sub ExecuteJavascript(Script As String)
WebChromeClient.ExecuteJavascript(Script)
End Sub
Private Sub ChromeClient_Message (Message As String, SourceID As String, LineNumber As Int, Level As String)
If SubExists(mModule, mEventName & "_Message") = False Then
' Log("SUB " & mModule & mEventName & "_Message DO NOT EXISTS")
Return
End If
Dim newDataStart As Int = sb.Length
sb.Append(Message).Append(",").Append(SourceID).Append(",").Append(LineNumber).Append(",").Append(Level).Append(Chr(10)).Append("+").Append("*")
Dim s As String = sb.ToString
Dim start As Int = 0
For i = newDataStart To s.Length - 1
Dim c As Char = s.CharAt(i)
If i = 0 And c = Chr(10) Then '\n...
start = 1 ' Might be a broken end of line character
Continue
End If
If i < s.Length - 2 And c = Chr(10) And s.CharAt(i + 1) = "+" And s.CharAt(i + 2) ="*" Then
Dim thisMessage As String = s.SubString2(start, i)
Dim msgArr() As String = Regex.Split(",", thisMessage) ' Return four components
cm.Message = msgArr(0)
cm.SourceID = msgArr(1)
cm.LineNumber = msgArr(2)
cm.Level = msgArr(3)
i = i + 2
' If SubExists(mModule, mEventName & "_Message") Then
CallSub2(mModule, mEventName & "_Message", cm)
If mAutoShow And mLogActive = False Then Show(mDuration) ' Automatically show a console on log messages (inly if hidden)
' End If
start = i + 1
End If
Next
If start > 0 Then sb.Remove(0, start)
End Sub
Private Sub ChromeClient_Progress (Progress As Int, Title As String, Url As String)
If SubExists(mModule, mEventName & "_Progress") = False Then Return
' If IsPaused(mModule) Then
' Do While IsPaused(mModule)
' Sleep(0)
' Loop
' End If
pm.Progress = Progress
pm.Title = Title
pm.Url = Url
' If SubExists(mModule, SubName) Then ' Here or use CallSub and check if caller is paused or use CallSubDelayed that start it before call a sub
' Log(">>>>> The MAIN sub " & SubName & " exist. Calling it ...")
CallSubDelayed2(mModule, mEventName & "_Progress", pm) ' Call a function that log the String
' End If
End Sub
Some relevant code of Main:
Sub Process_Globals
' Test HTML
Dim MyHTML As String = $"
<!DOCTYPE html>
<html>
<body>
<center><h1>JS Console test</h1></center>
<p>Click the button below to increase a counter and show it on the Javascript log.</p>
<button onclick="Test()">Increase count</button>
<p>Click the button below to reload this page.</p>
<button onclick="SendToB4A()">Reload the page</button>
<script>
var cnt = 0;
function Test() {
cnt += 1;
console.log("The number is: [" + cnt + "]");
}
function SendToB4A() {
console.log("Call B4A function...")
B4A.CallSub("myB4Asub",false,"Hello!");
}
</script>
</body>
</html>
"$
End Sub
Sub Globals
Dim WebView1 As WebView
Dim Console As JSConsole
Dim ConsoleHeight As Int
End Sub
Sub Activity_Create(FirstTime As Boolean)
ConsoleHeight = 34%y
WebView1.Initialize("WebView1")
Activity.AddView(WebView1, 0, 0, 100%x, 100%y)
Console.Initialize(Activity, Me, "Console", WebView1, ConsoleHeight, 400)
Console.AddJavascriptInterface("B4A")
WebView1.LoadHtml(MyHTML)
End Sub
Sub WebView1_PageFinished (Url As String)
Log("Page finished: " & Url)
End Sub
Sub Console_Message(cm As ConsoleMessage) ' Message level can be TIP, LOG, WARNING, ERROR, DEBUG or UNKNOWN (As string)
If cm.SourceID.StartsWith("file") Then cm.SourceID = "current HTML"
Console.Console.Text = Console.Console.Text & $"${cm.Level}: ${cm.Message} (On line ${cm.LineNumber} of ${cm.SourceID})${CRLF}"$
Log("Received JS console message: " & cm) ' JS console to B4A console
End Sub
Sub Console_Progress(pm As ProgressMessage)
Console.Console.Text = Console.Console.Text & $"Progress: ${pm.Progress}% - Title: ${pm.Title} - URL: ${pm.Url}${CRLF}"$
Log($"Progress: ${pm.Progress} - ${pm.Title} - ${pm.Url}"$)
End Sub
Sub myB4Asub(Text As String) 'Ignore THIS CANNOT CALLED HERE, IF I PUT IT ON JSConsole CLASS IT WORKS, BUT I WANT CALL HERE DIRECTLY
Log("Message received from JS: " & Text)
End Sub
Last edited: