Android Chrome 自定义标签回退

标签 android android-webview android-support-library chrome-custom-tabs

我正在实现后备 chrome 自定义标签

我指的是几个链接 it has some自定义回退实现。我不明白为什么需要它。

我做了以下处理后备并且工作正常。

  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);
    }

知道为什么处理回退需要如此复杂的实现吗?

使用以下版本的支持库编译'com.android.support:customtabs:25.3.1'

最佳答案

如果你看透CustomTabsIntent的源码,它只不过是创建一个普通的使用Intent.ACTION_VIEW打开URL的隐式intent的 helper 。它可以帮助您使用 Chrome 识别的特定键向 Intent 添加额外数据,Chrome 稍后会使用这些键来呈现自定义 UI。

这是来自the official page的解释:

Custom Tabs uses an ACTION_VIEW Intent with key Extras to customize the UI. This means that by default the page willopen in the system browser, or the user's default browser.

If the user has Chrome installed and it is the default browser, it will automatically pick up the EXTRAS and present a customized UI. It is also possible for another browser to use the Intent extras to provide a similar customized interface.

对于您链接上的解决方案,源代码取自here .从 CustomTabActivityHelper#openCustomTab 可以看出,首先它会寻找支持自定义标签的应用程序。如果可用,则启动 CustomTabsIntent 描述的隐式 Intent 。如果没有,请打开 WebViewActivity

如何确定是否有任何应用支持自定义标签?您可以在 CustomTabsHelper.getPackageNameToUse 上找到它。首先,它将解析所有可以使用 Intent.ACTION_VIEW 打开 URL 的应用程序。然后,它将检查该应用程序是否支持自定义选项卡。

然后,

  • 如果没有可用的应用,返回null
  • 如果只有 1 个应用程序可用,请将其退回。
  • 如果有 1 个以上的应用程序可用,并且其中 1 个是默认应用程序,则返回它。
  • 如果有超过 1 个应用程序可用,其中 1 个是 Chrome,请退回它。
  • 否则返回null
  • (如果有超过 1 个应用程序可用,您可以添加一个逻辑来要求用户选择他们想要的任何浏览器)

现在,您的解决方案如何?

如果我们使用您的解决方案,如果没有应用可以处理由 CustomTabsIntent 创建的隐式 Intent ,WebviewActivity 将被打开,在这种情况下没有安装浏览器?如果我们有浏览器但都不支持自定义选项卡会怎样?您的应用仍会要求在浏览器中打开链接,而不是在 WebViewActivity 中。

请记住,CustomTabsIntent 只是一个帮助程序,用于创建一个普通的隐式 Intent ,以使用 Intent.ACTION_VIEW 和各种 EXTRA 数据来打开 URL 以自定义 UI。如何自定义 UI 由浏览器处理。基本上,我认为我们可以在没有 CustomTabsIntent 的情况下自行创建并启动使用自定义 UI 打开浏览器的 Intent 。不过我从来没有尝试过这个。

如果你想在任何浏览器中打开链接,不管浏览器是否支持自定义标签,链接在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;
    }
}

关于Android Chrome 自定义标签回退,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43864029/

相关文章:

android - 如何更改 PreferenceFragment 警报对话框的分隔线颜色

Android webview 应用程序和 Google Analytics

Android Sherlock Fragment webview 问题

android-listfragment - 在 Fragment 中的方向改变时保留 searchView 状态

android - 在 Android P 上创 build 置 Activity

java - 检查来自 Java 服务器的 Google Play 购买

android - Eclipse 中的 Android 模拟器问题

android - 窗口错误日志在 Eclipse 中不可见

android - 我如何为我的 Android 应用程序实现像 "auto-hide navigation"这样的 chrome?

android - 使用 TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse 时出错