Android 11에서 맞춤 탭 사용

Android 11에서는 사용자가 기기에 설치한 다른 앱과 앱이 상호작용하는 방식이 변경되었습니다. 변경사항에 관한 자세한 내용은 Android 문서를 참고하세요.

맞춤 탭을 사용하는 Android 앱이 SDK 수준 30 이상을 타겟팅하는 경우 일부 변경이 필요할 수 있습니다. 이 도움말에서는 이러한 앱에 필요할 수 있는 변경사항을 설명합니다.

가장 간단한 경우 맞춤 탭은 다음과 같이 한 줄로 시작할 수 있습니다.

new CustomTabsIntent.Builder().build()
        .launchUrl(this, Uri.parse("https://www.example.com"));

이 접근 방식을 사용하여 애플리케이션을 실행하거나 툴바 색상 변경, 작업 버튼 추가와 같은 UI 맞춤설정을 추가하는 애플리케이션은 애플리케이션을 변경하지 않아도 됩니다.

네이티브 앱 선호

하지만 권장사항을 따랐다면 일부 변경이 필요할 수 있습니다.

첫 번째 관련 권장사항은 맞춤 탭을 처리할 수 있는 앱이 설치된 경우 애플리케이션에서 맞춤 탭 대신 인텐트를 처리하는 네이티브 앱을 사용해야 하는 것입니다.

Android 11 이상에서

Android 11에서는 새로운 인텐트 플래그 FLAG_ACTIVITY_REQUIRE_NON_BROWSER가 도입되었습니다. 이 플래그는 앱이 패키지 관리자 쿼리를 선언할 필요가 없기 때문에 네이티브 앱을 열어볼 때 권장되는 방법입니다.

static boolean launchNativeApi30(Context context, Uri uri) {
    Intent nativeAppIntent = new Intent(Intent.ACTION_VIEW, uri)
            .addCategory(Intent.CATEGORY_BROWSABLE)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                    Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
    try {
        context.startActivity(nativeAppIntent);
        return true;
    } catch (ActivityNotFoundException ex) {
        return false;
    }
}

해결 방법은 인텐트를 실행하고 FLAG_ACTIVITY_REQUIRE_NON_BROWSER를 사용하여 실행할 때 브라우저를 방지하도록 Android에 요청하는 것입니다.

이 인텐트를 처리할 수 있는 네이티브 앱을 찾을 수 없는 경우 ActivityNotFoundException이 발생합니다.

Android 11 이전

애플리케이션이 Android 11 또는 API 수준 30을 타겟팅할 수 있더라도 이전 Android 버전은 FLAG_ACTIVITY_REQUIRE_NON_BROWSER 플래그를 이해하지 못하므로 이러한 경우 패키지 관리자를 쿼리해야 합니다.

private static boolean launchNativeBeforeApi30(Context context, Uri uri) {
    PackageManager pm = context.getPackageManager();

    // Get all Apps that resolve a generic url
    Intent browserActivityIntent = new Intent()
            .setAction(Intent.ACTION_VIEW)
            .addCategory(Intent.CATEGORY_BROWSABLE)
            .setData(Uri.fromParts("http", "", null));
    Set<String> genericResolvedList = extractPackageNames(
            pm.queryIntentActivities(browserActivityIntent, 0));

    // Get all apps that resolve the specific Url
    Intent specializedActivityIntent = new Intent(Intent.ACTION_VIEW, uri)
            .addCategory(Intent.CATEGORY_BROWSABLE);
    Set<String> resolvedSpecializedList = extractPackageNames(
            pm.queryIntentActivities(specializedActivityIntent, 0));

    // Keep only the Urls that resolve the specific, but not the generic
    // urls.
    resolvedSpecializedList.removeAll(genericResolvedList);

    // If the list is empty, no native app handlers were found.
    if (resolvedSpecializedList.isEmpty()) {
        return false;
    }

    // We found native handlers. Launch the Intent.
    specializedActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(specializedActivityIntent);
    return true;
}

