I am currently trying to display a webview in a small popup. But I would like to first load the view, then display it when properly available.
So what I tried to do was 2 functions:
public void fetchPopUp(String url, Context appContext)
{
webView = new WebView(appContext);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(url);
}
public void displayPopUp(Activity activity)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder = new AlertDialog.Builder(activity, android.R.style.Theme_Material_Dialog_Alert);
} else {
builder = new AlertDialog.Builder(activity);
}
builder.setView(webView);
AlertDialog dial = builder.show();
webView.addJavascriptInterface(new TCWebAppInterface(dial, webView, activity), "WAIAndroid");
webView.loadUrl("javascript:WAIAndroid.resize(document.body.getBoundingClientRect().height)");
}
As for the WAIAndroid resize:
#JavascriptInterface
public void resize(final float height)
{
activity.runOnUiThread(new Runnable()
{
#Override
public void run()
{
Log.e("JIBA", "sizes\n" + activity.getResources().getDisplayMetrics().widthPixels + "\n" + height + "\n" + activity.getResources().getDisplayMetrics().density + "\n" + activity.getResources().getDisplayMetrics().heightPixels);
});
}
The strange thing is when I call both fetchPopup and displayPopup one after the other, it's working perfectly (beside size issue which was the point of the resize).
But when I call them separately, the popup display empty and the resize method is not called.
I tried to display every objects, the webview is still the good, I tried displaying when it's finished loading, it's called properly, I tried reloading, tried to make it invisible or gone then visible again, tried to reload in javascript. Nothing changes the fact that when I call the displayPop a bit later than right after the fetchPopup method, it doesn't work. I guessed that there's an issue after the url is loaded and "onPageFinished" is called. But I can't find how to work around this.
Any help would be greatly appreciated, I'm running out of ideas!
Thanks a lot SO!
Try to move your code:
webView.addJavascriptInterface(new TCWebAppInterface(dial, webView, activity), "WAIAndroid");
to your fetchPopUp function before loadUrl, like this:
public void fetchPopUp(String url, Context appContext){
webView = new WebView(appContext);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new TCWebAppInterface(dial, webView, activity), "WAIAndroid");
webView.loadUrl(url);
}
Related
I have a very simple use case i.e to programmatically call a button of a web page loaded inside my webview. Below is the code
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setAppCacheEnabled(false);
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url) {
//super.onPageFinished(view, url);
switch (view.getOriginalUrl()){
case Properties.LOGIN_URL:
webView.evaluateJavascript(
"javascript:" +
"var usernameField = document.getElementById('loginUserName');" +
"usernameField.value = '" + Properties.USERNAME + "';" +
"var passwordField = document.getElementById('loginPassword');" +
"passwordField.value = '" + Properties.PASSWORD + "';", null
);
webView.evaluateJavascript(
"javascript:" +
"document.getElementById('loginButton').click();", new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
}
}
);
break;
}
}
});
view.loadUrl(Properties.LOGIN_URL);
The username and password field injection is working fine but the code to perform button click isn't working.
I have searched a lot and experimented a lot but no luck, Is this even possible?
EDIT:
The URL I'm accessing is private to our organization, is on http and not secured, not sure if it's relevant.
I have tried another public page with login fields and the same code works and the button gets clicked successfully.
Okay I found the reason. So the onPageFinished was getting called while all the DOM elements are not loaded hence the javascript was failing. For time being it to work I have added a delayed Handler with 5 sec delay and it worked. The right solution should be to check for the page to complete to load (may be use jquery to check when the document is ready).
You can handle the webview button click this way
#JavascriptInterface
public void handleClick(String id) {
startActivity(new Intent(mContext, MainActivity.class));
}
In javascript
Android.handleClick(_id);
I have a webView, and I am scraping the HTML:
webView.addJavascriptInterface(new WebScraper(), "HtmlViewer");
view.loadUrl("javascript:HtmlViewer.showHTML" +
"('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');");
And the parameter html contains the whole HTML page:
#JavascriptInterface
public void showHTML(String html) {
scrapePage(html);
}
Now the problem is, sometimes my HTML page shows an error message if the data hasnt fully loaded, or AJAX calls failed. When that happens I want to refresh the page. So i did:
if (matcher.find()) {
id = matcher.group(1);
getLecture(dom);
} else {
Log.d("errors", "looks like player didnt load try again by refreshing page");
webView.post(new Runnable() {
#Override
public void run() {
webView.reload();
}
});
}
But it doesnt look like my showHTML(String html) method is called again after refreshing. I was hoping someone could help me.
thnkas
Wait until the page has finished loading through the interface method onPageFinished (in the WebViewClient class) and then get the content of the page using the evaluateJavascript method. This way you can get the content of the site without using the JavaScriptInterface annotation
The code will look like this
webView.evaluateJavascript("<GET HTML>", new ValueCallback<String>() {
#Override
public void onReceiveValue(String value) {
Log.i(TAG, "Found HTML content: " + value);
}
}
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");
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
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;
}