how to solve window.close() not work in Android webview c# - javascript

i just new using this visual studio c# android...
have system develop in vb.net... working fine in website... problem when wanted using mobile app... used this vs c# xamarin android....
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
WebView localWebView = FindViewById<WebView>(Resource.Id.LocalWebView);
localWebView.SetWebViewClient(new WebViewClient()); // stops request going to Web Browser
localWebView.Settings.JavaScriptEnabled = true;
localWebView.Settings.JavaScriptCanOpenWindowsAutomatically = true;
localWebView.SetWebChromeClient(new WebChromeClient());
localWebView.LoadUrl("http://www.facebook.com");
}
success run on this webview.... but have form have window.open....problem is how to window.close after used javascript to opened it.. means going back to my previous window.. and pass some information.... like: window.opener.document.getElementById(StrCtrlName2).value = MemberCode;
find and want to try this code... but look different for c#... how to convert it in visual studio c# because have error??... help me.. where to pun also i'm not sure... just learn c#
WebChromeClient webClient = new WebChromeClient(){
public void onCloseWindow(Window w){
super.onCloseWindow(w);
Log.d(TAG, "Window close");
}
};
thanks...

Welcome #haris!
The code you found is java. It's different from c# but also have a lot similarities.
If you want this particular java snippet in c# you would have do write something like this:
using Android.Webkit;
public class CustomWebChromeClient : WebChromeClient {
public override void OnCloseWindow(Android.Webkit.WebView window)
{
base.OnCloseWindow(window);
//Your favorite logging library call.
}
}
A quick explanation (in case you are interested).
In java snippet a ref to anonymous class which extends WebChromeClient is created and then we extend base onCloseWindow method by adding addition logging.
In c# the same can not be done so what I did, just created a named class CustomWebChromeClient which extends WebChromeClient and overrides OnCloseWindow
For more info refer to official Xamarin docs.

Related

How to call existing javascript function from android activity

I'm building an ebook reader, so I have a webview that is loading a page stored locally on the device which it retrieved from the ebook itself. On this page, it has a javascript function controls.nextPage() that loads and runs just fine; it's used to not actually navigate to new web pages, but instead redraw virtual pages using javascript. I have this function bound to a button on the web page itself so that I can manually click it to test, again, works just fine when I touch the button on my webview.
Now I am trying to trigger this exact function from within my app. Ideally, I want to do this from a gesture swipe but that is too complicated for this specific question, as I have other issues with the gestures I need to solve first. For now, I've set up a button in my navigation drawer to trigger it and test it:
NavigationView navigationViewRight = (NavigationView) findViewById(R.id.nav_view_webview_right);
navigationViewRight.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camera) {
// *** - Focus here - *** //
runOnUiThread(new Runnable() {
#Override
public void run() {
mWebview.evaluateJavascript("controls.pageNext()", null);
}
});
} else if (id == R.id.nav_share) {
}
drawer.closeDrawer(GravityCompat.END);
return true;
}
}
);
Note, I've also tried calling window.controls.pageNext() to no avail.
So my page is loaded, and I've hit my in-page button to test the function; works. I go to hit my navigation drawer button in the app? Error (when using window.controls.pageNext():
[INFO:CONSOLE(1)] "Uncaught TypeError: Cannot read property 'pageNext' of undefined", source: (1)
So it seems to be that evaluateJavascript() is being run in a fresh environment/thread. How can I tell it not to?
To get around this,I've tried to create an empty javascript interface in the hopes that I could simply initialize my page javascript into it and thus be able to call it from Android.
mWebview.addJavascriptInterface(new TestInterface(), "TestInterface");
public class TestInterface {
#JavascriptInterface
public void test() {
runOnUiThread(new Runnable() {
#Override
public void run() {
mWebview.evaluateJavascript("console.log('test')", null);
}
});
}
}
From my webapp, the javascript can call the interface just fine. calling TestInterface.test(); yields:
[INFO:CONSOLE(1)] "test", source: (1)
But when I tried to assign a new function to that interface from my webapp:
TestInterface.testTwo = function() {
console.log('testTwo');
};
TestInterface.testTwo();
Android wouldn't have it:
[INFO:CONSOLE(674)] "testTwo", source: http://127.0.0.1:8080/js/main.js (674)
What's weird is that it's not really giving me much info to go on. I do know that the rest of my page has issues loading after the testTwo() attempt that test() did not, so I'm assuming failure to load the rest of the script.
Lastly, out of curiousity, I changed my navigation drawer button to try and run the new function like this:
mWebview.evaluateJavascript("TestInterface.testTwo()", null);
Log:
[INFO:CONSOLE(1)] "Uncaught TypeError: TestInterface.testTwo is not a function", source: (1)
Yes but is it something else? I dunno. Thoughts? Thank you.
So I figured out in the end what my issue was. By running mWebview.evaluateJavascript("window.location", null); I realized that I in fact was not actually on the page I thought I was. My ebook page was being loaded into an iframe or some other type of skeleton/wrapper in such a way that the webapp functions were not in fact available when running evaluateJavascript().
So once figured that out, I can confirm some things that I originally questioned above:
So it seems to be that evaluateJavascript() is being run in a fresh
environment/thread. How can I tell it not to?
It does not. Just make sure you know what page is actually loaded.
To get around this,I've tried to create an empty javascript interface in the hopes that I could simply initialize my page javascript into it and thus be able to call it from Android.
This does in fact work. I'm not sure what my mistake was before, but if I create a javascript interface in Android, it's functions are available to the webapp AND I can in fact write new objects to the interface object from within the webapp. So, initializing new objects into TestInterface from within the webapp can be run within the Android app via:
mWebview.evaluateJavascript("TestInterface.someNewFunctionFromWebapp()", null);
I can NOT however overwrite any existing objects/properties of that javascript interface object. so TestInterface.test() is immutable.

