I implemented android webview and onKeyDown method for back key. (It implements webview.goBack();)
My problem is exactly similar to the question in this post below (no answers there)
How to control the Android WebView history/back stack?
PROBLEM - When I press back button, webview selects the previous URL, but if that URL was actually a redirect, it goes into this vicious cycle/loop. If you look at chrome or stock browser it correctly handles the back without going back to the redirects.
How can this be solved?
Example: go to gap.com. Then select "My Gap Credit Card". This opens a redirect link and then the final page. Now when I click back, it never goes to Gap.com home page.
Any suggestions...
Additional Information: I did implement the shouldOverrideUrlLoading. If I remove that method, it seems to work fine but with this method it does not...
I've just tested this on jellybean and it seems to work.
Essentially, whenever a new URL is loaded in the WebView keep a copy of the url.
On the next URL request, double check they we aren't already on this page, if they are, then go back in the webview history another step.
Essentially this is relying on the url passed into the override step being the redirected url, rather than the final redirected url.
public class MainActivity extends Activity {
private Button mRefreshButton;
private WebView mWebView;
private String mCurrentUrl;
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mRefreshButton = (Button) findViewById(R.id.refresh);
mRefreshButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mWebView.reload();
}
});
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(mCurrentUrl != null && url != null && url.equals(mCurrentUrl)) {
mWebView.goBack();
return true;
}
view.loadUrl(url);
mCurrentUrl = url;
return true;
}
});
mWebView.loadUrl("http://www.gap.com/");
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN) {
switch(keyCode) {
case KeyEvent.KEYCODE_BACK:
if(mWebView.canGoBack()){
mWebView.goBack();
return true;
}
break;
}
}
return super.onKeyDown(keyCode, event);
}
}
I hope this answer if anyone is still looking for it.I had been hunting to fix similar issues in my project and had tried multiple approaches like using
- WebView.HitTestResult
- Pushing the urls into the list
- onKeyDown and so on...
I think most of it would work if your app consists of just webview. But my project had a combination of native and webview and handles some native schema.
Essentially found that the key is how you override the method shouldOverrideUrlLoading. Since i wanted my app to handles some of the urls and the webview to handle some of the other ones especially the back handling.I used a flag for back presses something like ..
#Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mClient.setIsBackPressed(true);
//mClient is an instance of the MyWebviewClient
mWebView.goBack();
} else {
super.onBackPressed();
}
}
public class MyWebviewClient extends WebViewClient {
private Boolean isBackPressed = false;
public void setIsBackPressed(Boolean isBackPressed) {
this.isBackPressed = isBackPressed;
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (isBackPressed){
return false;
}
else {
// handle the url by implementing your logic
return true;
}
}
#Override
public void onPageFinished(WebView view, String url) {
isBackPressed = false;
super.onPageFinished(view, url);
}
}
In this way, whenever there is a redirect when you click back, then it return false and hence mocks the behaviour of the webview. At the same time, you make sure that the isBackPressed is set to false after the page finishes loading.
Hope this helps !!
Related
As an example, here used google as the website. When we click on any link and try to go back by hitting back button, it not calling goBack() but exiting. Back button in websites also not working.
public class MainActivity extends Activity {
public WebView mWebView;
#SuppressLint({"setJavaScriptEnabled"})
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = findViewById(R.id.WebView);
mWebView.clearCache(true);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
mWebView.loadUrl("http://google.com");
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
I'm using this code for months without any error, suddenly i got issue this few days back without any modifications. I also tried by calling goBack() in onBackPressed() with if statement but still shows same issue. Any help will be appreciated!!
because its chrome bug : https://bugs.chromium.org/p/chromium/issues/detail?id=794020
This has been fixed in M64. You can find a public release calendar here [1]. This is just an approximate schedule, but we aim to be close to the public schedule.
According to the schedule, M64 will go to stable January 23rd.
While M64 is currently in beta, we have not yet made a beta release with the fix. I do not have information for when such a beta release will go out (but it will be sooner than Jan 23rd).
[1] https://www.chromium.org/developers/calendar
I have a WebView, which loads an html page from server, but the page is invisible on the WebView.
The html is loaded properly (I've debugged with chrome://inspect and the html, including all javascripts exist), but it is invisible on the phone screen.
There was no changes in my code, when this bug appeared. The bug appeared when I installed updates to Android System WebView on my phone.
If I uninstall the updates, all works properly again.
In addition, I've checked the callbacks of the WebViewClient and noticed that onPageCommitVisible is not called. So somehow, the page is not loaded properly. Only if I press the Back button, to exit the WebView, I see that the onPageCommitVisible is called for my webpage (buat it doesn't help, as the back button exists the WebView, as expected).
Here is my code for the webview:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout rlMain = new RelativeLayout(getActivity());
rlMain.setContentDescription(Constants.STARTAPP_AD_MAIN_LAYOUT_CONTENT_DESCRIPTION);
rlMain.setId(Constants.MAIN_LAYOUT_ID);
getActivity().setContentView(rlMain);
// Create WebView and set its parameters
try{
webView = new WebView(getActivity().getApplicationContext());
webView.setBackgroundColor(0xFF000000);
getActivity().getWindow().getDecorView().findViewById(android.R.id.content).setBackgroundColor(0x00777777);
webView.setVerticalScrollBarEnabled(false);
webView.setHorizontalScrollBarEnabled(false);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
// set software acceleration
if (softwareAcceleration) {
ApiUtil.setWebViewLayerTypeSoftware(webView, null);
}
webView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
return true;
}
});
webView.setLongClickable(false);
webView.addJavascriptInterface(createJsInterface(), Constants.INTERFACE);
setWebViewSpecificParameters(webView);
webView.loadDataWithBaseURL("http://www.xxxxxx.com", getHtml(), "text/html", "utf-8", null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
webView.setWebViewClient(new MyWebViewClient());
RelativeLayout.LayoutParams webviewPrms = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
);
rlMain.addView(webView, webviewPrms);
}
public void setWebViewSpecificParameters(final WebView webView) {
webView.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return (event.getAction() == MotionEvent.ACTION_MOVE);
}
});
}
private class MyWebViewClient extends WebViewClient {
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
Logger.log(TAG, Log.DEBUG, "!!!!!shouldInterceptRequest" );
return super.shouldInterceptRequest(view, url);
}
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
Logger.log(TAG, Log.DEBUG, "!!!!!shouldInterceptRequest" );
return super.shouldInterceptRequest(view, request);
}
#Override
public void onPageFinished(WebView view, String url) {
setWebViewBackground(view);
runJavascript(Constants.JAVASCRIPT_SET_MODE_SERVER, getPosition());
runJavascript(Constants.JAVASCRIPT_ENABLE_SCHEME, "externalLinks");
InterstitialMode.this.onWebviewPageFinished();
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return clicked(url);
}
}
In an app that navigates from one website to another based on user input, there's a high chance (>50%) that the second website won't display at all after calling WebView.loadUrl and the previous website stays visible - it's even interactive, i.e. scrolling works. The problem is usually resolved after calling WebView.loadUrl again. There's no obvious indication of the bug occurring other than the user not seeing the second website. Relying on the user to reload the page manually is not a satisfying solution since the bug occurs quite often.
I was able to workaround this issue by using a custom WebViewClient to detect that the second website was not loaded properly and triggering a reload:
setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
buggyWebViewHandler.postDelayed(new Runnable() {
#Override
public void run() {
if (!wasCommitCalled) {
loadUrl(url);
}
}
}, 2500);
}
}
#Override
public void onPageCommitVisible(WebView view, String url) {
wasCommitCalled = true;
}
});
Where buggyWebViewHandler is a Handler or any other class that allows deferring a piece of code for some time. Additionally, you'll need to set wasCommitCalled = false; whenever WebView.loadUrl is called - for example by overriding the method.
Note that this only works for Android 23 onwards because that's when onPageCommitVisible was added. See here for a full implementation: https://github.com/TomTasche/OpenDocument.droid/blob/8c2eec5c57e5962e9ac4c46549be2241b259eb32/app/src/main/java/at/tomtasche/reader/ui/widget/PageView.java#L72-L96
If anyone is brave enough to dig deeper into why this is happening: while debugging it seemed that onPageStarted is not called whenever this bug occurs. Maybe that helps...
I am asking this after long searches without help.
I created a simple webview app with eclipse.
(Sometimes – app opens a web browser depending on the url)
I need to kill the app if there are no clicks (not active) within 5 minutes.
Whenever a user clicks on any link in the app – the timer would reset.
I know it should be simple but I’ve got mixed up with too many lines of code… :/
Can anyone be nice and show a code example for how it’s done ?
Thank you dearly
public class MainActivity extends Activity {
private WebView webView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final WebView webView = (WebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient() {
// if url contains url1,2,3 - launch in browser
#Override public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
if(url.contains("url1.com")||(url.contains("url2.com")) ||(url.contains("url3.com")) ) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
else {
view.loadUrl(url);
return false;
}
}
});
webView.loadUrl("http://starter-site.com");
}
}
Use a Handler. Create a Runnable that finishes your activity.
Each time a user clicks a link, do something like this:
myHandler.removeCallbacks(myFinishingRunnable);
myHandler.postDelayed(myFinishingRunnable, 5000);
Be careful not to leak your activity (if your Runnable is an inner class, make it static and give it a WeakReference to your activity). And it's probably a good idea to set/unset the callback when your activity is resumed/paused.
It seems like I have found a better solution. I decided to kill the app if a specific url string is in use.
So, if the click went on Google, it would simply launch the browser and shut down.
Hope this is ok in the Android way of mind...I guess not.
#Override
public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
if (url.contains("url1.com") || (url.contains("url2.com")) || (url.contains("url3.com"))) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
if (url.contains("google")) {
finish();
}
return true;
} else {
view.loadUrl(url);
return false;
}
}
I used to use onPageFinished to keep track of webview history:
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if ("about:blank".equals(url) && view.getTag() != null){
view.loadUrl(view.getTag().toString());
}else{
view.setTag(url);
}
}
now, when I try view.goBack() it goes back to about:blank and not to the page I want.
How can I set that "about:blank" url so to be put into webview history, so that when I go back I don't see it again. But I don't want view.clearHistory(), because it deletes all history.
Well assuming you want to go back in the history until you find the first non about:blank
here is how to do it:
in your activity
public void goBackInWebView(){
WebBackForwardList history = webView.copyBackForwardList();
int index = -1;
String url = null;
while (webView.canGoBackOrForward(index)) {
if (!history.getItemAtIndex(history.getCurrentIndex() + index).getUrl().equals("about:blank")) {
webView.goBackOrForward(index);
url = history.getItemAtIndex(-index).getUrl();
Log.e("tag","first non empty" + url);
break;
}
index --;
}
// no history found that is not empty
if (url == null) {
finish();
}
}
code logic is to loop throw all previous pages. and on first no empty page load it.
if nothing found finish the app.
I don't know a way of editing the history stack directly, but you could maybe fashion something with shouldOverrideUrlLoading()
From the WebView documentation:
A WebView has several customization points where you can add your own behavior. These are:
...
Creating and setting a WebViewClient subclass. It will be called when
things happen that impact the rendering of the content, eg, errors or
form submissions. You can also intercept URL loading here (via
shouldOverrideUrlLoading()).
I found #Mr.Me's answer to be a bit nicer as an OnKeyListener
public class BackButtonListener implements View.OnKeyListener {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
WebView webView = (WebView) v;
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
WebBackForwardList backstack = webView.copyBackForwardList();
int currentIndex = backstack.getCurrentIndex();
int i = -1;
while (webView.canGoBackOrForward(i)) {
if (!backstack.getItemAtIndex(currentIndex + i).getUrl().equals("about:blank")) {
webView.goBackOrForward(i);
return true;
}
i--;
}
webView.goBack();
return true;
}
}
return false;
}
}
Which you just set like so:
mWebView.setOnKeyListener(new BackButtonListener());
I am working in an android application that can post tweets to twitter and I am doing it with the Web View widget. If the user is not logged in it will go to the login screen and if it a logged-in user in it will go to the tweet page. My requirement is after twetting from my application it should return to my application. How can I handle this situation by WebView. How will I get the redirect url from my WebView.
Please help me.
Please look into my code:
public class TestTwittershareActivity extends Activity {
WebView webview;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
webview = (WebView) findViewById(R.id.webview);
webview.getSettings().setJavaScriptEnabled(true);
webview.loadUrl("http://www.twitter.com?status=");
webview.setWebViewClient(new HelloWebViewClient());
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
webview.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
private class HelloWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
}
If I understand your problem correctly, you need a way to check whether the user is done with sending his tweets. Your best bet is to check the url that is being loaded in your webview. Hopefully, this url has some kind of indication that the tweet is done (maybe something in the status part?). To check the url you can use the HelloWebViewClient class you've already created and override it's onPageFinished method. e.g. something like this:
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (url.contains("status=DONE")) {
// start your activity here
}
}
If you cannot detect based on the url that the user is finished, then things are much more complicated. In that case you can try to add a javascript that allows you to extract the html of the loaded page and you'll have to parse the html to look for clues if the user is done or not.