I have the following code.
public class MyActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
WebView webview = new WebView(this);
webview.getSettings().setJavaScriptEnabled(true);
//Add a client to the view
webview.setWebViewClient(mClient);
webview.loadUrl("http://www.google.com");
setContentView(webview);
}
private WebViewClient mClient = new WebViewClient()
{
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
Uri request = Uri.parse(url);
if(TextUtils.equals(request.getAuthority(), "www.google.com"))
{ //Allow the load
return false;
}
Toast.makeText(MyActivity.this, "Sorry, buddy", Toast.LENGTH SHORT).show();
return true;
}
};
}
Understood till the if condition but what does Toast.makeText() do? what happens by returning true?
You can find all the answer in a little place called The Documentation.
Here's the one for maketext : http://developer.android.com/reference/android/widget/Toast.html#makeText%28android.content.Context,%20java.lang.CharSequence,%20int%29
And the other for shoudOverrideUrl : http://developer.android.com/reference/android/webkit/WebViewClient.html#shouldOverrideUrlLoading%28android.webkit.WebView,%20java.lang.String%29
You can see that makeText is used to create a Toast, but you still need to call the method show() in order to display it, so your code is wrong.
As for shouldOverrideUrlLoading, returning True if the host application wants to leave the current WebView and handle the url itself, otherwise return false.
What your code do is "if the url requested is not www.google.com", don't load it and display an error toast.
From the Android documentation:
http://developer.android.com/reference/android/widget/Toast.html
A toast is a view containing a quick little message for the user.
See also: http://developer.android.com/guide/topics/ui/notifiers/toasts.html
You said you understood the "if" condition: it determines whether to load the content back to this WebView, based on the URL passed in, keeping the user from leaving the Google site.
It shows a short message on your device that says "Sorry, buddy" appears if you couldnt load the page
If your webview couldn't load the webpage you can see the toast message. and then after returning true, your webview is gonna be finished.
getAuthority()-Gets the decoded authority part of this URI.
if(TextUtils.equals(request.getAuthority(), "www.google.com"))- It will compare the athority part of request uri with String "www.google.com", If true its return false else it return true and it also shows the toast message(Toast is a object used to display short messages in android);
If WebViewClient is provided, return true means the host application handles the url, while return false means the current WebView handles the url. This method is not called for requests using the POST "method".
Related
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 loading an URL in webview. i.e. first I am loading a login page in a Webview. Then after user type username and password and tap login button in webview page, it shows the particular session key for that logon in the webview. Now I want to read that session key and hide that information from user. Is anybody has the idea how to do that?
Thanks.
The same question has been answered multiple times. And all the answers refer to the this post. Though this post has warnings in the end, I could not find any other eaiser way to achive what tou are looking for.
Below is the copy-paste of the code from this post.
final Context myApp = this;
/* An instance of this class will be registered as a JavaScript interface */
class MyJavaScriptInterface
{
#JavascriptInterface
#SuppressWarnings("unused")
public void processHTML(String html)
{
// process the html as needed by the app
}
}
//Edit 1 start
final ProgressDialog pd = ProgressDialog.show(OnlinePaymentActivity.this, "", "Please wait, your transaction is being processed...", true);
//Edit 1 end
final WebView browser = (WebView)findViewById(R.id.browser);
/* JavaScript must be enabled if you want it to work, obviously */
browser.getSettings().setJavaScriptEnabled(true);
/* Register a new JavaScript interface called HTMLOUT */
browser.addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT");
/* WebViewClient must be set BEFORE calling loadUrl! */
browser.setWebViewClient(new WebViewClient() {
/////Edit 2 start
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
{
If(browser.getVisibility() == View.VISIBLE)
{
browser.setVisibility(View.GONE);
}
pd.show();
}
///// Edit 2 end
#Override
public void onPageFinished(WebView view, String url)
{
//Edit 3 start
If(browser.getVisibility() == View.GONE)
{
browser.setVisibility(View.VISIBLE);
}
pd.dismiss();
// Edit 3 end
/* This call inject JavaScript into the page which just finished loading. */
browser.loadUrl("javascript:window.HTMLOUT.processHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');");
}
});
/* load a web page */
browser.loadUrl("http://lexandera.com/files/jsexamples/gethtml.html");
I found a way of doing this. i.e. First I am reading the token in the body text and once the token found, make the webView to invisible. Your suggestion is not applicable for me as sometimes I want to show the login page. i.e. if the user is not login to the particular account, we should show the web view. Once they return the token after login, webview should be hidden.
I'm looking for a way to log the requests and start/end times made by an embedded webview. I'm not able to find a way to do it so far other than rooting the phone and running tcpdump. That works for me, but I need to run this in the field, so that's not really viable. There are lots of ways to log the URL and start time, but I can't see the finish (or, bonus, the full response metadata).
shouldLoadResource could work if I could wrap the current request, but I'd have to fetch it myself with HTTP support in order to return it en masse, because there isn't enough API exposed to fully forward to the inner request. (I don't want to do that for a number of reasons, including that webview on devices doesn't use the same network stack as the HTTP classes, and because it will change the timing of subresources.)
I've been trying to find ways to turn on chromium_net debug flags to do this, but I can't figure out how do do that in the context of the WebView or system properties.
I would really rather not ship my own webcore to do this, but if needs must...
override method shouldInterceptRequest()
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
Log.d(LOG_TAG, "shouldInterceptRequest: " + url);
return super.shouldInterceptRequest(view, url);
}
In that case, you could also add a WebViewClient (see http://developer.android.com/reference/android/webkit/WebViewClient.html). Which would look something like
WebView webView.setWebViewClient(new MyWebViewClient());
.
.
.
public class MyWebViewClient extends WebViewClient
{
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
// Note time
// Return false to say we want the WebView to handle the url.
return false;
}
#Override
public void onPageFinished (WebView view, String url)
{
super.onPageFinished(view, url);
// Note time
}
}
Note that both shouldOverrideUrlLoading and onPageFinished are only called only for the main frame - they will not be called for iframes or framesets. But this should give you what you need.
-Edit: Solution Found-
Figured it out after some heavy searching - one person (I literally mean one) said they instead used onPageLoad(); which worked perfectly for my purposes. The difference is that onPageLoad() runs later than shouldOverrideUrlLoading, but It doesn't make a difference in my code.
I'm trying to set up Twitter authorization with OAuth for an Android app, and thus far I can successfully send the user to the authorization URL, however, what I am trying to do now is intercept the redirect to the callback (which would just lead to a 404 error, our callback URL isn't going to have an associated page on our servers). What I'm attempting to do is check if the URL is our callback, then extract the OAuth Verifier from the URL. I setup my WebView with this code:
view = (WebView)findViewById(R.id.twitterWbVw);
view.setWebViewClient(new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading(WebView wView, String url)
{
String urlHolder;
String[] verifExtrctr;
urlHolder = url.substring(0, url.indexOf('?'));
System.out.println("url");
if(urlHolder.equalsIgnoreCase(CALLBACK_URL))
{
verifExtrctr = urlHolder.split("?");
verifExtrctr = verifExtrctr[2].split("=");
if(verifExtrctr[0].equalsIgnoreCase("oauth_verifier"))
{
params[5] = verifExtrctr[1];
return true;
}
else
{
System.out.println("Inocorrect callback URL format.");
}
}
else
{
wView.loadUrl(url);
}
return true;
}
});
view.loadUrl(urlAuthorize.toExternalForm());
Thing is even System.out.println("url");(which I'm using to debug)doesn't run! So I'm pretty much dry on ideas, and can't find anyone with a similar problem. The authorization URL goes through fine, and I can successfully authorize the app, however the redirect to the callback URL for some reason never get's intercepted. Any help would be appreciated, this is in my onResume() if that matters.
After some research I conclude that despite what most of the tutorials out there say, shouldOverrideUrlLoading() does not get called when:
You load a URL like
loadUrl("http://www.google.com");
The browser redirects the user automatically via an HTTP Redirect. (See the comment from #hmac below regarding redirects)
It does however, get called when you you click on a link inside a webpage inside the webview. IIRC the twitter authorization uses an HTTP Redirect.. Bummer, this would be helpful if it worked how all the tutorials say it does. I think this is from a very old version the Android API...
You might want to consider overriding the onProgressChanged method of a WebChromeClient like here: How to listen for a WebView finishing loading a URL? or the onPageFinished() method of the WebViewClient.
I've found what I think is a reasonable way to do this thanks to the previous answer and comments pointing me in the right direction.
What I did is override onPageStarted and onPageFinished in a custom WebViewClient.
The code goes something like this...
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
if (pendingUrl == null) {
pendingUrl = url;
}
}
#Override
public void onPageFinished(WebView view, String url) {
if (!url.equals(pendingUrl)) {
Log.d(TAG, "Detected HTTP redirect " + pendingUrl + "->" + url);
pendingUrl = null;
}
}
And of course along with the Log.d you would put any specific code you want to run upon detecting the redirect.
For people stumbling across this, when the method shouldOverrideUrlLoading(WebView view, WebResourceRequest request) is not being called, look up your minSdkVersion. If you use below API 24 you should use shouldOverrideUrlLoading(WebView view, String url).
I have a javascript function 'gotoMainPage()'
function gotoMainPage( ) {
window.location.href = "main/main.do";
}
Now, WebViewClient's shouldOverrideUrlLoading(..) gets called if gotoMainPage( ) is executed as a result of a 'direct user interaction', such as user clicking on this div:
<div.... onclick='gotoMainPage();'/>
However, if the execution is done via setTimeout( gotoMainPage, 100 ); or via an XMLHttpRequest callback, shouldOverrideUrlLoading(..) is never called but the requested page is loaded into the webview.
Am I missing an obvious explanation or is this a bug?
Anyone?
In my case, when using window.location = "http://xxx" in my webpage, the event shouldOverrideUrlLoading() is not triggered.
However, if I use a custom url scheme or protocol such as "androidurl://", shouldOverrideUrlLoading() is fired. My workaround would to be use a custom protocol and add the following code in the shouldOverrideUrlLoading() method:
if (url.startsWith("androidurl://")) {
url = url.replaceAll("androidurl://", "http://");
}
This will change the custom protocol back to the "http://" protocol and you can handle the correct url from there.
This works for me.
I've seen this come up myself as well, which imho, its clearly a bug.
Perhaps you can latch on to :
#Override
public void onLoadResource (WebView view, String url)
{
}
and/or
#Override
public void onPageFinished(WebView webView, String url)
{
}
I tried this myself, and found that onLoadResource would be triggered, even if shouldOverride wasnt.