Android Javascript Engine; Need to replace WebView with Rhino, J2V8, etc

We are trying to replace our webview and html with a layout file and a javascript engine of some sort. There is a TON of javascript that must be called and we have a rather large JavaScriptInterface that will need to be accessable by the JS engine. I have been trying out Rhino and J2V8 but cannot figure out a way to give javascript access to a full class of methods or an annotation that works similarly to how you annotate methods for WebView.
If anyone has any insight, it would be much appreciated!
Thanks,
Jon
AFAIK there is no "out-of-the-box" solution for this for JSV8.
But have a look at following example:
public class V8ConsoleBridge implements JavaVoidCallback {
#Override
public void invoke(V8Object receiver, V8Array parameters) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parameters.length(); ++i) {
if (i > 0) {
sb.append(", ");
}
Object object = parameters.get(i);
if (object instanceof V8Object) {
V8Object v8Object = (V8Object) object;
sb.append(v8Object.toString());
v8Object.release();
} else {
sb.append(object);
}
}
Log.i("goebl-v8", sb.toString());
}
public static void register(V8 v8) {
V8ConsoleBridge console = new V8ConsoleBridge();
V8Object v8Console = new V8Object(v8);
v8.add("console", v8Console);
v8Console.registerJavaMethod(console, "debug");
v8Console.registerJavaMethod(console, "log");
v8Console.registerJavaMethod(console, "info");
v8Console.registerJavaMethod(console, "warn");
v8Console.registerJavaMethod(console, "error");
v8Console.release();
}
}
This is a hardcoded bridge for a JS console object to access Android logging system.
You could write generic code to
scan a class you want to expose in JavaScript, much like JavaScript-Interface for WebView, even with annotations like #JavascriptInterface to only include certain members
write generic code for the invoke which actually invokes members of the receiver class by using Java reflection.
Of course it would be great if J2V8 had this useful code, because it might be used by many projects. When you have a solid solution, create a pull request so I can use it too :-)
If you don't mind wading deep in source code, you might find it useful to check out NativeScript. They provide a generic way to access all Java classes known at compile time in JavaScript, which is internally done via reflection. Well, I've heard that they do it this way, I actually didn't read the source code of NativeScript. In NativeScript, you don't have to create bridges, it's done magically by the build- and runtime-system. Maybe the source inspires you to port the ideas to J2V8.

Override current Javascript functions with Android Webview

