태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.
2019. 1. 26. 11:30


[android] Chrome Custom Tabs


https://labs.ribot.co.uk/exploring-chrome-customs-tabs-on-android-ef427effe2f4


action button, ACTION_CUSTOM_TABS_CONNECTION, bindCustomTabsService, Builder, Callback, chrome browser cookie share, chrome browser permission share, chrome custom tab, chrome custom tab dependency, close icon, Cookie, custom tab, custom tab bind service, custom tab gradle, custom tabs, CustomTabService, CustomTabsIntent, data saver, enter animation, exit animation, external browser, internal chrome webview, internal webview, menu item, permission, Pre-loading, pre-warming, support library, toolbar customize, [android] Chrome Custom Tabs


-

기존에 특정 web page 를 보여주기 위해서는 external browser 에 의존하거나 internal WebView 를 활용하는 방법만 있었다.

그러나 “Custom Tabs” 라는 것이 support library 에 생겨나면서 external browser (chrome) 의 호환성을 갖추면서 internal 의 효과를 낼 수 있게 되었다.



-

Chrome custom tabs 를 통해 web page 를 로드하는 것은 기존의 external browser 나 WebView 에 비해 약 2배정도 빠르다.


action button, ACTION_CUSTOM_TABS_CONNECTION, bindCustomTabsService, Builder, Callback, chrome browser cookie share, chrome browser permission share, chrome custom tab, chrome custom tab dependency, close icon, Cookie, custom tab, custom tab bind service, custom tab gradle, custom tabs, CustomTabService, CustomTabsIntent, data saver, enter animation, exit animation, external browser, internal chrome webview, internal webview, menu item, permission, Pre-loading, pre-warming, support library, toolbar customize, [android] Chrome Custom Tabs


-

Chrome custom tabs 은 다음과 같은 기능을 제공한다.


    Toolbar 영역을 customize 할 수 있다.

        색상을 지정할 수 있다. ( 글자색을 지정할 수 없는 것은 안타깝다. )

        Action Button 1개를 추가할 수 있다.

        Menu Item 을 여러 개 추가할 수 있다.

        Close Icon (Toolbar 최좌측) 을 지정할 수 있다.


    Enter, Exit animation 을 지정할 수 있다.


    pre-warming 이라고 부르는 일종의 pre-loading 을 통해 훨씬 빠르게 web page 를 유저에게 보여줄 수 있다.


    Chrome browser 와 cookie 와 permission 을 공유하기 때문에 이미 연결된 사이트(예를 들어 자동 로그인으로 연결된 사이트)에 재연결(재로그인) 할 필요가 없다.


    Data Saver 가 켜져 있어도 이 녀석을 이용하는 데 문제 없다.


    Activity 에서는 Chrome custom tab 에서 전달하는 Callback 을 받을 수 있다.



-

우선 이 녀석을 활용하기 위해서는 dependency 정의가 필요하다.

compile 'com.android.support:customtabs:23.0.0’



-

Chrome tab 을 지원하는 package 가 존재하는지 query 가 필요하다

이 때 ACTION_VIEW 도 처리할 수 있으면서 CustomTabService.ACTION_CUSTOM_TABS_CONNECTION 도 처리할 수 있는 녀석이 custom tab 을 지원하는 chrome 이라고 볼 수 있다.

Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>(); 
for (ResolveInfo info : resolvedActivityList) { 
    Intent serviceIntent = new Intent(); 
    serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION); 
    serviceIntent.setPackage(info.activityInfo.packageName); 
    if (pm.resolveService(serviceIntent, 0) != null) { 
        packagesSupportingCustomTabs.add(info.activityInfo.packageName); 
    } 
}


query 후에 지원하는 activity intent 가 여러개일 경우 선별이 필요한데, 이 글의 필자는 다음과 같은 선별규칙을 거친다.


String packageNameToUser = null
if (packagesSupportingCustomTabs.isEmpty()) {
    packageNameToUser = null; 
} else if (packagesSupportingCustomTabs.size() == 1) { 
    packageNameToUser = packagesSupportingCustomTabs.get(0); 
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName) // defaultViewHandlerPackageName 는 pm.resolveActivity 로 query 한 기본 activity
    && !hasSpecializedHandlerIntents(context, activityIntent) // authority 나 path scheme 이 정의되었는지 확인
    && packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) { 
    packageNameToUser = defaultViewHandlerPackageName; 
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) { // com.android.chrome
    packageNameToUser = STABLE_PACKAGE; 
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) { // com.chrome.beta
    packageNameToUser = BETA_PACKAGE; 
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) { // com.chrome.dev
    packageNameToUser = DEV_PACKAGE; 
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) { // com.google.android.apps.chrome
    packageNameToUser = LOCAL_PACKAGE; 
}



