简体   繁体   English

Android Chrome自定义标签回退

[英]Android Chrome custom tabs fallback

I am implementing fallback chrome custom tabs 我正在实施fallback chrome自定义标签

I was referring to few links it has some custom fallback implementation. 我指的是它有一些自定义回退实现的链接。 I didn't get why it is required. 我不知道为什么需要它。

I did following for handling fallback and is working fine. 我做了以下处理后备并且工作正常。

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

Any idea why such complex implementation is required for handling fallback? 知道为什么处理回退需要这么复杂的实现吗?

using following version of support library compile 'com.android.support:customtabs:25.3.1' 使用以下版本的支持库compile 'com.android.support:customtabs:25.3.1'

If you see through the source code of CustomTabsIntent , it's nothing more than just a helper to create a normal implicit intent to open a URL using Intent.ACTION_VIEW . 如果您看到CustomTabsIntent的源代码,它只不过是一个帮助器来创建使用Intent.ACTION_VIEW打开URL的常规隐式意图。 It helps you to add extra data to the intent with the specific keys recognized by Chrome which later be used by Chrome to present a customized UI. 它可以帮助您使用Chrome识别的特定键向意图添加额外数据,Chrome随后会使用这些键来呈现自定义UI。

Here is the explanation from the official page : 以下是官方网页的解释:

Custom Tabs uses an ACTION_VIEW Intent with key Extras to customize the UI. 自定义选项卡使用ACTION_VIEW Intent和关键Extras来自定义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. 如果用户安装了Chrome并且它是默认浏览器,它将自动获取EXTRAS并显示自定义UI。 It is also possible for another browser to use the Intent extras to provide a similar customized interface. 另一个浏览器也可以使用Intent附加功能来提供类似的自定义界面。

For the solution on your link, the source code taken from here . 对于链接上的解决方案,源代码取自此处 As you can see from CustomTabActivityHelper#openCustomTab , first it will look for the app that support Custom Tabs. 正如您在CustomTabActivityHelper#openCustomTab看到的,首先它将查找支持自定义选项卡的应用程序。 If it is available, then start the implicit intent described by CustomTabsIntent . 如果可用,则启动CustomTabsIntent描述的隐式intent。 If not, open the WebViewActivity . 如果没有,请打开WebViewActivity

How to find out if any app support Custom Tabs? 如何找出是否有任何应用支持自定义标签? You can se it on CustomTabsHelper.getPackageNameToUse . 您可以在CustomTabsHelper.getPackageNameToUse At first, it will resolve all apps that can open a URL using Intent.ACTION_VIEW . 首先,它将解析所有可以使用Intent.ACTION_VIEW打开URL的应用程序。 Then, it will check if that apps support Custom Tabs. 然后,它将检查该应用是否支持自定义标签。

Then, 然后,

  • If no apps available, return null 如果没有可用的应用,则返回null
  • If only 1 app available, return it. 如果只有1个应用程序可用,请将其退回
  • If more than 1 app available and 1 of it is the default app, return it. 如果有超过1个应用程序且其中1个是默认应用程序,请将其返回。
  • If more than 1 app available and 1 of it is Chrome, return it. 如果有超过1个可用应用,其中1个是Chrome,请将其退回。
  • Else, return null 否则,返回null
  • (If more than 1 app available, you can put a logic to ask a user to choose any browser they want) (如果有超过1个应用程序可用,您可以设置一个逻辑来要求用户选择他们想要的任何浏览器)

Now, how about your solution? 现在,您的解决方案如何?

If we use your solution, WebviewActivity will be opened if no apps can handle implicit intent created by CustomTabsIntent , in this case no browser installed? 如果我们使用您的解决方案,如果没有应用程序可以处理CustomTabsIntent创建的隐式意图,则会打开WebviewActivity ,在这种情况下没有安装浏览器? What happened if we have browsers and none of it support Custom Tabs? 如果我们有浏览器并且它们都不支持自定义标签会发生什么? Your app will still ask to open the link in the browser and not in WebViewActivity . 您的应用仍会要求在浏览器中打开链接,而不是在WebViewActivityWebViewActivity

Remember, CustomTabsIntent is just a helper to create a normal implicit intent to open a URL using Intent.ACTION_VIEW with various EXTRA data to customized the UI. 请记住, CustomTabsIntent只是一个帮助器,可以创建一个正常的隐式意图,使用Intent.ACTION_VIEW打开一个URL,并使用各种EXTRA数据来自定义UI。 How to customized the UI is handled by the browsers. 如何自定义UI由浏览器处理。 Basically, I think we can create and start the intent to open the browser with customized UI by yourself without CustomTabsIntent . 基本上,我认为我们可以创建并启动使用自定义UI打开浏览器的意图,而无需CustomTabsIntent I never try this one though. 我从来没有试过这个。

If you want the link to be opened in any browser , no matter the browser support Custom Tabs or not, and the link to be opened in WebViewActivity if no apps available , your solution solve it even though it's not the best solution I guess. 如果您希望在任何浏览器中打开链接,无论浏览器是否支持自定义选项卡,以及在没有可用应用程序的情况下WebViewActivity打开的链接,您的解决方案即使不是我认为的最佳解决方案也能解决它。

But, if you want the link to be opened in the browser that support Custom Tabs , and the link to be opened in WebViewActivity if no apps that support Custom Tabs available , his solution is the right one. 但是,如果您希望在支持自定义选项卡浏览器中打开链接,并且如果没有支持自定义选项卡的应用程序可以在WebViewActivity打开链接,则他的解决方案是正确的。

But, if what you want is just providing fallback mechanism, it should not be that complicated. 但是,如果您想要的只是提供回退机制,那就不应该那么复杂了。 Here is the simpler code: 这是更简单的代码:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM