I am a beginner. When I start to make an app with webview.
I saw on docs that https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String)
Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
I am weak in Englis but I know what is hosting. but I don't get what is host application, why it calls like that?
1) Is host application means a web browser or webview in my app?
2) It should be helpful how shouldoverrideurlloading works with webview and browser.
3) return true will open a web browser??
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
final Uri uri = Uri.parse(url);
return handleUri(view, uri);
}
#TargetApi(Build.VERSION_CODES.N)
#Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
final Uri uri = request.getUrl();
return handleUri(view, uri);
}
and handleUri method
private boolean handleUri(WebView view, Uri uri) {
final String scheme = uri.getScheme();
final String host = uri.getHost();
// Based on some condition you need to determine if you are going to load the url
// in your web view itself or in a browser.
// You can use `host` or `scheme` or any part of the `uri` to decide.
if (scheme.startsWith("http:") || scheme.startsWith("https:")) {
view.loadUrl(uri.getPath());
return true;
} else {
return false;
}
}
When using webview you use shouldOverrideUrlLoading to enter a specific url(the one you intend the user to see). This method also will, with some more advanced coding used when scrapping data from web pages, allow you to collect the html data and possible modify or utilize the html document code in creative ways. Then displaying the webview to the user when your ready.
Ps. Heads up depending on which api your minimum is set at you have to use it differently. There are some tutorials on google searches but most are outdated. This is not a common practice. More often than not apps use an api provided by a url and then display the data or intended visuals.
Related
How can one use HTML links to navigate to local files (HTML pages) in WebView if targeting API 24 or higher?
This has been discussed before and solutions use the file:// URI scheme.
What worked so far was using
Go to local page
in an HTML file that is displayed in a WebView and clicking the link would load the local page app/src/main/assets/my_page.html.
However, starting from API 24, a FileUriExposedException is raised when clicking such a link. From logcat:
mypackage.myapp W/System.err: android.os.FileUriExposedException: file:///android_asset/my_page.html exposed beyond app through Intent.getData()
...
mypackage.myapp W/System.err: at org.chromium.android_webview.ResourcesContextWrapperFactory$WebViewContextWrapper.startActivity(ResourcesContextWrapperFactory.java:121)
mypackage.myapp W/System.err: at org.chromium.android_webview.AwContentsClient.sendBrowsingIntent(AwContentsClient.java:203)
According to the documentation, this is thrown when "an application exposes a file:// Uri to another app.". I wonder why this is the case, because according to the log everything seems to happen inside mypackage.myapp.
The documentation suggests using the content:// URI scheme instead, but this does not work in HTML files.
The following workaround (based on this answer) intercepts the loading of a file:// URI in the WebView and then loads it directly by app code with WebView.loadUrl(...). This is possible by overriding WebView.shouldOverrideUrlLoading in a WebViewClient passed to the WebView, e.g. when initializing it.
As there was an API change for this method in API 24, for compatibility there are two versions in the code (technically in the API<24 case one could also do as before, letting WebView open the file:// URI because the exception is not raised on devices running API<24).
if (android.os.Build.VERSION.SDK_INT >= 24) {
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest webResourceRequest) {
if (webResourceRequest.getUrl().getScheme().equals("file")) {
webView.loadUrl(webResourceRequest.getUrl().toString());
} else {
// If the URI is not pointing to a local file, open with an ACTION_VIEW Intent
webView.getContext().startActivity(new Intent(Intent.ACTION_VIEW, webResourceRequest.getUrl()));
}
return true; // in both cases we handle the link manually
}
});
} else {
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
if (Uri.parse(url).getScheme().equals("file")) {
webView.loadUrl(url);
} else {
// If the URI is not pointing to a local file, open with an ACTION_VIEW Intent
webView.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
}
return true; // in both cases we handle the link manually
}
});
}
The reason why there is an exception when letting the WebView open the link must have something to do with the Intent created by the WebView but I don't see whether or how it is exposed to another app.
That the workaround works is then because the WebView does not do anything with the link (no Intent is created), instead, when the link is clicked, the app gets control and opens the file:// URI direclty by passing it to WebView.loadUrl(...) - which seems to be fine.
I assume (but do not claim) that regarding security this is fine because the URI is only used to load the file it points to in this single WebView (and if this was problematic the system should throw the FileUriExposedException).
I never link in the HTML that way if you need to load an other page:
Go to local page
I link this way because my map struture look like this:
Go to local page
You just need that methode in your MainActivity.java and that will work:
private class MyBrowser extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("tel:") || url.startsWith("sms:") || url.startsWith("smsto:") || url.startsWith("mailto:") || url.startsWith("mms:") || url.startsWith("mmsto:") || url.startsWith("market:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
else {
view.loadUrl(url);
return true;
}
}
}
If you have any questions or It still don't work let me know
so I just started with Android programming and I am trying to make a little app using WebView. There is a url that redirects you to a pdf, I know WebView does not render pdf. So I want to use intent and display the pdf in Google Docs. However, the pdf address is randomly generated so I cant link it with
WebView.loadUrl("http://docs.google.com/gview?embedded=true&url=" + pdfURL);
How can I send an intent to Google Docs without using the exact pdf address?
I don't know what "randomly generated" means.
But the first thing that comes to my mind is to set a WebViewClient and override shouldOverrideUrlLoading:
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.endsWith(".pdf") == true) {
view.loadUrl("http://docs.google.com/gview?embedded=true&url=" + url);
return true;
}
return false;
}
});
Some more info in this thread.
I have a webview with custom webview client. I'm intercepting requests with:
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
// ...
}
sometimes I'm getting informations about custom events from webview in a form of a link:
xyz://do-something
When it happens I want webview to ignore it. But 'shouldInterceptRequest' has to return something and when I'm returning 'null' instead of my page it shows: 'unknown url xyz://do-something'. How can I deal with that? How can I intercept the link but disable action on webview side?
You just need to do a string comparison from the site that you are in and the site that you want to go to.
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.example.com")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
// 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;
}
Scenario
I have a WebView in my Android app which contains a Soundcloud embed (from Embedly). This embed has two buttons: "Play on Soundcloud" and "Listen in browser".
The "Play on Soundcloud" button contains a URL in format intent://tracks:257659076#Intent;scheme=soundcloud;package=com.soundcloud.android;end
Code
My WebView uses a custom WebViewClient (because I need to intercept some URLs for some different stuff).
protected class WebViewClient extends android.webkit.WebViewClient {
public WebViewClient() { }
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
PackageManager packageManager = context.getPackageManager();
// Create an Intent from the URL.
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
// Find out if I have any activities which will handle the URL.
List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
// If we have an app installed that can handle the URL, then use it.
if (resolveInfoList != null && resolveInfoList.size() > 0) {
Intent viewUrlIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(viewUrlIntent);
}
else {
// Do something else.
}
return true;
}
}
Problem
Clicking "Listen in browser" plays the track in the embed itself and works fine. Clicking "Play on Soundcloud" will call into shouldOverrideUrlLoading in the WebViewClient above (as expected). However, my code to find a activity can't find anything that can deal with this Soundcloud URL.
If I don't set my WebViewClient on the WebView (so it just does its own thing), the "Play on Soundcloud" button will work as expected and launch the Soundcloud app.
Temporary (crap) solution
I've managed to make this do what I want it to do by parsing the URL to get the track ID, then building a new URL using a format that Soundcloud definitely accepts (thanks to this SO post). A URL in the format "soundcloud://tracks:[TRACK_ID]" will be accepted by the Soundcloud app.
But WHY?
Either I am doing the whole "find out what activities can handle this URL" thing wrong, or maybe(?!) the default WebViewClient used by the WebView handles this explicitly?! Seems implausible.
I'm just extending the Temporary (crap) solution here, so this is far from a perfect answer, but might still help someone who absolutely needs to get this to work, also with private tracks.
The replace method works if the track is public, but with private tracks this does not work, probably because of the missing secret token in the intent URL.
Unfortunately the embed player does not contain all the necessary pieces of the URL I need to generate, except inside the iframe, which I cannot access due to cross-origin policy. So in addition to the iframe code I also need the share link.
What I ended up doing is making sure that the containing HTML page has the share link as a JS variable. I then read that variable using Java and create a new Intent with that URL. This works, because the official app also registers all soundcloud.com URLs.
So for private tracks this goes to the HTML page:
<script>var soundCloudURL = "https://soundcloud.com/my-profile/my-track/my-secret-token";</script>
Then inside your Android app you would have something like this:
#Override
public boolean shouldOverrideUrlLoading (WebView view, String url) {
if (uri.getScheme().contains("intent")) {
openSoundCloudPlayer();
return true;
}
}
private void openSoundCloudPlayer() {
appWebView.evaluateJavascript("(function() { return soundCloudUrl })();", new ValueCallback<String>() {
#Override
public void onReceiveValue(String soundCloudUrl) {
// JS null is converted into a string "null", not Java null.
if (soundCloudUrl != "null") {
// Take out the quotes from the string
soundCloudUrl = soundCloudUrl.replace("\"", "");
Uri newUri = Uri.parse(soundCloudUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, newUri);
startActivity(intent);
}
}
});
}
-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).