在 Android 中查看针对浏览器与外部应用程序的意图

View intents meant for browser vs external app in Android

我正在使用 android webview 组件构建一个基本的网络浏览器,并且最近添加了对在相关外部应用程序中打开 links 的支持,例如如果您在一个页面上并单击一个 youtube link,则会打开 youtube 应用程序而不是导航到网页。

当应用程序是新安装的并且您第一次点击 link 时(我怀疑我的应用程序此时不是默认浏览器),这可以很好地接受。然后它总是提示你是否想在另一个应用程序中打开它,即使唯一的其他相关应用程序是其他浏览器,这不是一个很好的用户体验,因为用户已经在他们想要打开的浏览器中 link 否则他们不会使用它。

所以我需要能够区分 link 有一个专门安装的应用程序(例如,它找到了维基百科 links 的维基百科应用程序)和 link没有专门的应用程序,适合任何浏览器打开。

这是MyWebViewClient.shouldOverrideUrlLoading()中的相关代码...

Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
if(intent!=null){
    PackageManager packageManager = context.getPackageManager();
    ResolveInfo info = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
    if (info != null) {
        String suggestedPackageName = info.activityInfo.applicationInfo.packageName;
        String intentAction = intent.getAction();
        final boolean packageMatchesThisBrowser = (MY_PACKAGE_NAME).equals(suggestedPackageName);
        final boolean isUrlAttempt = UrlHelper.isUrlAttempt(url);
        final boolean areSuggestedAppsOnlyBrowsers = false; // ????
        final boolean canItBeOpenedInThisBrowser = isUrlAttempt;
        if(canItBeOpenedInThisBrowser && (packageMatchesThisBrowser || areSuggestedAppsOnlyBrowsers)){
            return false; // allow the url to load normally in the current web view
        }else {
            // Else we have a dedicated app link (e.g. tel://, whatsapp://, intent://) or app supported links like (e.g. https://youtube.com/...)
            context.startActivity(intent);
            return true; // Launched the activity successfully so block webview from loading
        }
    } else {
        // ...
    }
}

我想到了这个,让我知道它是否有效我目前无法测试它。

/**
 * Assumes that a browser is identified by having a IntentFilter match all http and https
 * protocols without matching either a host or path in the url.
 */
public boolean checkIfOtherBrowser(Context context, ResolveInfo info, Intent intent) {
    if (!intent.getScheme().equals("http") || !intent.getScheme().equals("https")) {
        return false;
    }

    IntentFilter filter = info.filter;
    if (filter != null) {
        int result = filter.match(
                context.getContentResolver(),
                intent,
                true,
                "somelogtag"
        );

        return (result & IntentFilter.MATCH_CATEGORY_SCHEME) > 0 && // Scheme was matched
                (result & IntentFilter.MATCH_CATEGORY_HOST) == 0 &&
                (result & IntentFilter.MATCH_CATEGORY_PATH) == 0; // But not for a specific host or path
    }

    return false;
}

使用的研究资源:

感谢 JensV,我想我想出了一个可行的解决方案。至少它似乎能够区分链接专用应用程序(Instagram、维基百科等)与其他浏览器等通用应用程序。

public boolean areResolvedActivitiesOnlyBrowsers(Context context, Intent uriIntent) {
    PackageManager packageManager = context.getPackageManager();
    List<ResolveInfo> resolvedActivityList;

    final String scheme = uriIntent.getScheme();
    if (scheme == null || (!scheme.equals("http") && !scheme.equals("https"))) {
        return false;
    }

    // Find all activities that could open the URI intent
    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        resolvedActivityList = packageManager.queryIntentActivities(uriIntent, PackageManager.MATCH_ALL);
    }else{
        resolvedActivityList = packageManager.queryIntentActivities(uriIntent, 0);
    }

    // Check each activity for a match with the path part of the URI, if
    for (ResolveInfo activityResolveInfo : resolvedActivityList) {
        final int match = activityResolveInfo.match;
        boolean matchesPath = (match & IntentFilter.MATCH_CATEGORY_PATH) > 0;
        if(matchesPath){
            return false;
        }
    }
    return true;
}