How to control the Android WebView history/back stack? - android

I am trying to figure out a way to handle the WebView back stack similar to how the Android web browser handles it when the back button is pressed from within my own app's WebView.
The problem has to do with Javascript redirects. The WebView back stack appears to contain URLs that simply redirect. Here is my relevant code:
private class ArticleWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK)
{
WebView wv = (WebView)findViewById(R.id.web);
if (wv.canGoBack())
{
wv.goBack();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
As you can see, pressing the back button selects the previous URL in the stack, which loads in the WebView itself after shouldOverrideUrlLoading() is called. I'm actually doing a lot more inside shouldOverrideUrlLoading() that isn't relevant for this particular question, so "remove my WebViewClient implementation" won't work for me. At any rate, when the previous URL is a Javascript redirect, that URL loads and then Javascript immediately redirects to the URL the WebView was just at. I can't disable Javascript because the website depends on it. I also can't change the website (third-party).
The result of this redirection issue is a vicious cycle for the end-user who is frantically pressing back just to be slightly faster than the Android webpage processing engine.
Now here is where it gets interesting: The Android web browser handles the back button just fine for Javascript redirects! It follows the redirects just fine but the back button does what a user expects it to do. That means it is possible to correctly handle this scenario. How does the Android web browser handle this problem?

Detect when url loading is triggered by the user, and add those to the backstack instead.
private List<String> previous = new ArrayList<String>();
private String mLastUrl;
webview.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
Log.i("DebugDebug", "OnPageFinished " + url);
mLastUrl = url;
super.onPageFinished(view, url);
}
});
webview.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
WebView.HitTestResult hr = ((WebView)view).getHitTestResult();
if (hr != null && mLastUrl != null) {
if (previous.isEmpty() || !previous.get(previous.size() - 1).equals(mLastUrl)) {
previous.add(mLastUrl);
}
Log.i("DebugDebug", "getExtra = " + hr.getExtra() + "\t\t Type = " + hr.getType());
}
return false;
}
});
#Override
public void onBackPressed() {
Log.i("DebugDebug", "onBackPressed");
int size = previous.size();
if (size > 0){
webview.loadUrl(previous.get(size - 1));
previous.remove(size - 1);
} else {
super.onBackPressed();
}
}

your onKeyDown should look like these
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
myWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
here myWebView is declared in the Activity's onCreate

I had a similar problem with yours and found how to figure it out. Here is my answer.
When I click the first link(www.new.a) it automatically redirects other link(mobile.new.a). Usually the links redirect two or three, and my solution have been worked on almost every redirect links. I hope this answer help you out with annyoing redirecting links.
I finally figured out that. You need a WebViewClient with four APIs. There are shouldOverrideUrlLoading(), onPageStarted(), onPageFinished(), and doUpdateVisitedHistory() in the WebViewClient. All the APIs you need is API 1 so don't worry about.
Here is my answer. Check out that! :)

The correct answer is to return false on shouldOverrideUrlLoading() as described in https://stackoverflow.com/a/8644978/5022858 and https://developer.android.com/reference/android/webkit/WebViewClient.html. This prevents redirects from being added to the history list.

private class ArticleWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK)
{
WebView wv = (WebView)findViewById(R.id.web);
if (wv.canGoBack())
{
wv.goBack();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
This shouldOverrideUrlLoading method should return false if you want to handle the link in your webview rather than system browser, and do not call view.loadUrl(url), your webview will load url automatically if you return false. The history will be not correct if you load url manually.

boolean isGoBack = false; //back key is pressed
private long goBackTime = 0; //
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
isGoBack = true;
goBackTime = System.currentTimeMillis();
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
Webview mWebView.
setWebViewClient(new WebViewClient() { //setWebViewClient
#Override
public boolean shouldOverrideUrlLoading (WebView view, String url){
if (!TextUtils.isEmpty(startUrl) && !startUrl.equals(url) && isGoBack && System.currentTimeMillis() - goBackTime < 600) {
isGoBack = false;
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
finish(); //activity finish
}
} else {
view.loadUrl(url);
isGoBack = false;
return true;
}
return false;
}
#Override
public void onPageStarted (WebView view, String url, Bitmap favicon){
super.onPageStarted(view, url, favicon);
startUrl = url;
}
}

this snippet work for me, try it:
#Override
public void onBackPressed() {
BlankFragment fragment = (BlankFragment)
getSupportFragmentManager().findFragmentByTag("webView");
if (fragment.canGoBack()) {
fragment.goBack();
} else {
super.onBackPressed();
}
}

Related

WebView goback() method does not work

I have a requirement in my app where I need to render the multiple web pages in webview and provide the goback() method.
But unfortunately goback() method does not work in the case when we load the url(one of the url from multiple pages) which does not exit(Web page not available)and then we try to call the goBack()
it seems you complicate your code you just try simple code below
#Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
this is simple code work fine for you,i dont know if you are using flag for other reasons.
You can do like this.
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
And you Web page not available,you can use onReceivedError method.
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
#TargetApi(Build.VERSION_CODES.M)
#Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (request.isForMainFrame()) {
// you can do something when you Web page not available
// webView.goBack();
}
}
});

Android back button with web view functionality not working properly