-

Custom tab 을 지원하는 녀석은 bind 할 수 있는 service 를 갖는다.

보통 Activity 의 onStart 에서 bind 를 하고, onStop 에서 unbind 를 한다.

public void bindCustomTabsService(Activity activity) {
    if (mClient != null) return; // service binding 시 전달되는 binder 역할의 연결체, 이미 연결되었다는 의미

    String packageName = CustomTabsHelper.getPackageNameToUse(activity); // 위에서 설명한 custom tab 지원하는 packageName 을 얻어오는 과정
    if (packageName == null) return;

    mConnection = new CustomTabsServiceConnection() {
        @Override
        public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
            mClient = client;
            mClient.warmup(0L); // pre-load web page
            if (mConnectionCallback != null){ // 호출하는 activity 에서 
                 mConnectionCallback.onCustomTabsConnected();
            }
            // Initialize a session as soon as possible.
            getSession();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mClient = null;
            if (mConnectionCallback != null){
                mConnectionCallback.onCustomTabsDisconnected();
            }
        }
    };
    CustomTabsClient.bindCustomTabsService(activity, packageName, mConnection);
}

public void unbindCustomTabsService(Activity activity) {
    if (mConnection == null) return;
    activity.unbindService(mConnection);
    mClient = null;
    mCustomTabsSession = null;
}

public CustomTabsSession getSession() {
    if (mClient == null) {
        mCustomTabsSession = null;
    } else if (mCustomTabsSession == null) {
        mCustomTabsSession = mClient.newSession(null);
    }
    return mCustomTabsSession;
}



-

Service 가 연결이 된 후에는 Custom tab 을 열 수 있다.

private void openCustomTab() {
    CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder(); // CustomTabsIntent 는 support

    // titlebar show
    intentBuilder.setShowTitle(true);

    // titlebar color - color, 글자색을 바꿀 수 없는 것은 조금 아쉽다
    int color = getResources().getColor(R.color.primary);
    intentBuilder.setToolbarColor(color);

    // menu items 추가 - label, pendingIntent
    String menuItemTitle = getString(R.string.menu_title_share);
    PendingIntent menuItemPendingIntent = createPendingShareIntent();
    intentBuilder.addMenuItem(menuItemTitle, menuItemPendingIntent);
    String menuItemEmailTitle = getString(R.string.menu_title_email);
    PendingIntent menuItemPendingIntentTwo = createPendingEmailIntent();
    intentBuilder.addMenuItem(menuItemEmailTitle, menuItemPendingIntentTwo);

    // close btn - icon
    intentBuilder.setCloseButtonIcon(mCloseButtonBitmap);

    // action btn - icon, label, pendingIntent
    intentBuilder.setActionButton(mActionButtonBitmap, getString(R.string.menu_title_share), createPendingShareIntent());

    // enter & exit animation - 기본은 bottom to top animation 이다
    intentBuilder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left);
    intentBuilder.setExitAnimations(this, android.R.anim.slide_in_left, android.R.anim.slide_out_right);

    // WebViewFallback 은 custom tab 을 지원하는 app 이 없을 때 처리하는 내용을 담고 있다
    CustomTabActivityHelper.openCustomTab(
            this, intentBuilder.build(), Uri.parse(URL_ARGOS), new WebviewFallback());
}

public static void openCustomTab(Activity activity,
                                 CustomTabsIntent customTabsIntent,
                                 Uri uri,
                                 CustomTabFallback fallback) {
    String packageName = CustomTabsHelper.getPackageNameToUse(activity);

    //If we cant find a package name, it means there's no browser that supports
    //Chrome Custom Tabs installed. So, we fallback to the webview
    if (packageName == null) {
        if (fallback != null) {
            fallback.openUri(activity, uri);
        }
    } else {
        customTabsIntent.intent.setPackage(packageName);
        customTabsIntent.launchUrl(activity, uri);
    }
}



-

해당 article 에서는 service 에 binding 해서 하는 일이 warmup 을 밖에 없는데.. service binding 되지 않아도 launch 하는 데 문제가 없는지는 테스트가 필요하다. 예상으로는 warm-up 을 하지 않으면 속도는 똑같이 느릴 것이라 보이며, launch 하는 데는 문제가 없을 것 같다.



-

끝!




댓글을 달아 주세요


Posted by 돼지왕왕돼지