여기에서 사용된 접근 방식은 일반 http 인텐트를 지원하는 애플리케이션의 패키지 관리자를 쿼리하는 것입니다. 이러한 애플리케이션은 브라우저일 가능성이 높습니다.

그런 다음 실행하려는 특정 URL의 인텐트를 처리하는 애플리케이션을 쿼리합니다. 이렇게 하면 해당 URL을 처리하도록 브라우저와 네이티브 애플리케이션 설정이 모두 반환됩니다.

이제 두 번째 목록에서 첫 번째 목록에 있는 모든 브라우저를 삭제하면 네이티브 앱만 남게 됩니다.

목록이 비어 있으면 네이티브 핸들러가 없다는 것을 알고 false를 반환합니다. 그렇지 않으면 네이티브 핸들러의 인텐트를 실행합니다.

요약 정리

상황별로 적절한 방법을 사용해야 합니다.

static void launchUri(Context context, Uri uri) {
    boolean launched = Build.VERSION.SDK_INT >= 30 ?
            launchNativeApi30(context, uri) :
            launchNativeBeforeApi30(context, uri);

    if (!launched) {
        new CustomTabsIntent.Builder()
                .build()
                .launchUrl(context, uri);
    }
}

Build.VERSION.SDK_INT는 필요한 정보를 제공합니다. 30보다 크거나 같으면 Android는 FLAG_ACTIVITY_REQUIRE_NON_BROWSER를 인식하므로 새로운 접근 방식으로 나티바 앱을 실행해 볼 수 있습니다. 그렇지 않다면 이전 접근 방식으로 출시하려고 합니다.

네이티브 앱 실행에 실패하면 맞춤 탭을 실행합니다.

이 권장사항에는 상용구가 사용됩니다. Google은 라이브러리의 복잡성을 캡슐화하여 이를 더 간단하게 만들기 위해 노력하고 있습니다 android-browser-helper 지원 라이브러리의 업데이트 소식을 기다려 주세요.

맞춤 탭을 지원하는 브라우저 감지

또 다른 일반적인 패턴은 PackageManager를 사용하여 기기에서 맞춤 탭을 지원하는 브라우저를 감지하는 것입니다. 이에 관한 일반적인 사용 사례는 앱 명확성 대화상자를 피하기 위해 인텐트에서 패키지를 설정하거나 맞춤 탭 서비스에 연결할 때 연결할 브라우저를 선택하는 것입니다.

API 수준 30을 타겟팅할 때 개발자는 Android 매니페스트에 쿼리 섹션을 추가하여 브라우저를 맞춤 탭 지원과 일치시키는 인텐트 필터를 선언해야 합니다.

<queries>
    <intent>
        <action android:name=
            "android.support.customtabs.action.CustomTabsService" />
    </intent>
</queries>

마크업이 있으면 맞춤 탭을 지원하는 브라우저를 쿼리하는 데 사용되는 기존 코드가 예상대로 작동합니다.

자주 묻는 질문(FAQ)

Q: 맞춤 탭 제공자를 찾는 코드는 https:// 인텐트를 처리할 수 있는 애플리케이션을 쿼리하지만 쿼리 필터는 android.support.customtabs.action.CustomTabsService 쿼리만 선언합니다. https:// 인텐트의 쿼리를 선언하면 안 되나요?

A: 쿼리 필터를 선언할 때 쿼리 자체가 아니라 쿼리에 대한 응답을 PackageManager에 대한 응답을 필터링합니다. 맞춤 탭을 지원하는 브라우저는 CustomTabsService 처리를 선언하므로 필터링되지 않습니다. 맞춤 탭을 지원하지 않는 브라우저는 필터링됩니다.

결론

여기까지가 기존 맞춤 탭 통합을 Android 11과 호환되도록 조정하는 데 필요한 모든 변경사항입니다. Android 앱에 맞춤 탭을 통합하는 방법을 자세히 알아보려면 구현 가이드로 시작한 다음 권장사항을 확인하여 최고 수준의 통합 구축에 관해 알아보세요.

궁금한 점이나 의견이 있으면 알려주세요.