JavascriptInterface in Android's WebView: multiple calls to JS cause deadlock - javascript

This is the entire Java code I've used. I will explain in more detail below...
public class Test7 extends Activity {
//debug
private final static String TAG = "JSInterface";
private WebView wv;
private class JSInterface {
private WebView wv;
// Variables to manage interfacing with JS
private String returnValue;
private boolean canReadReturnValue;
private Lock lockOnJS;
private Condition condVarOnJS;
public JSInterface (WebView wv) {
this.wv = wv;
this.canReadReturnValue = false;
this.lockOnJS = new ReentrantLock();
this.condVarOnJS = lockOnJS.newCondition();
}
public void setReturnValue(String ret) {
lockOnJS.lock();
returnValue = ret;
canReadReturnValue = true;
condVarOnJS.signal();
lockOnJS.unlock();
Log.d(TAG, "returnValue = " + returnValue);
}
public String getReturnValue() {
Log.d(TAG, "enter in getReturnValue");
lockOnJS.lock();
while (!canReadReturnValue) {
try {
Log.d(TAG, "get wait...");
condVarOnJS.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lockOnJS.unlock();
Log.d(TAG, "returnValue: " + returnValue);
return returnValue;
}
public String getNewString() {
wv.loadUrl("javascript:JSInterface.setReturnValue(createNewString())");
return getReturnValue();
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
wv = (WebView) findViewById(R.id.webView1);
wv.getSettings().setJavaScriptEnabled(true);
wv.addJavascriptInterface(new JSInterface(wv), "JSInterface");
wv.loadUrl("file:///android_asset/prova7.html");
}
public void button1(View v) {
wv.loadUrl("javascript:func('1')");
}
}
And it seems work fine.
You can see that I've got a button (that we can call button1), and clicking on it, it tries to execute a JS method, called func().
public void button1(View v) {
wv.loadUrl("javascript:func('1')");
}
Inside this JS method, I have to call another Java method. This is the code:
function func(id) {
document.getElementById(id).innerHTML = JSInterface.getNewString();
}
I need to return the result of JSInterface.getNewString() to the innerHTML variable.
The code of JSInterface.getNewString() is this:
public String getNewString() {
wv.loadUrl("javascript:JSInterface.setReturnValue(createNewString())");
return getReturnValue();
}
You can see that I use the method setReturnValue and getReturnValue to return the value returned by another JS method. This is the code:
function createNewString() {
return "my New String";
}
The problem is that when I try to set the returnValue, the function createNewString is never executed! If I add a console.log() line, my logCat display nothing!
I cannot understand why this happens.

All the javascript and your JSInterface methods called from javascript are running on the single thread in Android WebView. So while you are waiting in condVarOnJS.await() no javascript can be executed, just because it is executed on the same thread.
Moreover, all the webview instances in your application share the same javascript thread.

In Internet Explorer I found the same problem. You can use setTimeout like this:
function func(id) {
setTimeout(
function(){
document.getElementById(id).innerHTML = JSInterface.getNewString();
},
500);
}

I did the functionality what you intend in that code,for me createNewString() is called,
I will show up the code i used,
In java
,
public String videoPlay(){
System.out.println("videoPlay");
mWebView.loadUrl("javascript:window.demo.setReturnValue(createNewString())");
return getReturnValue();}
public void setReturnValue(String test){
rotValue=test;
System.out.println(test);
}
public String getReturnValue(){
System.out.println("get"+rotValue);
return rotValue;
}
in HTML,
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script>
function inform(){
alert('et');
document.getElementById('myText').value=window.demo.videoPlay();
alert('et');
}
function createNewString() {
return "my New String";
}
</script>
</head>
<body onload="init()">
<form>
<input type='text' id='myText' />
<input type="button" name="test" value="Click me" onclick="inform()">
</form>
</body>
</html>
The function getter and setter called and values also set, but i have the output log as..
11-08 19:18:17.155: INFO/System.out(847): videoPlay
11-08 19:18:17.165: INFO/System.out(847): getnull
11-08 19:18:17.875: INFO/System.out(847): my New String
videoPlay called from JS and createnewString() also called from java to JS , but it returns the value before it set , because i don`t what is the purpose to use lock , even i tried using lock as you did for that it will print
11-08 19:18:17.155: INFO/System.out(847): videoPlay
11-08 19:18:17.165: INFO/System.out(847): getnull
using lock also the function callback works in wrong manner, you need work on locks.

Related

Webview - How to trigger Java Function from Javascript Button

So I got a problem, where there is a button that will call a webview function
The button is the one with id play in the index.html that will call the javascript playVideo function in src.js, where the playVideo function will notify the webview that the button has been pressed to check the condition in the Java Function.
How do I achieve this?
The codes are below to help getting context
index.html
<!DOCTYPE html>
<html>
<head>
<title> Video</title>
<script type="text/javascript" src="src.js"></script>
</head>
<body bgcolor="#333">
<center>
<video preload="auto" width="480" height="270"
#Embed-video
</video>
<br>
<br>
<div>
<a class="first" href="#" id="play">Play</a>
<a class="second" href="#" id="pause">Pause</a>
</div>
</center>
Javascript code
window.onload = function(){
var video = document.getElementById('my-video');
var play = document.getElementById('play');
var pause = document.getElementById('pause');
// associate functions with the 'onclick' events
play.onclick = playVideo;
pause.onclick = pauseVideo;
function playVideo(e) {
//call java function
}
function pauseVideo(e) {
}
}
Java Function
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(x,y) {
if (js.playVideo()) {
//do something
return true;
}else{
return false;
}
}
});
You should use annotation for Javascript like the following example
// Java File example
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, "show toast from web: " + toast, Toast.LENGTH_SHORT).show();
}
// Js file example
function showAndroidToast(msg) {
Android.showToast(msg);
}
Thus, you should try like the following code.
function playVideo(e) {
//call java function
Android.doSomething();
}
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(x,y) {
// implment your code with doSomething func here
if (doSomething()) {
//do something
return true;
}else{
return false;
}
}
});
#JavascriptInterface
public boolean doSomething() {
// do something
if(isPlaying) return true;
return false;
}

(Apache Wicket) Set java atrribute from a js function

I am brand new on Apache Wicket and I need to set value on a Java attribute. This value comes from a var on JS filled by a specific function from a specific GIS lib (https://leaflet.github.io/Leaflet.draw/docs/leaflet-draw-latest.html). This setting must be triggered by some component behavior.
Here is a simplified example code:
Wicket web page:
public class MapPage extends WebPage {
private static final long serialVersionUID = 1L;
private Integer coordinates;
// getters and setters
}
Wicket html:
<html xmlns:wicket="http://wicket.apache.org">
<head>
<!-- metas, scripts, and css imports -->
</head>
<body>
<script>
// component declarations
var coordinates = ''
map.on('draw:edited', function (e) {
e.layers.eachLayer(function(layer) {
coordinates = toWKT(layer);
// send coordinates to coordinates java attribute ??? how??
});
});
</script>
</body>
Thanks a lot!
This is a piece of code from one of my projects, where I want to handle a click on a (HighCharts) chart. It passes data to Wicket and Wicket then updates another panel to display details related to the click.
The relevant javascript part, where interactionurl is actually the callbackScript that is generated by the behavior later on:
interactionurl(JSON.stringify(myDataToPass));
The behaviour:
this.add( this.interactionbehavior = new AbstractDefaultAjaxBehavior()
{
#Override
protected void respond( final AjaxRequestTarget target )
{
RequestCycle cycle = RequestCycle.get();
WebRequest webRequest = (WebRequest) cycle.getRequest();
String param1 = webRequest.getQueryParameters().getParameterValue( "mydict" ).toString( "" );
//param1 contains the JSON map passed from javascript.
//you can also do stuff now, like replacing components using ajax
}
#Override
protected void updateAjaxAttributes( AjaxRequestAttributes attributes )
{
super.updateAjaxAttributes( attributes );
attributes.getExtraParameters().put( "mydict", "__PLACEHOLDER__" );
}
#Override
public CharSequence getCallbackScript()
{
String script = super.getCallbackScript().toString().replace( "\"__PLACEHOLDER__\"", "data" );
return script;
}
} );
You only need to pass the interaction url to the page on some moment. For this you can use renderHead in the component that has the behaviour:
#Override
public void renderHead( final IHeaderResponse response )
{
...
//use the `setupCallback` to store the callback script somewhere.., I store it in 'interactionurl'
String script = String.format( " setupCallback(this.interactionbehavior.getCallbackScript()); ");
response.render( OnDomReadyHeaderItem.forScript( script )
}

How to call JavaScript in Android 4.2

I have already found many examples about how to call JavaScript from android. But it's not working for me. My target SDK is 17(android 4.2). This is how I am loading my html page from my activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
myWebView = (WebView)findViewById(R.id.mapwebview1);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
JavaScriptHandler jScriptHandler = new JavaScriptHandler(this);
WebChromeClient myWebChromeClient = new WebChromeClient();
myWebView.setWebChromeClient(myWebChromeClient);
myWebView.addJavascriptInterface(jScriptHandler, "MyHandler");
myWebView.loadUrl("file:///android_asset/mywebpage.html");
myWebView.loadUrl("javascript:myFunc()");
}
Here is the code for my JavaScriptHandler:
public class JavaScriptHandler {
//TabFragmentMap mapFragment;
Context context;
//Fragment fragment;
public JavaScriptHandler (Context c){
this.context = c;
}
}
Here is the code for my html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>PhoneGap</title>
</head>
<body onload="myFunc()">
<h1 id="test1">Hello World</h1>
<input type="button" value="Say hello" onClick="moveMyself()" />
<div id="myDiv"></div>
<script type="text/javascript">
function myFunc()
{
document.getElementById('test1').innerHTML = 'Good Morning';
}
</script>
</body>
</html>
Try this:
final WebView webview = (WebView)findViewById(R.id.browser);
/* JavaScript must be enabled if you want it to work, obviously */
webview.getSettings().setJavaScriptEnabled(true);
/* WebViewClient must be set BEFORE calling loadUrl! */
webview.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url)
{
webview.loadUrl("javascript:(function() { " +
"document.getElementsByTagName('body')[0].style.color = 'red'; " +
"})()");
}
});
webview.loadUrl("http://code.google.com/android");
It was actually the same thing that Tamilarasi has given me. If somebody wants to call an existing JavaScript function from the html, do the following:
myWebView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url){
myWebView.loadUrl("javascript:myFunc()");
}
});
myWebView.loadUrl("file:///android_asset/myHtml.html");
Try this links i hope this will be help to u:
http://android-er.blogspot.in/2011/10/call-javascript-inside-webview-from.html
Android 4.2.1, WebView and javascript interface breaks
Javascript interface not working with android 4.2

using javascript in android webview

I'm trying to start an activity from a javascript interface in my webview.
The example shows a toast. How could i call a class instead of a toast?
public class JavaScriptInterface {
Context mContext;
/** Instantiate the interface and set the context */
JavaScriptInterface(Context c) {
mContext = c;
}
/** Show a toast from the web page */
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}
this for the html page.
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
<script type="text/javascript">
function showAndroidToast(toast) {
Android.showToast(toast);
}
You have to first register the JavaScriptInterface on your webview.
JavaScriptInterFace can be a inner class as shown below. This class will have a function that you can call from html page( via javaScript ) and inside this function you can write code to change activity.
Here is the working solution for you:
public class JavascriptInterfaceActivity extends Activity {
/** Called when the activity is first created. */
WebView wv;
JavaScriptInterface JSInterface;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
wv = (WebView)findViewById(R.id.webView1);
wv.getSettings().setJavaScriptEnabled(true);
// register class containing methods to be exposed to JavaScript
JSInterface = new JavaScriptInterface(this);
wv.addJavascriptInterface(JSInterface, "JSInterface");
wv.loadUrl("file:///android_asset/myPage.html");
}
public class JavaScriptInterface {
Context mContext;
/** Instantiate the interface and set the context */
JavaScriptInterface(Context c) {
mContext = c;
}
#android.webkit.JavascriptInterface
public void changeActivity()
{
Intent i = new Intent(JavascriptInterfaceActivity.this, nextActivity.class);
startActivity(i);
finish();
}
}
}
Here is the html page
<html>
<head>
<script type="text/javascript">
function displaymessage()
{
JSInterface.changeActivity();
}
</script>
</head>
<body>
<form>
<input type="button" value="Click me!" onclick="displaymessage()" />
</form>
</body>
</html>
Hope this helps...
You also need to add the #android.webkit.JavascriptInterface annotation on top of your changeActivity method in your android code, should you run on Android 4.2 or higher.
See this link for more.

How to call and pass arguments to a JavaScript method in a page hosted by the .NET WebBrowser control in C#? [duplicate]

This question already has answers here:
Closed 10 years ago.
I want to call a JavaScript function through C#, using a WinForm's WebBrowser control. I've attempted to search however could not find anything which answers my question, only solutions which involved ASP.NET.
Thank you in advance.
Edit:
This is the only question regarding this that I've found that actually has an answer that demonstrates how to call a JavaScript method with parameters, and also shows how to call a .NET function from JavaScript in the WebBrowser control.
I do not think this question should be marked as a duplicate as it adds good value. It's the first hit on a google search for "c# webbrowser call javascript function with parameters".
This is a nice example, that I found here:
http://www.codeproject.com/Tips/127356/Calling-JavaScript-function-from-WinForms-and-vice
HTML/JavaScript
<html>
<head>
<script type="text/javascript">
function ShowMessage(message) {
alert(message);
}
function ShowWinFormsMessage() {
var msg = document.getElementById('txtMessage').value;
return window.external.ShowMessage(msg);
}
</script>
</head>
<body>
<input type="text" id="txtMessage" />
<input type="button" value="Show Message" onclick="ShowWinFormsMessage()" />
</body>
</html>
C#
public partial class frmMain : Form {
public frmMain() {
InitializeComponent();
webBrowser1.ObjectForScripting = new ScriptManager(this);
}
private void btnShowMessage_Click(object sender, EventArgs e) {
object[] o = new object[1];
o[0]=txtMessage.Text;
object result = this.webBrowser1.Document.InvokeScript("ShowMessage", o);
}
private void frmMain_Load(object sender, EventArgs e) {
this.webBrowser1.Navigate(#"E:\Projects\2010\WebBrowserJavaScriptExample\WebBrowserJavaScriptExample\TestPage.htm");
}
[ComVisible(true)]
public class ScriptManager {
frmMain _form;
public ScriptManager(frmMain form) {
_form = form;
}
public void ShowMessage(object obj) {
MessageBox.Show(obj.ToString());
}
}
}

Categories