So your Basic4android Subs can't touch the UI, maybe a big deal, and can only accept primitives, probably not such a big deal.Remember that when methods of the passed java object is called in javascript context, it's not gonna be invoked in the Main(UI) thread, and if you need to get it called in the UI thread you will need to use a Handler and post a new runnable to run your code.
...
another important thing to remember is the fact that you cannot pass or return any complex object to or from javascript context, that is all you are allowed to use are primitive and String data types.
I don't see that unless I have missed something. SL4A appears to be a mechanism for exposing the Android API to interpreters that normally would not be able to access it and also providing a script editor for on device development of scripts for those interpreters.(SL4A) supports messaging between Javascript and a dozen scripting languages
However even if it doesn't work out of the tin it should be possible with a Basic4android library to provide an interface to addJavascriptInterface(). If you write one of those Webby program things that you think ought to work but doesn't then post it here and I'll have a play. For your attempt note that Basic4android method names are all lower case and comprise an underscore followed by the Sub name. So "Sub Fred" is compiled as "_fred".
java.lang.NoSuchMethodException: addJavascriptInterface
'Activity module
Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.
End Sub
Sub Globals
'These global variables will be redeclared each time the activity is created.
'These variables can only be accessed from this module.
Dim myWebView As WebView
End Sub
Sub Activity_Create(FirstTime As Boolean)
End Sub
Sub Activity_Resume
Activity.LoadLayout("layoutMain")
myWebView.Width=100%x
myWebView.Height=100%y
myWebView.LoadUrl("file:///android_asset/my_web_page.htm")
End Sub
Sub Activity_Pause (UserClosed As Boolean)
End Sub
Sub myWebView_PageFinished (Url As String)
Dim Obj1 As Reflector
Obj1.Target=myWebView
' all attempts to run the method produce an error:
' java.lang.NoSuchMethodException: addJavascriptInterface
' Obj1.RunMethod("addJavascriptInterface")
Obj1.RunMethod2("addJavascriptInterface", "myNewJSMethod", "java.lang.String")
End Sub
'Code module
'Subs in this code module will be accessible from all modules.
Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.
End Sub
Sub myNewJSMethod(myString As String)
' this will be the new javascript method
Log("This is the new javascript method executed")
Log(myString)
End Sub
Dim Obj1 As Reflector
Dim Obj2 As Reflector
Obj1.Target=WebV
Obj2.Target = Obj2.GetMostCurrent("main")
Msgbox(Obj1.ToString, "Target 1")
Msgbox(Obj2.ToString, "Target 2")
Obj1.RunMethod3("addJavascriptInterface", Obj2.Target, "java.lang.Object", "B4A", "java.lang.String")
Sub Activity_Resume
Activity.LoadLayout("layoutMain")
myWebView.Width=100%x
myWebView.Height=100%y
Dim Obj1 As Reflector
Obj1.Target=myWebView
Dim interface As JSInterface
Obj1.RunMethod3("addJavascriptInterface", interface, "java.lang.Object", "myinterface", "java.lang.String")
myWebView.LoadUrl("file:///android_asset/my_web_page.htm")
End Sub
package martinpearman.co.uk.jsinterface;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
import anywheresoftware.b4a.BA.Author;
import android.util.*;
@ShortName("JSInterface")
@Version(1)
@Author("Martin Pearman")
public class JSInterface {
public JSInterface(){}
public String newMethod(){
Log.v("martin", "hello console");
String str="It works!";
return str;
}
public String ToString(){
return "My debug message";
}
public String newMethod2(){
return "new method2";
}
}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JSMethods addJavascriptInterface test page</title>
<script type="text/javascript">
function init(){
// the page has loaded, lets try and call the new javascript method
var html=[];
if(document.myinterface){
html.push('"document.myinterface" IS defined');
html.push('Properties of document.myinterface are:');
for(var property in document.myinterface){
html.push(property);
}
html.push('End of document.myinterface properties');
} else {
html.push('"document.myinterface" is NOT defined');
}
if(window.myinterface){
html.push('"window.myinterface" IS defined');
html.push('Properties of window.myinterface are:');
for(var property in window.myinterface){
html.push(property);
}
html.push('End of window.myinterface properties');
} else {
html.push('"window.myinterface" is NOT defined');
}
document.getElementById('myDiv2').innerHTML=html.join('<br />');
document.getElementById('myDiv').innerHTML+=' debug #2';
try{
document.getElementById('myDiv').innerHTML+=window.myinterface.newMethod2()+' debug';
} catch(error){
document.getElementById('myDiv').innerHTML=error;
}
document.getElementById('myDiv').innerHTML+=' debug #3';
}
</script>
</head>
<body>
<div id="myDiv">debug #1</div>
<a href="javascript:void(0)" onclick="init()">Click me</a>
<div id="myDiv2"></div>
</body>
</html>
public class JSInterface {
public JSInterface(){}
public newInterface getInterface(){
return new newInterface();
}
}
class newInterface{
public String newMethod(){
Log.v("martin", "hello console");
String str="It works!";
return str;
}
public String ToString(){
return "My debug message";
}
public String newMethod2(){
return "new method2";
}
}
Dim interface As JSInterface
Obj1.RunMethod3("addJavascriptInterface", interface.getInterface, "java.lang.Object", "myinterface", "java.lang.String")
If you end up being able to call methods from your library I can see no reason why you couldn't call Basic4android Subs using my original code fragment as long as you lowercase the names and prefix them with an underscore.Dump the "window." -- just reference FileUtil directly.
> Then I displayed all members of the window object and my "FileUtil" is
> missing.
It's not supposed to be there. You do not get an actual Javascript
object from addJavascriptInterface().
It is a bit more like you get a new Javascript keyword -- you can write
"FileUtil.read()", and the Javascript interpreter will interpret it, but
there is no FileUtil object.
package martinpearman.co.uk.jsinterface;
import android.webkit.WebView;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
import anywheresoftware.b4a.BA.Author;
@ShortName("JSInterface")
@Version(1)
@Author("Martin Pearman")
public class JSInterface {
static NewInterface thisInterface=new NewInterface();
public void addInterface(WebView myWebView, String interfaceName){
myWebView.addJavascriptInterface(thisInterface, interfaceName);
}
}
package martinpearman.co.uk.jsinterface;
final class NewInterface {
public String newMethod(String myString) {
myString=ReverseString.reverseIt(myString);
return myString;
}
}
class ReverseString {
public static String reverseIt(String source) {
int i, len = source.length();
StringBuffer dest = new StringBuffer(len);
for (i = (len - 1); i >= 0; i--)
dest.append(source.charAt(i));
return dest.toString();
}
}
Activity.LoadLayout("layoutMain")
myWebView.Width=100%x
myWebView.Height=100%y
Dim interface As JSInterface
interface.addInterface(myWebView, "myinterface")
myWebView.LoadUrl("file:///android_asset/my_web_page.htm")
I would have thought it should have as long as you got an instance from Reflector.GetGetMostCurrent and used the internal Basic4android method names. Maybe it's because the Basic4android methods are static. If you want to post a small app with a web page that calls into your library I would be happy play to see whether it is possible to call into a code module from Javascript.I added a codemodule to my project ... That didn't work.
public Object raiseEvent(Object sender, String event, Object[] params) {
private BA ba
public void Initialize(BA aba)
{
ba = aba;
}
// can't pass Object arrays from Javascript
// Javascript might need String or primitive and not Object return types - I don't know.
// If it does add a cast to the expected type to the return from raiseEvent
public Object CallSub0(String sub)
{
sub = sub.toLowerCase();
return ba.raiseEvent(this, sub, new Object[0]);
}
public Object CallSub1(String sub, String param1)
{
sub = sub.toLowerCase();
return ba.raiseEvent(this, sub, new Object[] = { param1});
}
...
Sub myJSInterfaceInstance_messageReceived(messageName As String, message As String)
' code here
End Sub
The compiler will give you a BA instance if you want one by using the invisible (to the Basic4android programmer) BA parameter in a method call.
typeof valueReturnedFromJavaMethodToJS is always 'object'
value = value + '';
That's an interesting possibility. What is the mechanism for external code to trigger a Javascript event. I haven't seen any reference to that possibility while Googling for Javascrip information.Send data from B4A to javascript - the interface would trigger a javascript event and the javascript would ask the interface for the data when the event fires.
This is exactly the same as the Javascript calling a CallSub method in the library that then calls the B4A Sub. The only difference is that the name of the called Sub is decided by the Javascript and not by the library code.Send data from javascript to B4A - the interface library instance could listen for an event
In the example code you posted public void Initialize(BA aba), whereabouts is this Initialize method called from .
Dim Whatever As JSInterface
Whatever.Initialize ' the compiler adds the BA instance parameter to the call