Pass variable from plugin back to cordova - javascript

The Cordova plugin I'm using plays a VR video file, and is called via GoogleVRPlayer.playVideo(videoUrl, fallbackVideoUrl).
Somewhere in the .java files of the plugin, there's:
#Override
public void onLoadError(String errorMessage) {
// I want to know if this function is executed
Log.e(TAG, "Error loading video: " + errorMessage);
}
Basically, when the video fails to load, I want to set a variable in my javascript in cordova to "error", for example var video_status = "error", so I can use this information later on in my app.
I've found some answers that would possibly solve my problem, but I can't seem to integrate it the right way. I have very little experience with native plugins and Java.
Anyone who can help me with this?

Since the Cordova plugin GoogleVRPlayer launches a new Activity (VrVideoActivity), I would use a Singleton class as an interstitial data bridge between the two Activites to hold the error message.
This is because, on launching the video player activity, your app (the Cordova activity) is paused in the background and will only resume execution once the video player activity is closed.
By using the intermediate class, both the Cordova plugin and the video activity are able to share data.
So I would do something like this:
Create a new file called CordovaBridge.java in cordova-vr-player/src/android/java/neotrino/
package com.neotrino;
public class CordovaBridge {
private String errorMsg = null;
public String getErrorMsg() {return errorMsg;}
public void setErrorMsg(String errorMsg) {this.errorMsg = errorMsg;}
private static final CordovaBridge holder = new CordovaBridge();
public static CordovaBridge getInstance() {return holder;}
}
Modify GoogleVRPlayer.java as follows:
import com.neotrino.CordovaBridge;
#Override
public void onResume(boolean multitasking) {
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
String errorMsg = CordovaBridge.getInstance().getErrorMsg();
if(errorMsg != null){
webView.loadUrl("javascript:window.video_status = '"+errorMsg+"'");
}
}
});
}
Modify VrVideoActivity.java as follows:
import com.neotrino.CordovaBridge;
#Override
public void onLoadError(String errorMessage) {
// I want to know if this function is executed
Log.e(TAG, "Error loading video: " + errorMessage);
CordovaBridge.getInstance().setErrorMsg(errorMessage);
}
I haven't tested the above code, but in terms of an approach it should give you an approximation on which to base your solution.

Related

Executing JavaScript instructions after loading HTML file in a WebView

I realized an Android program that loads a HTML file into a WebView, the HTML file loads Leaflet.js that shows different styles of maps based on the provider you gave it.
I load the page in the OnCreate() method with this instruction:
myWebView.loadUrl("file:///android_asset/test.html");
After that, I need to take the list of different maps from a Handler (with class name JavascriptInterface.java) that I added to the WebView like this:
myWebView.addJavascriptInterface(new JavaScriptInterface(this, myWebView), "MyHandler");
The final code needs to look like this:
myWebView.loadUrl("file:///android_asset/test.html");
myWebView.loadUrl("javascript:window.MyHandler.getMaps()");
The problem is that the first loadUrl() isn't fast enough, so the second loadUrl goes before the map is initialized, causing a lot of problems.
The temporary, and also horrible, solution that I found is to hold the second loadUrl for 1 second before executing it, like this:
myWebView.loadUrl("file:///android_asset/test.html");
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
// Actions to do after 1 second
myWebView.loadUrl("javascript:window.MyHandler.getMaps()");
}
}, 1000);
There is a better way of executing an instruction after another one? I already tried using an ASyncTask, but there is the possibility that I didn't implemented it well.
Please call myWebView.loadUrl("javascript:window.MyHandler.getMaps()"); inside Webview onPageFinished event.
myWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.d("WebView", "onPageStarted " + url);
}
#Override
public void onPageFinished(WebView view, String url) {
myWebView.loadUrl("javascript:window.MyHandler.getMaps()");
}
});
myWebView.loadUrl("file:///android_asset/test.html");

Android WebView call JavaScript function

