Is there a way I can have my own history stack for webview?
It sounds to me that webview.canGoBack() similar to stack.peek(), and webview.goBack() similar to stack.pop(). I wanted to have a way to create my own stack that keeps the webview history because I want to add additional info to the stack depends on the page that is currently being viewed, and this stack might have non-url strings. This is why I wanted to create my own custom stack instead of using the native methods.
I tried to create a stack and add piece of info when onPageStarted is called. then I realized there are several redirects happen would cause onPageStarted being called several times within a page.
Let's say I start from pageA -> pageB -> pageC. there are 1 redirect happen on pageA and 2 redirects happen on pageB. what's gonna happen is:
by using webview.goBack(), my page go from pageC -> pageB -> pageA. this is exactly the behavior I want.
but when using my custom stack, my page go from pageC -> pageB -> pageB-> pageB -> pageA -> pageA
is htere a way that I can create a custom stack that has the same behavior as webview.goBack()?
Thanks for your help!
What you need to do is to override the WebViewClient function shouldOverrideUrlLoading which gets called
before a new url is loaded, and which allows you to peek its value. Your implementation can then store all visited urls:
ArrayList<String> visitedUrlStack = new ArrayList<String>();
webview.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
visitedUrlStack.add(url);
return false; // continue with loading of url!!
}
});
You can interrogate visitedUrlStack at any point in your program for a list of visited urls.
It is important that your implimentation of shouldOverrideUrlLoading() will return false. This implies
to the webview that it can go on loading the url.
Related
I have a standard task to display a ListView (UrlListView) with urls. When the user touches the row I open an activity (UrlViewActivity) with WebView.
Unfortunately, this trivial task brings many troubles for me in Android...
Standard approach:
UrlViewActivity has an *.xml file with a layout that contains WebView.
When the user touches any row inside UrlListView, I start this activity, set this layout as a content view, find my WebView by id and ask it to load an url. Yes, I also call inside my activity's onPause - mWebView.onPause() and inside onDestroy - mWebView.destroy();
Everything works pretty good until the user tries to open various urls rather often: opens an url, closes UrlViewActivity, opens another url and e t. c...usually after 15-25 such loads (every UrlViewActivity launch creates a new WebView) famous Android 4.4 bug occurs on Moto X and Nexus 5:
https://code.google.com/p/android/issues/detail?id=73632
so my app freezes inside nativeLockCanvas...
Then I tried another approach that some guys on StackOverFlow recommended:
Keeping WebView as a static object, create it only during the first startup and on every UrlViewActivity launch add this WebView and clear its state (and remove this WebView inside onStop() of course).
No freezing!!!
But there is a big problem to clear WebView's state on Android:
It's really hard to force a WebView correctly measure its height when loading a new url (WebView always tries to remember previous long page and doesn't want to decrease it height even when loading a very small page).
There are many advices to reset WebView's height like the following: How a change the content size of a webview in android?
But unfortunately clearView() is already deprecated and does nothing...The only working approach that I found is: calling mWebView.loadUrl("about:blank"); and inside overriden onPageFinished(WebView view, String url) method call mWebView.requestLayout();
That really works and pages become finally take correct height and scrolling works ok, but...it remembers blank page inside its history and shows it to user when he presses back button...
To resolve the problem of storing blank page in WebView's history I've tried to use clearHistory() method, but...if I call this method inside onPageFinished(WebView view, String url) right after my correct page was loaded guess what? WebView mystically forgets that it has just measured itself correctly (during first onPageFinished(WebView view, String url) call when "about:blank" page was loaded) and shows long scrolling even for a short pages...
Here is my code snippet:
enum WebViewStates {
WEB_VIEW_INITIALIZED, WEB_VIEW_RESET_DONE, WEB_VIEW_HISTORY_CLEARED
}
private ViewGroup mWebViewContainer;
private WebViewStates mWebViewState;
private void setupWebView() {
mScrollView.smoothScrollTo(0, 0);
mWebViewContainer.addView(mWebView);
mWebViewState = WebViewStates.WEB_VIEW_INITIALIZED;
mWebView.loadUrl("about:blank");
mWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
switch (mWebViewState) {
case WEB_VIEW_INITIALIZED:
mWebViewState = WebViewStates.WEB_VIEW_RESET_DONE;
mWebView.requestLayout();
mWebView.loadUrl(mPreviewUrl);
break;
case WEB_VIEW_RESET_DONE:
mWebViewState = WebViewStates.WEB_VIEW_HISTORY_CLEARED;
mWebView.clearHistory();
break;
case WEB_VIEW_HISTORY_CLEARED:
break;
}
super.onPageFinished(view, url);
}
});
}
I have a webview that shows ads (not my ads), the problem is when user clicks the "x" button to exit the ad, the ad still directs them to a site. What I wonder is since I can't control the ads, can I instead Disable page directing/forwarding inside webview? that means even if user clicks a link inside my webview nothing should happen.
You are looking for WebClient.shouldOverrideUrlLoading method.
webview.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading (WebView view, String url){
//True if the host application wants to leave the current WebView and handle the url itself, otherwise return false.
return true;
}
});
I tried using shouldOverrideUrlLoading, but it didn't work. It looks like that this method is called only once when the html is loaded. After that, you click a link but the method is no more invoked.
I am also making a WebView embedding Youtube Player. Instead of forwarding ads redirect from the WebView, I prefer to open ads in a browser. So I override onLoadResource method:
#Override
public void onLoadResource(final WebView view, final String url) {
if(url.indexOf("googleadservices.")>-1){
view.getSettings().setJavaScriptEnabled(false);
view.stopLoading();
view.postDelayed(
new Runnable(){
#Override
public void run(){
Uri uri=Uri.parse(url);
Intent i=new Intent(Intent.ACTION_VIEW,uri);
i.setClassName("com.android.browser","com.android.browser.BrowserActivity");
startActivity(i);
}
}
,100
);
}
}
It worked. When I clicked the ads link, a new browser is opened in which ads site is displayed well, and the WebView was not redirected. When I push the return button, WebView show up again and I can continue watching video.
But there were still problems. If I repeat opening browser and returning to WebView for many times, the WebView might fail to block redirecting to the ads site. It is just redirected to the ads site. If I am lucky I could repeat opening and returning for 100 times. But sometimes It failed just when I repeat several times. I don't know why.
Does anyboday have any idea about how to improve it? Or is there another way to disable ads redirect?
You can build undetected webview build-id adblocker
I know it is too late for answering this question, however, for the sake of others who have the same question.
Well, you can build webview build-id adblocker, if you wish to prevent ads from loading, and provide smooth experience to the users, I am confident, because I have already implemented it in may app.
The Idea
Is to have a black list of all possible ad-serves domain name, then while webview load resources, you will prevent loading from black list domains. so it depends on how many ads-serves domain you have in the black list, fortunately, there is one website (pgl.yoyo.org/as/) which provide you with a very long list of ad-serves domai names, and listed them in many flavoures.
you can read this article for:
how to implement webview build-id adblocker
, you will build it %100 as long as you follow step by step instructions.
A summary of what we need to do:
Get the list of ad hostnames from pgl.yoyo.org.
Save the list somewhere, load it when application starts.
UseWebViewClient.shouldInterceptRequest(WebView, String) to intercept
requests.
Check if the request URL belongs to one of the hostnames in
the list and override it, returning a dummy resource instead of the
actual one, which is supposed to be ads
I have an android application that loads a webview from a server. I do not have the server code so I cannot change anything in Javascript. I want to figure out when a button is being clicked in a webview and what is the label in the button. I do not know the Id, I just want to get the label.
I tried searching for this but could not find an answer. I found solutions where you can work in the javascript but in my case I cannot.
This suggestion may help to find useful information that could lead to determination of your button label. Override shouldOverrideUrlLoading(), shouldInterceptRequest() and/or onLoadResource() for the WebViewClient so you can get at the URL of any redirects.
Example:
webview.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Try to learn something useful from the 'url' here.
// Continue as normal, loading the 'url' within this WebView.
view.loadUrl(url);
return false; // Allow the WebView to handle the request.
}
// Optional: Add similar for "shouldInterceptRequest()" and/or "onLoadResource()".
});
Note: Overriding shouldOverrideUrlLoading() as above is the standard way to keep redirects within the same WebView rather than redirecting to the default browser application.
You might really want to check this page:
Building Web Apps in WebView (Google API Guides)
Specifically, it seems that addJavascriptInterface might be what you are looking for:
addJavascriptInterface(Object object, String name)
It allows you to execute your Java code from javascript and, paired with the ability to insert code in a page, it's an incredibly powerful tool for granting you a high level of coupling between your Activity and your page.
I think that at this point you will already know what to do, but I'll sketch a possible course of action anyway:
create a javascript interface with the callbacks you want executed in your activity when a button is pressed
as soon as your page loads, install the code to call your javascript interface in each button (or link) by injection
Hope this helps
Is it possible to capture touch events over a WebView in the activity that contains it, without loosing link functionality?
Consider a WebView showing a webpage with links. If the user taps on a link I would like the WebView to handle the touch event. If the user taps somewhere else I would like the activity to handle the touch event.
How can this be done?
Yes, it is.
(I can't elaborate more on this unless you are more specific on your question.)
If i understand your question correctly it'd seem difficult to interpret whether something is a link or not in the onTouchEvent() since all it knows about is X,Y coordinates (not html). However, off the top of my head it seems you could probably use the javascript binding piece in WebView to have javascript decide if the something is a link or not and act accordingly. Again, I haven't done this before nor tested it...just thinking out loud.
When initializing your WebView, try:
mWebView.setWebViewClient(new WebViewClientOverrider());
Then create a private class:
private class WebViewClientOverrider extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//TODO handle the link
return true;
}
}
Lastly, replace the TODO handle the link line with your own code for handling the link selection.
Or you can monitor the stack trace for:
android.webkit.CallbackProxy.uiOverrideUrlLoading()
See http://www.javapractices.com/topic/TopicAction.do?Id=78 for how to determine your stack trace from a throwable (created in an overriden WebView.loadUrl() method).
I have an android application that loads web pages in an activity with a WebView. I am using the retrieving the page manually and using WebView's loadDataWithBaseURL to display it on screen. Everything there is fine.
Now, i am trying to override the Back button press to simulate going back in the WebView history stack. I am able to override the Back button press, i can see that there is a history stack in the WebView, i can see that the history url is correct, but when i call WebView's goBack() method, it displays a blank page.
Anyone encountered this before or give me a couple of suggestions to proceed from this?
Edit: If i use WebView's loadUrl method, the Back button with an override works as intended. But why.... If i need to handle this manually, how do i start messing with history pages?
I got the same problem also. I found that the problem went away if I set the historyUrl parameter on the call to loadDataWithBaseURL.
You should check if the canGoBack() method returns true before calling goBack()
The only solution I've found is to create a Stack<String> and manually manage history
The way I deal with this is keeping a local stack pointer to the number of loaded pages after the root page loaded using loadDataWithBaseURL . When going back, if my pointer hits 1 I am at root level and reload the root page with loadDataWithBaseURL.
FYI, I use this code in Activities with fragments, so the fragments implement the interface IBackButtonListener which helps me to capture the back button in the main activity and propagate the event to the current fragment. If the fragment returns true it means it has taken care of the event.
IBackbuttonListener.java
public interface IBackButtonListener {
public boolean onBackButtonPressed();
}
Fragment that implements IBackButtonListener and has a webview loaded from html data.
private int historyStackPointer = 0;
...
#Override
public boolean onBackButtonPressed() {
boolean rtn = false;
if (webView.canGoBack()) {
if(historyStackPointer > 1) {
webView.goBack();
historyStackPointer--;
rtn = true;
}else{
if(historyStackPointer == 1) {
// Reload the html data
webView.loadDataWithBaseURL("file:///android_asset/", html_data, "text/html", "UTF-8", null);
historyStackPointer = 0;
rtn = true;
}else{
webView.loadUrl("about:blank");
rtn = false;
}
}
} else {
rtn = false;
}
return rtn;
}
html_data is a String with the page's html.
What I noticed is that if the url ends in .html, that white screen appears when back button is pressed.
On the other hand, if you remove that .html from your url - obviously only if this is supported by that website (i.e. the redirection and all is handled properly at the server side and that it doesn't trigger the 404 Page Not Found error), that url will act as your base this time and when you press the back button, that white screen should not appear this time.
for example: you have to replace
http://example.com/page.html to:
http://example.com/page