Call a Java function by String name like in Javascript - javascript

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

Related

how to run a c# method from js inside of the webview app in xamarin

I have a problem with running a c# method from js that is inside of a webview in xamarin.
I have a c# method like this:
public void simpleMethod(int a,string b,bool c){
string[] data;
if(c){
data[a] = b;
}
}
Now how do I call this method from the javasript?
You can create a Custom Renderer of WebView
Create a custom WebView in Forms
public class HybridWebView : View
{
Action<int, string, bool> action;
public static readonly BindableProperty UriProperty = BindableProperty.Create (
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri {
get { return (string)GetValue (UriProperty); }
set { SetValue (UriProperty, value); }
}
public void RegisterAction (Action<int, string, bool> callback)
{
action = callback;
}
public void Cleanup ()
{
action = null;
}
public void InvokeAction (int a,string b,bool c)
{
if (action == null ) {
return;
}
action.Invoke (a,b,c);
}
}
in ContentPage
The HybridWebView instance will be used to display a native web control on each platform. It's Uri property is set to an HTML address , and which will be displayed by the native web control.
The HybridWebViewPage registers the action to be invoked from JavaScript, as shown in the following code example:
public partial class xxxPage : ContentPage
{
public xxxPage ()
{
//...
hybridWebView.RegisterAction ((a,b,c) => simpleMethod(a,b,c));
}
public void simpleMethod(int a,string b,bool c)
{
string[] data;
if(c){
data[a] = b;
}
}
}
<ContentPage.Content>
<local:HybridWebView x:Name="hybridWebView" Uri="https://learn.microsoft.com"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</ContentPage.Content>
in your html file
For example you want to call the method when click a button
<button type="button" onclick="javascript:invokeCSCode(a,b,c);">Invoke C# Code</button>
//...
<script type="text/javascript">
function invokeCSCode(a,b,c) {
try {
invokeCSharpAction(a,b,c);
}
catch (err){
log(err);
}
}
Creating the Custom Renderer on iOS
[assembly: ExportRenderer (typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.iOS
{
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>, IWKScriptMessageHandler
{
const string JavaScriptFunction = "function invokeCSharpAction(a,b,c){window.webkit.messageHandlers.invokeAction.postMessage(a,b,c);}";
WKUserContentController userController;
protected override void OnElementChanged (ElementChangedEventArgs<HybridWebView> e)
{
base.OnElementChanged (e);
if (e.OldElement != null) {
userController.RemoveAllUserScripts ();
userController.RemoveScriptMessageHandler ("invokeAction");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup ();
}
if (e.NewElement != null) {
if (Control == null) {
userController = new WKUserContentController ();
var script = new WKUserScript (new NSString (JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript (script);
userController.AddScriptMessageHandler (this, "invokeAction");
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView (Frame, config);
SetNativeControl (webView);
}
Control.LoadRequest (new NSUrlRequest (new NSUrl (Element.Uri, false)));
}
}
public void DidReceiveScriptMessage (WKUserContentController userContentController, WKScriptMessage message)
{
Element.InvokeAction (message.Body.ToString ());
}
}
}
In addition, Info.plist must be updated to include the following values:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Creating the Custom Renderer on Android
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace CustomRenderer.Droid
{
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, Android.Webkit.WebView>
{
const string JavascriptFunction = "function invokeCSharpAction(a,b,c){jsBridge.invokeAction(a,b,c);}";
Context _context;
public HybridWebViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
Control.RemoveJavascriptInterface("jsBridge");
var hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
if (Control == null)
{
var webView = new Android.Webkit.WebView(_context);
webView.Settings.JavaScriptEnabled = true;
webView.SetWebViewClient(new JavascriptWebViewClient($"javascript: {JavascriptFunction}"));
SetNativeControl(webView);
}
Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
Control.LoadUrl(Element.Uri);
}
}
}
}
public class JavascriptWebViewClient : WebViewClient
{
string _javascript;
public JavascriptWebViewClient(string javascript)
{
_javascript = javascript;
}
public override void OnPageFinished(WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript(_javascript, null);
}
}
public class JSBridge : Java.Lang.Object
{
readonly WeakReference<HybridWebViewRenderer> hybridWebViewRenderer;
public JSBridge (HybridWebViewRenderer hybridRenderer)
{
hybridWebViewRenderer = new WeakReference <HybridWebViewRenderer> (hybridRenderer);
}
[JavascriptInterface]
[Export ("invokeAction")]
public void InvokeAction (int a,string b,bool c)
{
HybridWebViewRenderer hybridRenderer;
if (hybridWebViewRenderer != null && hybridWebViewRenderer.TryGetTarget (out hybridRenderer))
{
hybridRenderer.Element.InvokeAction (a,b,c);
}
}
}
You don't.
Basically you have to detect / trigger a piece of javascript code, detect it with your C# webview configuration/initialization code, and then execute your C#.
Take a look at https://developer.android.com/reference/android/webkit/JavascriptInterface for android to understand what I'm saying, and implement it on Android.
For iOS you have a WKWebview listener that you can use to receive informations from your webpage.

Is it possible to return a string from C# to Javascript using Xamarin.iOS?

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#.

Access Java Enum fields in javascript

I'm trying to access the facility String of this enum in java script
public enum FacilityEnum {
CAR_VALET("carValet"),
INDOOR("indoorPark"),
DISABLED_ACCESS("disabledAccess"),
EV_CHARGE("evCharge"),
private String facility;
private FacilityEnum(String facility) {
this.facility = facility;
}
public String getFacility() {
return facility;
}
public void setFacility(String facility) {
this.facility = facility;
}
}
This enum is used in a Facility.class
#Entity
public class Facility {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long facilityId;
#Enumerated(EnumType.STRING)
private FacilityEnum service;
#ManyToMany(mappedBy = "facilities")
#JsonBackReference("parks-facilities-services")
private Set<Park> parks;
}
public FacilityEnum getService() {
return service;
}
public void setService(FacilityEnum service) {
this.service = service;
}
which has a ManyToMany relation with Park.class.
The problem comes when i need to use the facility String in javascript
This the javascript interested part, i'm using Spring + Thymleaf
var parcheggi = JSON.parse([[${parks}]]); //my list of Parks
parcheggi.forEach(function (arrayItem) { //it's ok
var parcheggio = arrayItem;
var services = parcheggio.facilities; //it's ok, i get Facility objects
var servicesDiv = '<div>';
services.forEach(function (service){
var s = service; //the single Facility
servicesDiv += '<img src="/images/park_icons/facilities/' + s.service + '.png" />'
});
servicesDiv += '</div>';
//rest of the code...
In this case s.service is the rough Enum (CAR_VALET, INDOOR...) if i try s.service.facility I get undefined.. I need to have carValet, indoor, disabledAccess and so on...
One way to do what you want is to configure object mapper to serialize enums using toString method. You would add the following to the object mapper configuration:
objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
(Note that previous Jackson versions have equivalent to this property but it's different).
Then just add toString to your enum:
#Override
public String toString ()
{
return facility;
}
You are getting undefined because your Enum can't be deserialized in JSON, you have two options here:
Either change the Implementation of your Enum, so it contains only the Stringconstants and it will be correctly mapped by Jackson.
Your code would be:
public enum FacilityEnum {
CAR_VALET,
INDOOR,
DISABLED_ACCESS,
EV_CHARGE;
}
Or you should override the toString() method in your Enum so it can
be deserialized and returned as a String.
Your code would be:
public enum FacilityEnum {
CAR_VALET("carValet"),
INDOOR("indoorPark"),
DISABLED_ACCESS("disabledAccess"),
EV_CHARGE("evCharge"),
private String facility;
private FacilityEnum(String facility) {
this.facility = facility;
}
public String getFacility() {
return facility;
}
public void setFacility(String facility) {
this.facility = facility;
}
#Override
public String toString() {
return facility;
}
}

How to use Android webview to run javascript that depends on another library/module

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

How do I assign a JSONObject or JavaScriptObject to $wnd[x] in GWT?

I read some var from my HTML-Page using GWT Dictionary. The var looks like this:
var test = {
"a" : "123",
"b" : "jg34l",
...
}
Now I get via AJAX-Call new content for my JS var. At the moment I overwrite it like this:
public native void set(String key, String value) /*-{
$wnd["test"][key] = value;
}-*/;
public void onResponseReceived(Request request, Response response) {
JSONObject obj = (JSONObject) JSONParser.parseLenient(response.getText());
for (String key : obj.keySet()) {
JSONString val = (JSONString) obj.get(key);
set(key, val.stringValue());
}
}
As you can see I get a JSON-String. Parse it. Cast it to JSONObject. Take every key-value-pair and use the JSNI-method to set the pairs.
There must be an easier way to do this?! I want simply to say: $wnd["test"] = myJsonObject
Please help me, 'cause it is performance-critical step (up to 1000 key-value-pairs).
You can try using JSO (should be also much faster than JSONObject)
Something like this:
public native void set(JavaScriptObject data) /*-{
$wnd['test'] = data;
}-*/;
public void onResponseReceived(Request request, Response response) {
JavascriptObject data = JsonUtils.safeEval(response.getText()).case();
set(data);
}
I haven't tried myself, so I can't say 100% if it would work.
Update:
Instead of a Dictionary you can directly return a JavaScriptObject that supports hash like access:
public class MyDictionary extends JavaScriptObject {
public native String get(String name) /*-{
return this[name];
}-*/;
}
public native MyDictionary getData() /*-{
return $wnd['test'];
}-*/
Here's my version for GWT Dictionary (true dynamic language switch):
public native void setTranslation(String locale, JavaScriptObject translationData) /*-{
$wnd[locale] = translationData;
}-*/
#Inject
TranslationProvider translationProvider;
public void onResponseReceived(Request request, Response response) {
String newLocale = "en";
JavaScriptObject translationData = JsonUtils.unsafeEval(response);
setTranslation(newLocale, translationData);
translationProvider.setLocale(newLocale);
LocaleChangeEvent.fire(eventBus);
}
Every Widget that displays translatable text registers to LocaleChangeEvent and implements LocaleChangeEventHandler:
public interface LocaleChangeEventHandler extends EventHandler {
void onLocaleChange(LocaleChangeEvent event);
}
In the onLocaleChange method it simply calls the get(...) method from the TranslationProvider class to receive its String:
public class LocaleAwareLabel extends Label implements LocaleChangeEventHandler {
TranslationProvider translationProvider;
#Inject
public LocaleAwareLabel(TranslationProvider translationProvider, EventBus eventBus) {
this.translationProvider = translationProvider;
eventBus.addHandler(LocaleChangeEvent.TYPE, this);
get();
}
public void get() {
this.setText(translationProvider.get("myDictionaryKey"));
}
#Override
public void onLocaleChange(LocaleChangeEvent event) {
get();
}
}
And here the TranslationProvider class:
#Singleton
public class TranslationProvider {
Dictionary dictionary;
public void setActiveLocale(String locale) {
dictionary = Dictionary.getDictionary(locale);
}
public String get(String key) {
dictionary.get(key);
}
}
That's it!

Categories

Resources