安卓Chrome自定义标签回退

5

我正在实现 Chrome 自定义选项卡的回退功能。

我参考了一些链接,其中有一些自定义回退的实现。但我不明白为什么需要这样做。

我已经采取以下措施来处理回退,并且运作良好。

  try {
        CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
        builder.setToolbarColor(ContextCompat.getColor(context, R.color.appthemecolor));
        CustomTabsIntent customTabsIntent = builder.build();
        customTabsIntent.launchUrl(context, Uri.parse(url));
    } catch (ActivityNotFoundException e) {
        e.printStackTrace();
        Intent intent = new Intent(context, WebviewActivity.class);
        intent.putExtra(WebviewActivity.EXTRA_URL, url);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        context.startActivity(intent);
    }

有没有想过为什么需要如此复杂的实现来处理回退?使用以下版本的支持库compile 'com.android.support:customtabs:25.3.1'
1个回答

10
如果你查看CustomTabsIntent的源代码,它本质上只是一个创建使用Intent.ACTION_VIEW打开URL的常规隐式意图的助手。它帮助您向意图添加额外的数据,这些数据具有Chrome识别的特定键,稍后由Chrome用于呈现自定义UI。
以下是来自官方页面的解释:
Custom Tabs使用具有关键附加项的ACTION_VIEW意图来自定义UI。这意味着默认情况下,页面将在系统浏览器或用户的默认浏览器中打开。
如果用户已安装Chrome并且它是默认浏览器,则会自动拾取EXTRAS并呈现自定义UI。还可以让另一个浏览器使用Intent extras来提供类似的自定义界面。
对于你链接中的解决方案,源代码来自这里。正如你从CustomTabActivityHelper#openCustomTab中看到的,首先它会查找支持Custom Tabs的应用程序。如果可用,则启动由CustomTabsIntent描述的隐式意图。如果没有,则打开WebViewActivity
如何找出是否有应用程序支持Custom Tabs?您可以在CustomTabsHelper.getPackageNameToUse中查看它。首先,它将解析所有可以使用Intent.ACTION_VIEW打开URL的应用程序。然后,它将检查这些应用程序是否支持Custom Tabs。
然后,
- 如果没有可用的应用,则返回null - 如果只有一个可用的应用程序,则返回该应用程序。 - 如果有多个可用的应用程序,其中一个是默认应用程序,则返回该应用程序。 - 如果有多个可用的应用程序,并且其中一个是Chrome,则返回该应用程序。 - 否则,返回null - (如果有多个应用程序可用,则可以放置逻辑以要求用户选择他们想要的任何浏览器)
那么,你的解决方案怎么样呢?
如果没有应用程序能处理由CustomTabsIntent创建的隐式意图,则会打开WebviewActivity,在这种情况下没有安装浏览器?如果我们有浏览器,但没有一个支持Custom Tabs,会发生什么?你的应用程序仍然会要求在浏览器而不是WebViewActivity中打开链接。

请记住,CustomTabsIntent只是一个帮助程序,用于创建一个普通的隐式意图,使用Intent.ACTION_VIEW打开URL,并使用各种额外数据来自定义UI。如何自定义UI由浏览器处理。基本上,我认为我们可以自己创建并启动意图,以打开具有自定义UI的浏览器,而无需使用CustomTabsIntent。虽然我从未尝试过这个。

如果您希望链接在任何浏览器中打开,而不管浏览器是否支持自定义选项卡,并且在没有可用应用程序时在WebViewActivity中打开链接,则您的解决方案可以解决问题,即使我认为它不是最佳解决方案。

但是,如果您想要在支持自定义选项卡的浏览器中打开链接,并且在没有支持自定义选项卡的应用程序可用时在WebViewActivity中打开链接,则他的解决方案是正确的。

但是,如果您只是想提供备用机制,那么代码就不必那么复杂。这里是更简单的代码:

public class CustomTabs {

    private static final String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";

    private static final String STABLE_PACKAGE = "com.android.chrome";
    private static final String BETA_PACKAGE = "com.chrome.beta";
    private static final String DEV_PACKAGE = "com.chrome.dev";
    private static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";

    public static void openTab(Context context, String url) {
        CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();

        /* do some UI customization here */

        CustomTabsIntent customTabsIntent = builder.build();

        String packageName = getPackageNameToUse(context);

        if (packageName == null) {
            Intent intent = new Intent(context, WebviewActivity.class);
            intent.putExtra(WebviewActivity.EXTRA_URL, url);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

            context.startActivity(intent);
        } else {
            customTabsIntent.intent.setPackage(packageName);
            customTabsIntent.launchUrl(context, Uri.parse(url));
        }
    }

    private static String getPackageNameToUse(Context context) {
        String packageNameToUse = null;

        PackageManager pm = context.getPackageManager();

        Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));

        ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);

        String defaultViewHandlerPackageName = null;
        if (defaultViewHandlerInfo != null) {
            defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
        }

        List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);

        List<String> packagesSupportingCustomTabs = new ArrayList<>();
        for (ResolveInfo info : resolvedActivityList) {
            Intent serviceIntent = new Intent();
            serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
            serviceIntent.setPackage(info.activityInfo.packageName);
            if (pm.resolveService(serviceIntent, 0) != null) {
                packagesSupportingCustomTabs.add(info.activityInfo.packageName);
            }
        }

        if (packagesSupportingCustomTabs.isEmpty()) {
            packageNameToUse = null;
        } else if (packagesSupportingCustomTabs.size() == 1) {
            packageNameToUse = packagesSupportingCustomTabs.get(0);
        } else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
                && !hasSpecializedHandlerIntents(context, activityIntent)
                && packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
            packageNameToUse = defaultViewHandlerPackageName;
        } else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
            packageNameToUse = STABLE_PACKAGE;
        } else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
            packageNameToUse = BETA_PACKAGE;
        } else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
            packageNameToUse = DEV_PACKAGE;
        } else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
            packageNameToUse = LOCAL_PACKAGE;
        }
        return packageNameToUse;
    }

    private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
        try {
            PackageManager pm = context.getPackageManager();
            List<ResolveInfo> handlers = pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER);

            if (handlers == null || handlers.size() == 0) {
                return false;
            }

            for (ResolveInfo resolveInfo : handlers) {
                IntentFilter filter = resolveInfo.filter;
                if (filter == null) continue;
                if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
                if (resolveInfo.activityInfo == null) continue;
                return true;
            }

        } catch (RuntimeException e) {
            Log.e("LOG", "Runtime exception while getting specialized handlers");
        }

        return false;
    }
}

嗨,感谢您的帖子!但是LOCAL_PACKAGE和STABLE_PACKAGE之间有什么区别?为什么允许用户在这里使用LOCAL_PACKAGE? - Jiechao Wang
@JiechaoWang 老实说,我不太清楚。这段代码来自Chrome团队提供的示例代码。STABLE_PACKAGE很明显,是我们通常使用的Chrome标准版本。至于LOCAL_PACKAGE,从包名来看,我认为它是系统附带的应用程序。 - marcelljee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接