I using WebView to show this website
http://app.wlojii.com
I want to make control panel in notification bar to play/stop music.
The problem i cant find the function that play/stop the music.
I inspected the button element and tried some variants of functions unsuccessfully (play, play(), player.play()), and i used this method to call the functions:
mWebView.loadUrl("javascript: ...")
I am not so familiar with JavaScript and web, i need some help.
Pavel
I have added a git repo with the working code https://github.com/premithk/webviewclickeventexample
The problem last time was that getElementsByClassName returns an array. So i have changed the code to
mWebview.loadUrl("javascript(function({l=document.getElementsByClassName('mejs-playpause-button');
e=document.createEvent('HTMLEvents');
e.initEvent('click',true,true);
l[0].dispatchEvent(e);})()");
Any ways the code in repo works such that there is a button (native) that will act as play/pause
Register a Javascriptinterface or use this library.
https://github.com/lzyzsd/JsBridge/blob/master/README.md
The library acts as a bridge between webview and the Java part of the app
Yes, The idea is to inject an object to the JS part and use that as a bridge to call a function inside the native part. Have updated the git repo reflecting the change. Only thing is that before we inject the event listener we have to make sure the page is fully loaded, so it might take some time. So first thing is to add the interface mWebview.addJavascriptInterface(new JSInterface(), "jsnativebridge");
Then define the class
private class JSInterface {
#JavascriptInterface
public void playPauseClicked(String string) {
Log.d("console", "" + string);
}
}
Then wait for page load and add the event listener like below
mWebview.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(activity, description, Toast.LENGTH_SHORT).show();
}
public void onPageFinished(WebView view, String url) {
Log.d("console", "" + "loaded");
mWebview.loadUrl(
"javascript:(function() { " +
"var ch=document.getElementsByClassName('mejs-playpause-button');" +
"ch[0].addEventListener('click', function(){" +
" jsnativebridge.playPauseClicked('Yes');" +
"});"+
"})()"
);
}
});
You can refer the git repo, have added a working demo.
Cheers

Parse HTML contents of Webview - Android