Hi I am developing android application in which I am using one web view. My web view has some data which redirect to some different link. So when I click it should come back to previous content. And finally it should close the web view when it comes to landing page. I tried it in following ways :
webView = new WebView(this);
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains(URLManager.URL_DICOM_BACK_PRESSED_URL)) {
processBackPressed();
return true;
} else {
return super.shouldOverrideUrlLoading(view, url);
}
}
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
if (failingUrl.contains(URLManager.URL_DICOM_BACK_PRESSED_URL)) {
processBackPressed();
} else {
showError("Server unreachable");
}
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
});
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(url);
#Override
public void onBackPressed() {
if (webView != null && webView.canGoBack()) {
webView.goBack();
} else {
processBackPressed();
}
}
So problem is that whenever it is coming back to previous page it is fist showing current page and then previous page.So looks like first refresh the page and then load previous url. Am I doing anything wrong. Need Some Help. Thank you.
Try this -
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN){
switch(keyCode)
{
case KeyEvent.KEYCODE_BACK:
if(myWebView.canGoBack()){
myWebView.goBack();
}else{
finish();
}
return true;
}
}
return super.onKeyDown(keyCode, event);
}

Webview cannot go back with local html

I have a webview that load up a URL from internet, but when there is no connectivity (onReceiedError) it loads up a locally saved html document in assets folder.
but when I press back button on that particular activity (which loads up the local HTML) it doesn't go back to the previous activity instead tries to refresh the page.. It is only after 4-5 Back presses it goes back.
here is my code.
private class HelloWebViewClient extends WebViewClient{
#Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
mWebView.getSettings().setBuiltInZoomControls(false);
mWebView.getSettings().setLoadWithOverviewMode(false);
mWebView.getSettings().setUseWideViewPort(false);
mWebView.loadUrl("file:///android_asset/offline.html");
}
public boolean shouldOverrideUrlLoading(WebView webview, String url)
{
webview.loadUrl(url);
return true;
}
}
#Override
public boolean onKeyDown(int KeyCode, KeyEvent event)
{
if ((KeyCode)== KeyEvent.KEYCODE_BACK && mWebView.canGoBack())
{
mWebView.goBack();
return true;
}
return super.onKeyDown(KeyCode, event);
}
#Override
public void onBackPressed()
{
this.finish();
overridePendingTransition (0, R.anim.right_slide_out);
return;
}
}
Works fine with the normal webpage to load from Internet.
coz in onReceivedError you change the url . so there will be no goback event
you have to just show toast message and delete mWebView.loadUrl("file:///android_asset/offline.html");

Detect if user has completed Intent.ACTION_VIEW or just hit BACK

I have a webview and on click on some banner I am sending Intent.ACTION_VIEW to open browser or whatever user finds suitable like this:
w.setWebViewClient(new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url != null && url.startsWith("http://")) {
view.getContext().startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
} else {
return false;
}
}
});
Now what I want is to find a way to know if user just hit back button before opening browser or he actually opened browser. I found this question but no solution there
You can do one thing which may not be the best practice to use but I think this will solve your problem
Initialization => boolean isPageLoading = false;
webview.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return true;
}
public void onPageFinished(WebView view, String url) {
isPageLoading = false;
}
public void onPageStarted(WebView view, String url, Bitmap favicon){
isPageLoading = true;
}
});
Now you have right indicator which will state, whether page is loading or not.
Now in your activity override back key press event
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) { // Back key pressed
if(isPageLoading){
// Do the tasks you want to do when page is loading
}else{
// Do the tasks you want to do when page loading is finished (or not in progress)
}
return true;
}
return super.onKeyDown(keyCode, event);
}
Note that you can change the behavior of this functionality by adding more boolean variables which will indicate different status info (such as whether page loading has started or not etc)
Hope this will solve issue you have raised
Another possible solution: use startActivityForResult(intent, 1) instead of startActivity(intent) and check the requestCode and resultCode this way:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
//intent 1 was
if(resultCode == Activity.RESULT_OK) {
//successful
} else {
//not succusseful
}
}
}
This works for most sorts of Intent (email, camera, etc)

Android : Simple web application crashes on links other then the http scheme

I am a beginner in developing for the Android, so excuse me if this is obvious. I created a simple web application using the samples from android.com however, when a link like tel: or mailto: is clicked, the application crashes and askes me to force quit. I was wondering if anyone could tell me why.
public class MyApplication extends Activity {
WebView mWebView;
private class InternalWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// This is my web site, so do not override; let my WebView load the page
if (Uri.parse(url).getHost().equals("m.mydomain.nl")) {
return false;
} else {
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* Use the WebView class to display website */
mWebView = (WebView) findViewById(R.id.webview);
mWebView.setWebViewClient(new InternalWebViewClient());
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl("http://m.mydomain.nl/");
}
}
Sorry to be so stupid, this actualy fixed it. With mailto, there is no host, thus getHost() will return null and causes the exception.
private class InternalWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// This is my web site, so do not override; let my WebView load the page
if (Uri.parse(url).getHost() != null && Uri.parse(url).getHost().equals("m.domain.nl")) {
return false;
} else {
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
}

Categories

Resources