The InvokeScript() Method returns null.
JavaScript:
function gpsToAddress(gpsX, gpsY) {
var coords = new daum.maps.LatLng(gpsX, gpsY);
geocoder.coord2detailaddr(coords, CallbackA)}
function CallbackA(status, result) {
if(status===daum.maps.services.Status.OK)
{
return result[0].jibunaddress;
}
}
and C#:
private void f_ret_gpstoaddress(double v_gps_x, double v_gps_y,out string v_address)
{
object[] args = { "gpsToAddress(" + v_gps_x + "," + v_gps_y + ");" };
v_address = (string)webBrowser1.Document.InvokeScript("eval", args);
return;
}
private void button3_Click(object sender, EventArgs e)
{
f_ret_gpstoaddress(37.353933, 127.944739, out v_address);
MessageBox.Show(v_address);
}
the 'args' and 'v_address' returns null and the messageBox returns null, too.
I want to return some values.
Please, help me!
EDIT : OK, I edited the C# code like this:
private string f_ret_gpstoaddress(double v_gps_x, double v_gps_y, out string v_address)
{
var args = "gpsToAddress(" + v_gps_x + "," + v_gps_y + ");" ;
v_address = webBrowser1.Document.InvokeScript("eval",new object[] { args }).ToString();
return v_address;
}
private void button3_Click(object sender, EventArgs e)
{
f_ret_gpstoaddress(37.353933, 127.944739, out v_address);
MessageBox.Show(v_address);
}
So, args is not null, but v_address is still null. What's problem??
Your function gpsToAddress doesn't return anything. But it called a callback function when process is finished.
I suggest to you to pass the callback function when you called the function.
Please look this thread for more details : Make async event synchronous in JavaScript
Edit: Based on the above question :
Create a callback class
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class Callback
{
// allows an instance of Callback to look like a function to the script
// (allows callback() rather than forcing the script to do callback.callMe)
[System.Runtime.InteropServices.DispId(0)]
public void callMe(string v_address)
{
MessageBox.Show(v_address);
}
}
Call js function with a callback :
private string f_ret_gpstoaddress(double v_gps_x, double v_gps_y, out string v_address)
{
Callback cb = new Callback();
var args = "gpsToAddress(" + v_gps_x + "," + v_gps_y + "," + cb);" ;
v_address = webBrowser1.Document.InvokeScript("eval",new object[] { args }).ToString();
return v_address;
}
The js function take a callback as argument :
function gpsToAddress(gpsX, gpsY, callback) {
function CallbackA(status, result) {
if(status===daum.maps.services.Status.OK)
{
callback(result[0].jibunaddress);
}
else {
callback('');
}
}
var coords = new daum.maps.LatLng(gpsX, gpsY);
geocoder.coord2detailaddr(coords, CallbackA)
}
Edit 2 : (dummy sample)
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
string javascript = #"<html><head><script type='text/javascript'>function gpsToAddress(param1, callback) {
function CallbackA()
{
callback(param1);
}
setTimeout(function() { CallbackA() }, 1000);
}</script></head></html>";
public Form1()
{
InitializeComponent();
webBrowser1.DocumentText = javascript;
}
private void button1_Click(object sender, EventArgs e)
{
Callback cb = new Callback();
webBrowser1.Document.InvokeScript("gpsToAddress", new object[] { 123, cb });
}
}
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class Callback
{
// allows an instance of Callback to look like a function to the script
// (allows callback() rather than forcing the script to do callback.callMe)
[System.Runtime.InteropServices.DispId(0)]
public void callMe(string v_address)
{
MessageBox.Show(v_address);
}
}
}
Related
Say I define such a function:
function helloWorld(e) {
console.log("Hello " + e);
return;
}
How Can i be able to call it like this:
String funcName="helloWorld";
funcName(e);
In Java is there a style as simple as in Javascript?
This is known as Reflection:
import java.lang.reflect.Method;
public class Demo {
public static void main(String[] args) throws Exception{
Class[] parameterTypes = new Class[1];
parameterTypes[0] = String.class;
Method method1 = Demo.class.getMethod("method1", parameterTypes);
Demo demo = new Demo();
Object[] parameters = new Object[1];
parameters[0] = "message";
method1.invoke(demo , parameters);
}
public void method1(String message) {
System.out.println(message);
}
}
Taken from https://stackoverflow.com/a/4685609/5281806
I have used HybridWebView from XLabs to build a HybridWebView that can communicate with C# code. I have build custom renderers to do this. In simple terms I have:
iOS
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>, IWKScriptMessageHandler
{
const string JavaScriptFunctionTemplate = "function {0}(){{window.webkit.messageHandlers.invokeAction.postMessage('{0}|' + JSON.stringify(arguments));}}";
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
userController = new WKUserContentController();
foreach (var f in Element.RegisteredFunctions.Where(ff => !ff.IsInjected))
{
var script = new WKUserScript(new NSString(string.Format(JavaScriptFunctionTemplate, f.Name)), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
f.Injected();
}
userController.AddScriptMessageHandler(this, "invokeAction");
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView(Frame, config)
{
NavigationDelegate = new CustomWKNavigationDelegate(this.Element)
};
SetNativeControl(webView);
}
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
Load(Element.Uri);
}
}
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
var bits = message.Body.ToString().Split('|');
if (bits.Count() == 2)
{
var result = Element.InvokeFunction(bits[0], bits[1]);
//How do I return result back to Javascript function?
}
}
the InvokeFunction method returns a value result which is a string.
How can I return this string result back to the javascript function which was injected?
Edit
Do I need to edit my JavaScriptFunctionTemplate I notice in the XLabs they have a similar template but append something to the end
Is there any reason why you couldn't simply 're-call' your function using the methods described here?
Their example:
void OnCallJavaScriptButtonClicked (object sender, EventArgs e)
{
...
int number = int.Parse (numberEntry.Text);
int end = int.Parse (stopEntry.Text);
webView.Eval (string.Format ("printMultiplicationTable({0}, {1})", number, end));
}
So yours would be something like:
const string JavaScriptFunctionTemplate = "function {0}(){{window.webkit.messageHandlers.invokeAction.postMessage('{0}|' + JSON.stringify(arguments));}}";
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
var bits = message.Body.ToString().Split('|');
if (bits.Count() == 2)
{
var result = Element.InvokeFunction(bits[0], bits[1]);
webView.Eval(string.Format ("JavaScriptFunctionTemplate({0})", result)); // or however your parameter structure is.
}
}
EDIT:
Just want to encorporate the LINK that the OP found as it looks to be a solid guide to the solution in Objective-C which is fairly straight forward to convert to Xamarin C#.
We can run simple javascript function (jsCode) like:
function() {
return "test"
}
in webview:
String js = "(" + jsCode + ") ();"
mWebView.evaluateJavascript(js, new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
Log.d("return value: ", s); // Returns the value from the function
}
});
But what if my javascript depends on another module or reference another file like:
import { double } from 'mymodule';
Can this be achieved?
I achieved this by evaluating the external javascript file before evaluating the jsCode
private String[] jsFiles = {"external_lib.js", "main.js"};
private void evaluateJS() {
try {
String js = readFromAssets(jsFiles[fileIndex]);
Log.e(TAG, "evaluating: " + jsFiles[fileIndex]);
mWebView.evaluateJavascript(js, new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
fileIndex++;
if (fileIndex == jsFiles.length) {
// we have evaluated the last javascript file
Log.e("return value: ", s); // Returns the value from the function
return;
}
evaluateJS();
}
});
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
the code in main.js uses variables and functions that are defined in external_lib.js
In my android application, i have called one javascript method from html page to java page.
this.start = function ()
{
try
{
Mynamespace.myapi.getvalue("myvalue", {
onSuccess: function (data)
{
value= JSON.parse(data);
alert("called");
},
onFailure: function (error) { }
});
}
catch (err) { }
if (value== null) { value= new Object();
};
In the above, getvalue method is called in my java class and returned some values, but before getting the value inside the OnSuccess/OnFailure, the below line is called. How to wait the method up to the callback is called?
if (value== null) { value= new Object();
private class LongOperation extends AsyncTask<String, Void, String>
{
protected void onPreExecute()
{
progressDialog = new ProgressDialog(activity.this);
progressDialog.setTitle("Processing...");
progressDialog.setMessage("Please wait...");
progressDialog.setCancelable(true);
progressDialog.show();
}
protected String doInBackground(String... params)
{
try
{
//Getting data from server
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String result)
{
progressDialog.dismiss();
}
}
How to call this
ProgressDialog progressDialog;
LongOperation mytask = null;
mytask = new LongOperation();
mytask.execute();
I have analyzed more about my query. Finally i got a solution. String that i have loaded with webview was having more single quotes so unable to load with webview. So instead of calling Success failure callback, the next line is called.
Now I have replaced the single quotes "\'" by "\'" in my java class. Working fine. :-)
I'm doing a project in GWT to deploy in AppEngine and I'm getting a warning in Eclipse saying:
JavaScript parsing: Expected an identifier in JSNI
reference
Any ideas on what's causing this?
public void callFacebookAPI(String url) {
JsonpRequestBuilder requestBuilder = new JsonpRequestBuilder();
requestBuilder.requestObject(url, new AsyncCallback<FbUser>() {
public void onFailure(Throwable caught) {
System.out.println("FAIL" );
}
#Override
public void onSuccess(FbUser result) {
facebookUser = result;
System.out.println("Facebook name:" + facebookUser.getName());
}
});
}
private final native void doFbLoginFunction() /*-{
FB.login(function(response) {
if (response.authResponse) {
// connected
//return response.session;
var accessToken = response.accessToken;
var url = "http://graph.facebook.com/me?access_token=";
var facebookUrl = url + accessToken;
#com.google.gwt.smartpark.client.map.SmartPark::callFacebookAPI(Ljava/lang/String;Ljava/lang/
String;)(facebookUrl);
} else {
// cancelled
}
});
callFacebookAPI is not static so there must be something before the # in the reference in JSNI, e.g.
var that = this;
$wnd.FB.login($entry(function(response) {
// ...
that.#com.google.gwt.smartpark.client.map.SmartPack::callFacebookAPI(Ljava/lang/String;)(facebookUrl);
// ...
}));
Also, your callFacebookAPI takes a single argument, so the JSNI signature should have a single Ljava/lang/String;.