Android open intent:// protocol in WebView

2020-03-25 09:50发布

问题:

When i load url into WebView and try to open links, some of them shows an error page like:

net::ERR_UNKNOWN_URL_SCHEME intent://maps.yandex.ru?utm_medium=tab...

How can i open this links, or at least check whether appropriate app installed and run it?

I've tried to override url loading by implementing custom WebViewClient:

@Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (!url.startsWith("http")) {
            getActivity().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse(url)));
            return true;
        }
        return false;
    }

but app crashes with ActivityNotFoundException in this case.

02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=intent://maps.yandex.ru?utm_medium=tab-maps&text=магазин&utm_source=serp&yandexuid=2258091361456330110 }
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1798)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.app.Instrumentation.execStartActivity(Instrumentation.java:1512)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.app.Activity.startActivityForResult(Activity.java:3930)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.app.Activity.startActivityForResult(Activity.java:3890)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:784)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.app.Activity.startActivity(Activity.java:4213)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.app.Activity.startActivity(Activity.java:4181)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at ru.uxapps.voicesearch.FWeb$CustomWebViewClient.shouldOverrideUrlLoading(FWeb.java:70)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at com.android.webview.chromium.WebViewContentsClientAdapter.shouldOverrideUrlLoading(WebViewContentsClientAdapter.java:325)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at org.chromium.android_webview.AwContentsClientBridge.shouldOverrideUrlLoading(AwContentsClientBridge.java:266)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:37)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.os.Looper.loop(Looper.java:148)
02-24 20:58:51.886 31255-31255/ru.uxapps.voicesearch W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5417)
02-24 20:58:51.887 31255-31255/ru.uxapps.voicesearch W/System.err:     at java.lang.reflect.Method.invoke(Native Method)

App crashes even if required app is installed. But if open this link in Chrome, it shows maps.yandex.ru site, or opens app if installed.

Is there a common solution, which will work for all intent:// links? Thanks for your suggestions.

回答1:

I found a solution. This question and this documentation helps me to understand situation.

As a result, i've written a link handler which follow this logic:

  1. Open http and https in the same WebView
  2. Tries to handle known schemes (tel: etc.)
  3. If can't find a known activity, parses intent: scheme and tries to run appropriate app
  4. If required app not installed, tries to load provided fallback url into current WebView
  5. If no fallback url provided, redirects to market and ask to install required app

The code is:

mWebView.setWebViewClient(new CustomWebViewClient());

//...

private class CustomWebViewClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView webView, String url) {
        if (url.startsWith("http")) return false;//open web links as usual
        //try to find browse activity to handle uri
        Uri parsedUri = Uri.parse(url);
        PackageManager packageManager = getActivity().getPackageManager();
        Intent browseIntent = new Intent(Intent.ACTION_VIEW).setData(parsedUri);
        if (browseIntent.resolveActivity(packageManager) != null) {
            getActivity().startActivity(browseIntent);
            return true;
        }
        //if not activity found, try to parse intent://
        if (url.startsWith("intent:")) {
            try {
                Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
                if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
                    getActivity().startActivity(intent);
                    return true;
                }
                //try to find fallback url
                String fallbackUrl = intent.getStringExtra("browser_fallback_url");
                if (fallbackUrl != null) {
                    webView.loadUrl(fallbackUrl);
                    return true;
                }
                //invite to install
                Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(
                        Uri.parse("market://details?id=" + intent.getPackage()));
                if (marketIntent.resolveActivity(packageManager) != null) {
                    getActivity().startActivity(marketIntent);
                    return true;
                }
            } catch (URISyntaxException e) {
                //not an intent uri
            }
        }
        return true;//do nothing in other cases
    }

}

Maybe it needs some cleanup, but it can be helpful. Please tell me if you know an easier way of doing this, i still looking for the best solution.