I created a WebView App and it's working fine but there is a link which I want it to open in the default browser instead of that app what can I do.
Add a WebViewClient to your webView (if not already added) and then override shouldOverrideUrlLoading () method:
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView webview, String url) {
if (url.contains(myUrl)) {
Intent intent= new Intent(Intent.ACTION_VIEW, myUrl);
context.startActivity(intent);
return true;
} else {
return super.shouldOverrideUrlLoading(webview, url);
}
}
}
This way your are telling your webview to not continue loading a specific url. Instead, launch the proper application (mostly a browser) to handle the url.
The problem is rather simple.
In the application we want to keep track of the current url being displayed. For that we use shouldOverrideUrlLoading callback from the WebViewClient by saving the url into a class field for every update. Here is the relevant code:
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
mCurrentUrl = url;
// If we don't return false then any redirect (like redirecting to the mobile
// version of the page) or any link click will open the web browser (like an
// implicit intent).
return false;
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
...
}
});
mWebView.loadUrl(mInitialUrl);
However, there is at least one scenario, where the callback never gets triggered and the mCurrentUrl field doesnt get updated.
The url: https://m.pandora.net/es-es/products/bracelets/556000
Last updated url (shouldOverrideUrlLoading never gets called when clicking the product): https://m.pandora.net/es-es/products/bracelets
I have tried with callbacks like onPageStarted(), but the url also gets filtered and there doesn't seem to be an accessible one upstream since its protected code.
Reading android documentation about WebView I found this:
https://developer.android.com/guide/webapps/migrating.html#URLs
The new WebView applies additional restrictions when requesting resources and resolving links that use a custom URL scheme. For example, if you implement callbacks such as shouldOverrideUrlLoading() or shouldInterceptRequest(), then WebView invokes them only for valid URLs.
But still doesnt make sense since the above url is generic and should meet the standard.
Any alternative or solution to this?
When you click a product on that web page, it loads the new content in with JavaScript and updates the visible URL in the address bar using the HTML5 History APIs.
From the above MDN article:
This will cause the URL bar to display http://mozilla.org/bar.html, but won't cause the browser to load bar.html or even check that bar.html exists.
These are sometimes called single-page applications. Since the actual loaded page doesn’t change, the WebView callback for page loads isn’t called.
In case you know precisely what kind of HTTP request you want to intercept, you could use the shouldInterceptRequest callback that gets called for each request. It’s likely that the web application loads some data from an API, for example when a product is shown, which you could then detect.
If detecting this isn’t possible, but you’re in control of the web application, you could use the Android JavaScript interface to invoke methods within the Android application directly from the web page.
If you’re not in control of the loaded page, you could still try to inject a local JavaScript file into the web page and observe when the history APIs are used, then call methods in your Android application over the JS interface. I tried observing these events in Chrome with the method described in the previous link and it seems to work fine.
Maybe this helps someone, although the signature in the question is correct, but Android Studio suggests the following method signature:
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
which then never called. It took me a while to notice that the right signature is:
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Sorry if this not 100% fit the question, but I believe this may help someone in the same situation. It's not always easy to notice that the second parameter is different.
Please omit mWebView.getSettings().setDomStorageEnabled(true);
Then again try, if a new url found then will invoke shouldOverrideUrl()
I had the same problem like you, and I've finished with extending of WebViewChromeClient with listening for callback to
public void onReceivedTitle(WebView view, String title)
mWebView.setWebChromeClient(mSWWebChromeClient);
private WebChromeClient mSWWebChromeClient = new WebChromeClient() {
#Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (!view.getUrl().equals(mCurrentUrl)) {
mCurrentUrl = view.getUrl();
//make something
}
}
};
For me the problem was below line -
mWebView.getSettings().setSupportMultipleWindows(true);
After removing it shouldOverrideUrlLoading was being called.
after stumbling on this problem and searching for solutions, I've found the one that worked perfectly for me
https://stackoverflow.com/a/56395424/10506087
override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
// your code here
super.doUpdateVisitedHistory(view, url, isReload)
}
Another approach you can try: Catch the url by javascript side. Initialize your webView with this:
webView.addJavascriptInterface(new WebAppInterface(getActivity()), "Android");
After page is completely loaded (You can use an algorithm to check this like this https://stackoverflow.com/a/6199854/4198633), then:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript("(function() {return window.location.href;})", new ValueCallback<String>() {
#Override
public void onReceiveValue(String url) {
//do your scheme with variable "url"
}
});
} else {
webView.loadUrl("javascript:Android.getURL(window.location.href);");
}
And declare your WebAppInterface:
public class WebAppInterface {
Activity mContext;
public WebAppInterface(Activity c) {
mContext = c;
}
#JavascriptInterface
public void getURL(final String url) {
mContext.runOnUiThread(new Runnable() {
#Override
public void run() {
//do your scheme with variable "url" in UIThread side. Over here you can call any method inside your activity/fragment
}
});
}
}
You can do something like that to get url, or anything else inside the page.
Add
webView.getSetting().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
then shouldOverrideUrl will be triggered.
onProgressChanged is always triggered when reloading, loading new page with userclick or XmlHttpRequest.
Compare the URL of previous load and the current load, you'll know it's reloading or loading a new page. This works perfect in my single page Web App.
First declare a global variable to store last URL.
String strLastUrl = null;
Then override onProgressChanged(WebView view, int progress)
mWebView.setWebChromeClient(new MyWebChromeClient(){
#Override
public void onProgressChanged(WebView view, int progress) {
if (progress == 100) {
//A fully loaded url will come here
String StrNewUrl = view.getUrl();
if(TextUtils.equals(StrNewUrl,strLastUrl)){
//same page was reloaded, not doing anything
}else{
//a new page was loaded,write this new url to variable
strLastUrl = StrNewUrl;
//do your work here
Log.d("TAG", "A new page or xhr loaded, the new url is : " + strLastUrl);
}
}
super.onProgressChanged(view, progress);
}
});
I've also tried above solutions, but most of them have issue in my case:
doUpdateVisitedHistory sometimes can not return correct url after "#" made by XmlHttpRequest.
My case is a single page web App. The web App uses javascript with
xhr to display new page when user click an item. For example, user is
currently at http://example.com/myapp/index.php , after clicking, the
browser url becomes
http://example.com/myapp/index.php#/myapp/query.php?info=1, but in
this case, doUpdateVisitedHistory returns
http://example.com/myapp//myapp/
onReceivedTitle doesn't work in my case because the response retrieved by XMLHttpRequest does not have <title></title> tag.
The JavascriptInterface method also works, but I'm afraid it will cause
security related issues with javascript.
public class AndroidMobileAppSampleActivity extends Activity {
/** Called when the activity is first created. */
String mCurrentUrl="";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView mWebView = (WebView) findViewById(R.id.mainWebView);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.setWebViewClient(new MyCustomWebViewClient());
mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
mWebView.loadUrl("https://m.pandora.net/es-es/products/bracelets/556000");
}
private class MyCustomWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
mCurrentUrl = url;
Log.i("mCurrentUrl",""+mCurrentUrl);
view.loadUrl(url);
return true;
}
}
}
try this one...
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 have this app which reads articles in a webview. When ArticleActivity is first started, the html of the webpage is actually downloaded into a string and parsed to make it look like a mobile site, then I use wb.loadData(html, "text/html; charset=UTF-8", null);. The downloading occurs in an asynctask since I can't do internet activity on the ui thread, but for now, whenever a link on the page is clicked, it just defaults to loadUrl() as per the webviewclient. I can use goBack() in history with this, no problem.
The problem lies with the search bar widget. I call SearchResultsActivity, which opens a webview and loads a url. When the user clicks on an article, SearchResultsActivity sends it BACK to ArticleActivity through an intent. The problem with that is that I want to use loadData() for the new article link, and I try to do that in onResume(), but nothing happens if I'm still on the previously loaded page. The log statement shows that the Url from EXTRA_RETURN_RESULT did make it, so I think the intent is good. I think this is due to a javascript "same origin" thing, so that's why if I use loadDataWithBaseURL(), the page loads. But if I try the back button now, the previously loaded page is blank! What do I do to preserve history when I load the new page?
class SearchWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// if it is morning sign out AND is an article, send url to ArticleActivity
if(Uri.parse(url).getHost().endsWith("morningsignout.com")) {
Intent intent = new Intent(view.getContext(), ArticleActivity.class);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, url); // Put url in intent
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); // Open w/ old articleActivity if exists
view.getContext().startActivity(intent);
return true;
}
// Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
// view.getContext().startActivity(intent);
return false;
}
}
from ArticleActivity
#Override
protected void onResume() {
super.onResume();
// URL from CategoryActivity
String intentURL = getIntent().getStringExtra(Intent.EXTRA_HTML_TEXT);
// Set webView to new article
if (intentURL != null) new URLToMobileArticle(webView).execute(intentURL);
else {
// If statement is reached, then intent originated from SearchResultsActivity
intentURL = getIntent().getStringExtra(Intent.EXTRA_RETURN_RESULT);
new URLToMobileArticle(webView).execute(intentURL);
Log.d("ArticleActivity", "Loading: " + intentURL);
}
}
what URLToMobileArticle is, getArticles() is the download/parsing function:
public URLToMobileArticle(WebView webview) {
this.wb = webview;
}
#Override
protected String doInBackground(String... params) {
return getArticle(params[0]);
}
#Override
protected void onPostExecute(final String html) {
wb.loadData(html, "text/html; charset=UTF-8", null);
// wb.loadDataWithBaseURL(link, html, "text/html; charset=UTF-8", null, null);
Log.d("URLToMobileArticle", "Loaded webpage");
}
The answer to this is that I can use shouldInterceptRequest() to always load the html in the way I see fit. I just need to ensure that I can distinguish between webpage urls and other requests like images or style sheets
I have used webview and its work fine but i need to find some text in webview.It work upto api level 18 but after it has deprecated, so i want to know is there any way to find out this issue and solve this problem. Any body have any idea then please help me out this problem.I appreciate your help
I am not sure if this is the problem, but you should call loadUrl last, after everything else.
Also you seem to loop your webview with shouldOverridemethod, check my version
webviewdevelopment = (WebView) findViewById(R.id.webViewdevelopment);
webviewdevelopment.setWebViewClient(new myWebClient());
webviewdevelopment.getSettings().setJavaScriptEnabled(true);
webviewdevelopment.loadUrl("http://stackoverflow.com");
public class myWebClient extends WebViewClient {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri;
try {
uri = Uri.parse(url);
} catch (NullPointerException e) {
// let Android deal with this
return true;
}
String host = uri.getHost(); //Host is null when user clicked on email, phone number, ...
if (host != null && host.equals("stackoverflow.com")) {
// This is my web site, so do not override; let my WebView load the page.
return false;
}
else {
// You should consider this, since you are using JavaScript.. you may make your users vulnerable
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs or anything else (email, phone number, ...)
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
}