I'd like to detect when my webview loads a certain page, for example, an incorrect login page. I've tried using onLoadResource and shouldOverrideUrlLoading, but I can't get either to work, and I'm thinking a better way would to parse the HTML whenever the webview starts loading a page, and if a certain string is found within the HTML, then do whatever.
Is there a method to do this? I've tried using TagSoup, but I have no clue how to relate it into my webview. Here's what my code looks like now:
String fullpost = "pass=" + passwordt + "&user=" + usernamet + "&uuid=" + UUID;
String url = "mydomain.com";
mWebview.postUrl(url, EncodingUtils.getBytes(fullpost, "BASE64"));
mWebview.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView mWebview, String url) {
String webUrl = mWebview.getUrl();
if (webUrl.contains("/loginf")) {
MainActivity.this.mWebview.stopLoading();
MainActivity.this.setContentView(R.layout.preweb);
}
}
});
Basically, the postUrl is initiated from a user click on a button in a layout, and that's what starts the WebView, and then I call setContentView to the layout that contains the webview.
From there, if the login info is correct, the webpage goes to XXX, and if it's incorrect, it goes to YYY. So, I want to detect immediately (and on every page load from there on out), if YYY is loaded, then //domagic. Hope that makes sense. Being the page redirect from url to XXX or YYY is automatic and not initiated by the user, shouldOverrideUrlLoading doesn't work, and I can't figure out how to use onLoadResource, so I'm just completely lost.
My current thought is loading everything in a separate thread and then using the WebView to display the content (that way I can parse the HTML), but I'm not sure how that'd work or even how to do it.
Anyone have any ideas or suggestions?
I think I've read a way to get a text string of a webview's content. Then, you could use jsoup to parse it. [neh, don't even need jsoup; just indexOf string check]
I'll suggest that you do consider handling the login with an HTTP client. It gives you flexibility, and seems the more proper way to go. I've been using the loopj library for HTTP get and post requests. It allows for simpler code. For me, anyway, a relative Android newbie. Here's some code from my project, to get you thinking. I've left out stuff like progress bar, and cookie management.
private void loginFool() {
String urlString = "http://www.example.com/login/";
// username & password
RequestParams params = new RequestParams();
params.put("username", username.getText().toString());
params.put("password", password.getText().toString());
// send the request
loopjClient.post(urlString, params, new TextHttpResponseHandler() {
#Override
public void onStart() {
// called before request is started
//System.err.println("Starting...");
}
#Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
// called when response HTTP status is "200 OK"
// see if 'success' was a failed login...
int idx = responseString.indexOf("Please try again!");
if(idx > -1) {
makeMyToast("Sorry, login failed!");
}
// or actual success-ful login
else {
// manage cookies here
// put extractData in separate thread
final String responseStr = responseString;
new Thread(new Runnable() {
public void run(){
extractData(responseStr);
selectData(defaultPrefs.getInt("xyz_display_section", 0));
// start the next activity
Intent intent = new Intent(MainActivity.this, PageViewActivity.class);
startActivity(intent);
finish();
}
}).start();
}
}
#Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
makeMyToast("Whoops, network error!");
}
#Override
public void onFinish() {
// done
}
});
}
You can see that, in the response handler's onSuccess callback, I can test for a string, to see if the login failed, and, in the onFailure callback, I give a network error message.
I'm not experienced enough to know what percent of web servers this type of post login works on.
The loopj client receives and manages cookies. If you will be accessing pages from the site via a webview you need to copy cookies from the loopj client, over to the webview. I cobbled code from a few online posts, to do that:
// get cookies from the generic http session, and copy them to the webview
CookieSyncManager.createInstance(getApplicationContext());
CookieManager.getInstance().removeAllCookie();
CookieManager cookieManager = CookieManager.getInstance();
List<Cookie> cookies = xyzCookieStore.getCookies();
for (Cookie eachCookie : cookies) {
String cookieString = eachCookie.getName() + "=" + eachCookie.getValue();
cookieManager.setCookie("http://www.example.com", cookieString);
//System.err.println(">>>>> " + "cookie: " + cookieString);
}
CookieSyncManager.getInstance().sync();
// holy crap, it worked; I am automatically logged in, in the webview
EDIT: And, I should have included the class variable definitions and initializations:
private AsyncHttpClient loopjClient = new AsyncHttpClient();
private PersistentCookieStore xyzCookieStore;
xyzCookieStore = new PersistentCookieStore(this);
loopjClient.setCookieStore(Utility.xyzCookieStore);

Calling javascript function in android kitkat

Since upgrading my nexus 4 to Android kitkat, my application stopped working. I was using javascript methods in webview but now my methods are not called. This is how i setup webview and load my javascripts:
mWebview.getSettings().setJavaScriptEnabled(true);
JavaScriptInterface jsi = new JavaScriptInterface(mCtx);
mWebview.addJavascriptInterface(jsi, ANDROID_BRIDGE);
mWebview.setWebViewClient(new WebClient());
mWebview.setWebChromeClient(new OwnChromeClient());
mWebview.setWebContentsDebuggingEnabled(true);
mWebview.loadUrl("about:blank"); //load dummy url to onPageFinished called in webViewClient
and my webClient:
public class WebClient extends WebViewClient {
#Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
Log.e(WebClient.class.getSimpleName(), description + " code "
+ errorCode);
}
/**
* Load javascript into webview
*/
#Override
public void onPageFinished(WebView view, String url) {
ECalcMarblesWrapper wrapper = new ECalcMarblesWrapper(mCtx);
String javascripts = wrapper.getJavascripts();
String content = String.format(jsHtmlContainer, javascripts,
jsHelpFunctions);
view.loadUrl("javascript: " + content);
}
}
all loading methods are called but when i call any of javascript methods, this error is printed out to console:
Uncaught ReferenceError: initForm is not defined
where initForm is my javascript method.
On api 18 application is doing just fine, i have javascript annotation in my methods
If the target version of the application is set to 17 or higher you need to add annotation #JavascriptInterface above each method you want to export to the web view.
everything changes in Android kitkat for webview and javascript. you need to use evaluateJavascript()
check here: https://developer.android.com/guide/webapps/migrating.html
and here: https://developer.android.com/reference/android/webkit/WebView.html#evaluateJavascript(java.lang.String, android.webkit.ValueCallback)

Calling JavascriptInterface method through the WebView and JS function after changing config often doesnt work

I have some problem with calling a JavaScript function in a WebView and getting response in the app (on my Galaxy Tab 2 '10). I call the js-function directly after content loading. According this one solution i use a PictureListener
webView.setPictureListener(new PictureListener() {
#Override
public void onNewPicture(WebView view, Picture picture) {
webView.loadUrl("javascript: JsImpl.init('{response}');");
}
});
it works fine: when content is loaded, onNewPicture always is started.
"javascript: JsImpl.init('{response}') loads js that calls:
class JsImpl {
private final static String LOG_TAG = "JsImpl";
#JavascriptInterface
public void init(String json) {
Log.d(LOG_TAG, json);
}
}
In the log i see D/JsImpl(18400): {response}. It seems to be everything okay but...
When I switch my tab on a landscape or portrait mode the WebView is recreated, onNewPicture are started but the method public void init(String json) in the JsImpl is often not called at all!
So if i change orientation, sometimes js works, sometimes doesn't work. No exception, no errors...
Who faced with the same problem?
Below is full code
public class NextActivity extends Activity {
#SuppressLint("SetJavaScriptEnabled")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_next);
final WebView wv = (WebView) findViewById(R.id.WebView);
wv.getSettings().setJavaScriptEnabled(true);
wv.loadUrl("file:///android_asset/index.html");
wv.addJavascriptInterface(new JsImpl(), "JsImpl");
wv.setPictureListener(new PictureListener() {
#Override
public void onNewPicture(WebView view, Picture picture) {
wv.loadUrl("javascript: JsImpl.init('{responce}');");
}
});
}
class JsImpl {
private final static String LOG_TAG = "JsImpl";
#JavascriptInterface
public void init(String json) {
Log.d(LOG_TAG, json);
}
}
}
html code is not needed here, just put on the assets dir an empty html file index.html.
EDIT:
I've found one soution but i dont know exactly is it correct varion. But it works.
I added handler loop
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (isJSSetup) return;
Log.i("handler-loop", "JS didn't handle. Reload page");
wv.loadUrl(CONTENT_PAGE);
handler.postDelayed(this, 1000);
}
}, 1000);
when #JavascriptInterface init(String json) is called, isJSSetup became true. In handler loop i check if JS is setup or no if (isJSSetup) return;. If no - i reload URL again wv.loadUrl(CONTENT_PAGE) and try to call init(String json). So long as it is not going to work. It's hardcode, so i'm still looking for a good solution.
I hope this will help, but I can't say for sure because you are not showing how your WebView is restored after orientation changes.
I was having very hard time making my nested fragments to work properly after orientation changes. I wanted to retain the form values and all functionality during screen rotations. It all worked fine except the JavascriptInterface was NEVER working after orientation changes.
My WebView is restored in the onCreateView. I had to remove the restoreState call for my WebView and it all works now.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.layoutT4, container, false);
webView = (WebView) rootView.findViewById(R.id.webViewT4);
prepareBrowser(); // the browser is initiated here including the JavascriptInterface
if (webView != null) {
String URL = (savedInstanceState == null) ? "" : savedInstanceState.getString("URL");
if (URL.equals("")) {
webView.loadUrl("yourhome.html");
} else {
//webView.restoreState(savedInstanceState.getBundle("webViewBundle")); // this line was preventing the JavascriptInterface to work properly
webView.loadUrl(URL); // this is saved in onSaveInstanceState - I just reopen the URL here, the PHP page will take care of reloading the data
}
}
return rootView;
}

Categories

Resources