I'm porting my website to Android, and using an Webview to show the content to user. There are a lot of Javascript functions in my website, and I want to intercept them. I already seen a "solution" here.
However, I think there should be a more proper way, using Javascript Interface:
this.webView.addJavascriptInterface(this.webJavascriptInterface, "Android");
This way, I have to modify my website to call both myFunction() and Android.myFunction(). I tried to leave a blank String in the interface:
this.webView.addJavascriptInterface(this.webJavascriptInterface, "");
but the result was as I guess, it couldn't work. Is there a way to override current Javascript functions in Webview?
// android
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
webView.loadUrl("javascript:function inputClick(val){native.abcd(val);}"); // override js function
}
});
webView.loadUrl("file:///android_asset/test.html");
webView.addJavascriptInterface(new Object() {
#JavascriptInterface
public void abcd(int val) {
Log.e(TAG, "#js abcd" + val);
}
}, "native");
<!-- html -->
<input type="button" onclick="inputClick(2)" value="button">
== add some explain at Sep 07,2015
because javascript function could be override, so you can override javascript function while page finished.
if not, normal implement maybe like this:
// js code
function inputClick(val) {
native.abcd(val); // native and abcd defined in WebView method addJavascriptInterface
}
but this normal implement is seems not work in iOS(Object-C), and must edit HTML page.
is just move the java-javascript bridge code from HTML to Java. (see method onPageFinished in example code)
You can do this by injecting a new javascript that redefines old function, basically you can override existing function with this.
For example, to override window.print() function, I use this
webView.loadUrl("javascript:window.print = function() {Android.printPage();}")

Android utilize V8 without WebView

