Sử dụng Thẻ tuỳ chỉnh trên Android 11

Android 11 đã giới thiệu các thay đổi về cách ứng dụng có thể tương tác với ứng dụng khác mà người dùng đã cài đặt trên thiết bị. Bạn có thể đọc thêm về những thay đổi đó trong tài liệu về Android.

Khi một ứng dụng Android sử dụng Thẻ tuỳ chỉnh nhắm đến SDK cấp 30 trở lên, một số thay đổi có thể cần thiết. Bài viết này đề cập đến những thay đổi có thể cần thiết đối với những ứng dụng đó.

Trong trường hợp đơn giản nhất, bạn có thể khởi chạy Thẻ tuỳ chỉnh bằng một lớp lót như sau:

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

Các ứng dụng chạy ứng dụng bằng phương pháp này hoặc thậm chí thêm các cách tuỳ chỉnh giao diện người dùng như thay đổi màu thanh công cụ, thêm nút hành động sẽ không cần thực hiện bất kỳ thay đổi nào trong ứng dụng.

Ưu tiên ứng dụng gốc

Tuy nhiên, nếu làm theo các phương pháp hay nhất, bạn có thể phải thực hiện một số thay đổi.

Phương pháp hay nhất đầu tiên là các ứng dụng nên ưu tiên ứng dụng gốc xử lý ý định thay vì Thẻ tuỳ chỉnh nếu một ứng dụng có khả năng xử lý ý định đó được cài đặt.

Trên Android 11 trở lên

Android 11 ra mắt cờ Ý định mới (FLAG_ACTIVITY_REQUIRE_NON_BROWSER). Đây là cách được đề xuất để thử mở ứng dụng gốc vì không yêu cầu ứng dụng khai báo bất kỳ truy vấn trình quản lý gói nào.

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

Giải pháp là cố gắng chạy Ý định và sử dụng FLAG_ACTIVITY_REQUIRE_NON_BROWSER để yêu cầu Android tránh các trình duyệt khi khởi chạy.

Nếu không tìm thấy một ứng dụng gốc có khả năng xử lý Ý định này, thì hệ thống sẽ gửi ActivityNotFoundException.

Trước Android 11

Mặc dù ứng dụng có thể nhắm đến Android 11 hoặc API cấp 30, nhưng các phiên bản Android trước sẽ không hiểu được cờ FLAG_ACTIVITY_REQUIRE_NON_BROWSER, vì vậy, chúng ta cần dùng đến việc truy vấn Trình quản lý gói trong những trường hợp đó:

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

Phương pháp được sử dụng ở đây là truy vấn Trình quản lý gói cho các ứng dụng hỗ trợ ý định http chung. Những ứng dụng đó có thể là trình duyệt.

Sau đó, truy vấn các ứng dụng xử lý tương ứng cho URL cụ thể mà chúng ta muốn khởi chạy. Thao tác này sẽ trả về chế độ thiết lập của cả trình duyệt và ứng dụng gốc để xử lý URL đó.

Bây giờ, hãy xoá mọi trình duyệt có trong danh sách đầu tiên khỏi danh sách thứ hai, và chúng ta sẽ chỉ còn các ứng dụng gốc.

Nếu danh sách này trống, tức là không có trình xử lý gốc và trả về giá trị false. Nếu không, chúng tôi sẽ khởi chạy ý định cho trình xử lý gốc.

Kết hợp kiến thức đã học

Chúng ta cần đảm bảo sử dụng đúng phương thức cho từng tình huống:

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 cung cấp thông tin mà chúng tôi cần. Nếu giá trị bằng hoặc lớn hơn 30, Android sẽ biết FLAG_ACTIVITY_REQUIRE_NON_BROWSER và chúng ta có thể thử chạy ứng dụng nativa theo phương pháp mới. Nếu không, chúng ta sẽ thử khởi chạy theo cách tiếp cận cũ.

Nếu không chạy được ứng dụng gốc thì chúng tôi sẽ khởi chạy Thẻ tuỳ chỉnh.

Có một số mẫu nguyên mẫu áp dụng cho phương pháp hay nhất này. Chúng tôi đang nỗ lực để đơn giản hoá việc này bằng cách tóm tắt sự phức tạp trong một thư viện. Hãy chú ý theo dõi thông tin cập nhật cho thư viện hỗ trợ android-browser-helper.

Phát hiện các trình duyệt hỗ trợ Thẻ tuỳ chỉnh

Một mẫu phổ biến khác là sử dụng PackageManager để phát hiện những trình duyệt hỗ trợ Thẻ tuỳ chỉnh trên thiết bị. Một số trường hợp sử dụng phổ biến là thiết lập gói trên Intent (Ý định) để tránh hộp thoại phân định ứng dụng hoặc chọn trình duyệt để kết nối khi kết nối với dịch vụ Thẻ tuỳ chỉnh.

Khi nhắm đến API cấp 30, nhà phát triển sẽ phải thêm phần truy vấn vào Tệp kê khai Android, khai báo bộ lọc ý định khớp với các trình duyệt có hỗ trợ Thẻ tuỳ chỉnh.

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

Khi có thẻ đánh dấu, mã hiện có dùng để truy vấn các trình duyệt hỗ trợ Thẻ tuỳ chỉnh sẽ hoạt động như dự kiến.

Câu hỏi thường gặp

Hỏi: Mã tìm kiếm các truy vấn của nhà cung cấp Thẻ tuỳ chỉnh cho các ứng dụng có thể xử lý ý định https://, nhưng bộ lọc truy vấn chỉ khai báo một truy vấn android.support.customtabs.action.CustomTabsService. Không cần khai báo truy vấn cho ý định https://?

Đáp: Khi khai báo bộ lọc truy vấn, bộ lọc truy vấn sẽ lọc phản hồi cho một truy vấn theo PackageManager, chứ không lọc cho chính truy vấn đó. Vì các trình duyệt hỗ trợ Thẻ tuỳ chỉnh sẽ khai báo việc xử lý CustomTabsService, nên các trình duyệt đó sẽ không bị lọc ra. Các trình duyệt không hỗ trợ Thẻ tuỳ chỉnh sẽ bị lọc ra.

Kết luận

Đó là tất cả những thay đổi cần thiết để điều chỉnh quá trình tích hợp Thẻ tuỳ chỉnh hiện có sao cho hoạt động với Android 11. Để tìm hiểu thêm về cách tích hợp Thẻ tuỳ chỉnh vào một ứng dụng Android, hãy bắt đầu với hướng dẫn triển khai, sau đó xem các phương pháp hay nhất để tìm hiểu về cách xây dựng chế độ tích hợp cao cấp.

Hãy cho chúng tôi biết nếu bạn có câu hỏi hoặc ý kiến phản hồi!