Android - Cant refresh webview from js interface - javascript

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);
}
}

Related

Android WebView and JavaScript programmtically clicking button not working

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);

Android WebView downloading then displaying later in AlertDialog

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);
}

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");

Hiding div in Android WebView app with java doesn't work

I have a div called downloadapp that I'd like to display to users who visit my mobile website instead of using my Android app. So I need to hide that div for users who already use my app.
My code:
private void startWebView(String url) {
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
//On error, open local file
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
webView.loadUrl("file:///android_asset/www/myerrorpage.html");
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
#Override
public void onPageFinished(WebView view, String url) {
view.loadUrl("javascript:document.getElementById('downloadapp').style.display = 'none';");
}
});
webView.loadUrl(url);
}
Problem:
My app loads the div first and its actually being displayed for a couple of seconds. Then suddenly, the whole webpage loaded in the app disappears and a blank page with the word "none" is the only thing that is left on the screen.
Is there something wrong with the way I try to hide the div "downloadapp" or what am I missing here?
What I want it to do is immediately hide the div before the webpage is being displayed.
This is happening on newer versions of WebView -- if your app is targeting API level 19 or above (KitKat), then if a javascript: URL returns a value, this value is interpreted as content for a new web page to open. In your case, as 'none' is the return value of the assignment, it is used as the content for a new page.
Just put void(0); at the end of your code in javascript: to prevent it from returning a value.

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