I'm exercising executing javascript from Java. Rhino works very well for this on desktop, but has to fall back to (slow) interpreted mode on Android (due to dalvik being unable to execute the Java bytecode the Rhino JIT compiles).
Android has its built-in V8 javascript engine which is accessed internally via JNI and ought to give much better performance than Rhino; however, the only way I can find to access it is indirectly through a WebView.
Unfortunately, WebView requires a Context, and crashes with NPE with a null context, so I'm unable to even instantiate a dummy WebView to merely execute the code and return the result. The nature of my exercise doesn't really allow me to provide a Context for WebView, so I'm hoping perhaps there's something I'm overlooking.
Several of these V8Threads run in parallel, so it's not really feasible (as far as I'm aware) to add a WebView to my layout and hide it, as I don't believe a single WebView can execute functions in multiple threads.
private class V8Thread extends Thread
{
private WebView webView;
private String source;
private double pi;
private int i, j;
public V8Thread(int i, int j)
{
pi = 0.0;
this.i = i;
this.j = j;
source = "";
try {
InputStreamReader isReader = new InputStreamReader(assetManager.open("pi.js"));
int blah = isReader.read();
while (blah != -1)
{
source += (char)blah;
blah = isReader.read();
}
webView = new WebView(null);
webView.loadData(source, "text/html", "utf-8");
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(this, "V8Thread");
} catch (IOException e) {
e.printStackTrace();
}
}
public double getResult()
{
return pi;
}
#Override
public void run()
{
webView.loadUrl("javascript:Androidpicalc("+i+","+j+")");
}
}
Ideally there must be some supported way to call V8 directly, or at least execute javascript without requiring an actual WebView, as it seems a rather clunky and convoluted method just to run javascript code.
UPDATE
I've rearranged my code a bit, though unseen here is that now I am instantiating the V8Threads on the AsyncTasks's onPreExecute() while keeping everything else in doInBackground(). The source code is read in earlier in the program, so it's not redundantly re-read for each thread.
Because now the V8Thread is instantiated on the UI Thread, I can pass it the current view's Context (I'm using fragments so I can't just pass it "this"), so it no longer crashes.
private class V8Thread extends Thread
{
private WebView webView;
private double pi;
private int i, j;
public V8Thread(int i, int j)
{
pi = 0.0;
this.i = i;
this.j = j;
source = "";
webView = new WebView(v.getContext());
}
#SuppressWarnings("unused")
public void setResult(String in)
{
Log.d("Pi",in);
}
public double getResult()
{
return pi;
}
#Override
public void run()
{
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(this, "V8Thread");
webView.loadData(source, "text/html", "utf-8");
//webView.loadUrl("javascript:Androidpicalc("+i+","+j+")");
webView.loadUrl("javascript:test()");
Log.d("V8Thread","Here");
}
}
However, when executing, logcat spits out one per thread of the error "Can't get the viewWidth after the first layout" and the javascript code never executes. I know the thread fires completely, because the "Here" log message is sent. Here's the relevant test() function in the js code.
function test()
{
V8Thread.setResult("blah");
}
Working correctly, "blah" should show up four times in logcat, but it never shows up. Could be my source code is read incorrectly, but I doubt that.
Scanner scan = new Scanner(assetManager.open("pi.js"));
while (scan.hasNextLine()) source += scan.nextLine();
The only other thing I can imagine is that due to these aforementioned errors, the webView never actually gets around to executing the javascript.
I'll also add that pi.js contains only javascript, no HTML whatsoever. However, even when I wrap it in just enough HTML for it to qualify as a webpage, still no luck.
You can create a new V8 Context via its API and use that to execute your JavaScript, look into https://android.googlesource.com/platform/external/v8 include directory which contains two C++ header files. Link against the libwebcore.so (compiled from https://android.googlesource.com/platform/external/webkit) library via the NDK, nothing special.
v8::Persistent<v8::Context> context = v8::Persistent<v8::Context>::New(v8::Context::New());
context->Enter();
Refer to https://developers.google.com/v8/get_started which will work on Android. Just make sure the device actually ships with V8 (some older devices ship with JSC [JavaScript Core]).
A bit of a late response but it may be useful to anyone stumbling upon this question. I used the J2V8 library which is a Java wrapper on Google's V8 engine. This library comes with pre-compiled V8 binaries for x86 and armv7l Android devices. It work seamlessly. See here for tutorials. Just keep in mid that since pure V8 is just an Ecmascript engine, there is no DOM element available.
I found this really nifty open source ECMAScript compliant JS Engine completely written in C called duktape
Duktape is an embeddable Javascript engine, with a focus on portability and compact footprint.
You'd still have to go through the ndk-jni business, but it's pretty straight forward. Just include the duktape.c and duktape.h from the distributable source here(If you don't want to go through the build process yourself) into the jni folder, update the Android.mk and all that stuff.
Here's a sample C snippet to get you started.
#include "duktape.h"
JNIEXPORT jstring JNICALL
Java_com_ndktest_MainActivity_evalJS
(JNIEnv * env, jobject obj, jstring input){
duk_context *ctx = duk_create_heap_default();
const char *nativeString = (*env)->GetStringUTFChars(env, input, 0);
duk_push_string(ctx, nativeString);
duk_eval(ctx);
(*env)->ReleaseStringUTFChars(env, input, nativeString);
jstring result = (*env)->NewStringUTF(env, duk_to_string(ctx, -1));
duk_destroy_heap(ctx);
return result;
}
Good luck!
Can you get a hold of the Context that is your Application? There are a couple ways to do this.
Call getApplication() from your Activity (Application is a child of Context)
Call getApplicationContent() from a Context (Context likely being your Activity)
UPDATE
According to this Android documentation, your bound Javascript code will run in a separate process anyways, so there should be no need to set it up in its own Thread.
From the link:
Note: The object that is bound to your JavaScript runs in another thread and not in the thread in which it was constructed. (The 'object' being referred to is the JavascriptInterface class)
You can use the AndroidJSCore project. It is not based on V8, but JavaScriptCore. The current version (2.2+) supports JIT compilation on all processors not named MIPS.
UPDATE 2018: AndroidJSCore has been superseded by LiquidCore, which is, in fact, based on V8. Not only does it include the V8 engine, but all of Node.js is available as well.

passing parameters to running silverlight application

The scenario is I have a list of items in HTML; when I click on an item I use JS to dynamically create the HTML to load a silverlight app passing in the specific item # (using initParams); and my silverlight app visualizes this in a nice way. I do this on the same page rather than loading a new webpage, and the transition is smooth.
I know it is possible to have silverlight call a JS function on my page (opposite to what I need). I'm thinking it is also possible for my JS function to raise an event/call a method in silverlight, but not exactly sure how - has anyone tried this? While a workaround would be to recreate the silverlight app each time, just raising an event in existing, loaded SL app would would be the perfect solution to my problem.
regards
ewart.
You can call a method in your Silverlight application from JavaScript.
See this blog post
You just need to create a class in your silverlight app that registers itself as callable from JS:
[ScriptableType]
public partial class SomeClass
{
private bool mouseHeldDown = false;
private Point moveMeOffset = new Point();
public SomeClass()
{
HtmlPage.RegisterScriptableObject("SilverlightObject", this);
}
[ScriptableMember]
public void DoThing(int x)
{
//do some stuff
}
}
Then you can call this from JS
document.getElementById("mySilverlightControl").content.SilverlightObject.DoThing(5);

Categories

Resources