Đáp ứng các trường hợp sử dụng phổ biến trong khi có chế độ hiển thị gói hạn chế

Tài liệu này trình bày một số ví dụ phổ biến về trường hợp sử dụng trong đó một ứng dụng tương tác với các ứng dụng khác. Mỗi phần sẽ có hướng dẫn về cách khai báo chế độ hiển thị gói của các ứng dụng khác đã cài đặt mà bạn cần xem xét nếu ứng dụng của mình nhắm đến Android 11 (API cấp 30) trở lên.

Khi ứng dụng của bạn nhắm đến Android 11 trở lên và sử dụng ý định để bắt đầu một hoạt động trong ứng dụng khác, thì phương pháp đơn giản nhất là gọi ý định và xử lý ngoại lệ ActivityNotFoundException nếu không có ứng dụng nào.

Nếu một phần ứng dụng của bạn phụ thuộc vào việc biết liệu lệnh gọi đến startActivity() có thể thành công hay không, chẳng hạn như hiển thị giao diện người dùng, hãy thêm một phần tử vào phần tử <queries> của tệp kê khai ứng dụng. Thông thường, phần tử mới này là một phần tử <intent>.

Mở URL

Phần này mô tả cách mở URL trong một ứng dụng nhắm đến Android 11 trở lên. Hãy làm theo một trong các ví dụ ở những tiểu mục sau đây, tuỳ thuộc vào cách ứng dụng của bạn mở URL.

Mở URL trong trình duyệt hoặc ứng dụng khác

Để mở URL, hãy sử dụng một ý định chứa thao tác theo ý định ACTION_VIEW, như mô tả trong phần về cách tải một URL web. Sau khi bạn gọi startActivity() bằng ý định này, một trong các trường hợp sau sẽ xảy ra:

  • URL sẽ mở trong một ứng dụng trình duyệt web.
  • URL sẽ mở trong một ứng dụng hỗ trợ URL ở dạng liên kết sâu.
  • Hộp thoại phân định sẽ xuất hiện, cho phép người dùng chọn ứng dụng sẽ mở URL.
  • ActivityNotFoundException xuất hiện vì không có ứng dụng nào được cài đặt trên thiết bị có thể mở URL. (Đây là điều bất thường.)

    Ứng dụng của bạn nên nắm bắt và xử lý ActivityNotFoundException nếu ngoại lệ đó xuất hiện.

Hành vi chế độ hiển thị gói của hệ thống không ảnh hưởng đến phương thức startActivity(). Vì thế, bạn không cần phải thêm phần tử <queries> vào tệp kê khai của ứng dụng hay thực hiện bất kỳ thay đổi nào đối với phần tử <queries> hiện có.

Kiểm tra xem có trình duyệt nào không

Trong một số trường hợp, ứng dụng của bạn nên xác minh rằng có ít nhất một trình duyệt trên thiết bị, hoặc rằng một trình duyệt cụ thể là trình duyệt mặc định, trước khi cố mở URL. Trong trường hợp đó, hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="https" />
</intent>

Khi bạn gọi queryIntentActivities() và chuyển một ý định trên web làm đối số, trong một số trường hợp, danh sách được trả về sẽ bao gồm các ứng dụng trình duyệt có sẵn. Danh sách này sẽ không bao gồm các ứng dụng trình duyệt nếu người dùng đã thiết lập URL để mở trong một ứng dụng không phải là trình duyệt theo mặc định.

Mở URL trong Thẻ tuỳ chỉnh

Thẻ tuỳ chỉnh cho phép một ứng dụng tuỳ chỉnh giao diện của trình duyệt. Bạn có thể mở một URL trong Thẻ tuỳ chỉnh mà không cần thêm hay thay đổi phần tử <queries> trong tệp kê khai ứng dụng.

Tuy nhiên, bạn nên kiểm tra xem thiết bị có một trình duyệt hỗ trợ Thẻ tuỳ chỉnh hay không, hoặc chọn một trình duyệt cụ thể để mở bằng Thẻ tuỳ chỉnh thông qua CustomTabsClient.getPackageName(). Trong những trường hợp đó, hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>

Cho phép các ứng dụng không phải là trình duyệt xử lý URL

Ngay cả khi ứng dụng của bạn có thể mở URL bằng Thẻ tuỳ chỉnh, bạn vẫn nên cho phép một ứng dụng không phải là trình duyệt mở một URL nhất định nếu có thể. Để cung cấp chức năng này trong ứng dụng của bạn, hãy thực hiện lệnh gọi đến startActivity() bằng cách sử dụng một ý định đặt cờ ý định FLAG_ACTIVITY_REQUIRE_NON_BROWSER. Nếu hệ thống trả về ActivityNotFoundException, thì ứng dụng của bạn có thể mở URL trong Thẻ tuỳ chỉnh.

Nếu một ý định có cờ này, lệnh gọi đến startActivity() sẽ trả về ActivityNotFoundException khi một trong hai điều kiện sau đây xảy ra:

  • Lệnh gọi đã chạy trực tiếp một ứng dụng trình duyệt.
  • Lệnh gọi đã hiển thị hộp thoại phân định cho người dùng, trong đó tuỳ chọn duy nhất là ứng dụng trình duyệt.

Đoạn mã sau đây cho biết cách cập nhật logic để sử dụng cờ ý định FLAG_ACTIVITY_REQUIRE_NON_BROWSER:

Kotlin

try {
    val intent = Intent(ACTION_VIEW, Uri.parse(url)).apply {
        // The URL should either launch directly in a non-browser app (if it's
        // the default), or in the disambiguation dialog.
        addCategory(CATEGORY_BROWSABLE)
        flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER
    }
    startActivity(intent)
} catch (e: ActivityNotFoundException) {
    // Only browser apps are available, or a browser is the default.
    // So you can open the URL directly in your app, for example in a
    // Custom Tab.
    openInCustomTabs(url)
}

Java

try {
    Intent intent = new Intent(ACTION_VIEW, Uri.parse(url));
    // The URL should either launch directly in a non-browser app (if it's the
    // default), or in the disambiguation dialog.
    intent.addCategory(CATEGORY_BROWSABLE);
    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // Only browser apps are available, or a browser is the default.
    // So you can open the URL directly in your app, for example in a
    // Custom Tab.
    openInCustomTabs(url);
}

Tránh hiện hộp thoại phân định

Nếu muốn tránh hiện hộp thoại phân định mà người dùng có thể thấy khi họ mở một URL, cũng như muốn tự xử lý URL trong các tình huống này, thì bạn có thể sử dụng một ý định đặt cờ ý định FLAG_ACTIVITY_REQUIRE_DEFAULT.

Nếu một ý định có cờ này, lệnh gọi đến startActivity() sẽ trả về ActivityNotFoundException khi lệnh gọi đã hiển thị một hộp thoại phân định cho người dùng.

Nếu một ý định có cả cờ này và cờ ý định FLAG_ACTIVITY_REQUIRE_NON_BROWSER, lệnh gọi đến startActivity() sẽ trả về ActivityNotFoundException khi xảy ra một trong hai điều kiện sau đây:

  • Lệnh gọi đã chạy trực tiếp ứng dụng trình duyệt.
  • Lệnh gọi đã hiển thị hộp thoại phân định cho người dùng.

Đoạn mã sau đây cho biết cách sử dụng kết hợp cờ FLAG_ACTIVITY_REQUIRE_NON_BROWSER và cờ FLAG_ACTIVITY_REQUIRE_DEFAULT:

Kotlin

val url = URL_TO_LOAD
try {
    // In order for this intent to be invoked, the system must directly launch a
    // non-browser app.
    val intent = Intent(ACTION_VIEW, Uri.parse(url).apply {
        addCategory(CATEGORY_BROWSABLE)
        flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER or
                FLAG_ACTIVITY_REQUIRE_DEFAULT
    }
    startActivity(intent)
} catch (e: ActivityNotFoundException) {
    // This code executes in one of the following cases:
    // 1. Only browser apps can handle the intent.
    // 2. The user has set a browser app as the default app.
    // 3. The user hasn't set any app as the default for handling this URL.
    openInCustomTabs(url)
}

Java

String url = URL_TO_LOAD;
try {
    // In order for this intent to be invoked, the system must directly launch a
    // non-browser app.
    Intent intent = new Intent(ACTION_VIEW, Uri.parse(url));
    intent.addCategory(CATEGORY_BROWSABLE);
    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REQUIRE_NON_BROWSER |
            FLAG_ACTIVITY_REQUIRE_DEFAULT);
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // This code executes in one of the following cases:
    // 1. Only browser apps can handle the intent.
    // 2. The user has set a browser app as the default app.
    // 3. The user hasn't set any app as the default for handling this URL.
    openInCustomTabs(url);
}

Mở tệp

Nếu ứng dụng của bạn xử lý tệp hoặc tệp đính kèm, chẳng hạn như kiểm tra xem thiết bị có thể mở một tệp nhất định hay không, thì thông thường, cách dễ nhất là thử bắt đầu một hoạt động có thể xử lý tệp đó. Để làm vậy, hãy sử dụng một ý định có thao tác theo ý định ACTION_VIEW và URI biểu thị tệp cụ thể. Nếu không có ứng dụng nào trên thiết bị, ứng dụng của bạn có thể nắm bắt ActivityNotFoundException. Trong logic xử lý ngoại lệ, bạn có thể hiển thị lỗi hoặc cố tự xử lý tệp.

Nếu ứng dụng của bạn phải biết trước liệu ứng dụng khác có thể mở một tệp nhất định hay không, hãy thêm phần tử <intent> trong đoạn mã sau đây vào phần tử <queries> của tệp kê khai. Bạn có thể thêm loại tệp nếu đã biết nội dung tệp vào thời gian biên dịch.

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
  <data android:mimeType="application/pdf" />
</intent>

Sau đó, bạn có thể kiểm tra xem có ứng dụng nào không bằng cách gọi resolveActivity() cùng với ý định của mình.

Cấp quyền truy cập URI

Lưu ý: Nội dung đề xuất này áp dụng cho mọi ứng dụng, bất kể những điều sau:

  • Phiên bản SDK mục tiêu của ứng dụng.
  • Liệu ứng dụng của bạn có xuất trình cung cấp nội dung hay không.

Để các ứng dụng nhắm đến Android 11 trở lên có thể truy cập vào URI nội dung, ý định của ứng dụng của bạn phải khai báo quyền truy cập vào URI bằng cách đặt ít nhất một trong các cờ ý định sau đây: FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION.

Trên Android 11 trở lên, quyền truy cập vào URI sẽ cung cấp chức năng sau đây cho ứng dụng nhận được ý định:

  • Đọc hoặc ghi dữ liệu mà URI nội dung biểu thị, tuỳ thuộc vào quyền URI được cấp.
  • Hiểu rõ về ứng dụng chứa trình cung cấp nội dung khớp với đơn vị quản lý URI. Ứng dụng chứa trình cung cấp nội dung có thể khác với ứng dụng gửi ý định.

Đoạn mã sau đây minh hoạ cách thêm cờ ý định quyền URI để ứng dụng khác nhắm đến Android 11 trở lên có thể xem dữ liệu trong URI nội dung:

Kotlin

val shareIntent = Intent(Intent.ACTION_VIEW).apply {
    flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
    data = CONTENT_URI_TO_SHARE_WITH_OTHER_APP
}

Java

Intent shareIntent = new Intent(Intent.ACTION_VIEW);
shareIntent.setFlags(FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setData(CONTENT_URI_TO_SHARE_WITH_OTHER_APP);

Kết nối với các dịch vụ

Nếu ứng dụng của bạn cần tương tác với một dịch vụ không tự động hiển thị, thì bạn có thể khai báo thao tác theo ý định phù hợp trong phần tử <queries>. Những phần sau đây cung cấp ví dụ thông qua các dịch vụ thường được truy cập.

Kết nối với công cụ chuyển văn bản sang lời nói

Nếu ứng dụng của bạn tương tác với một công cụ chuyển văn bản sang lời nói (TTS), hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.TTS_SERVICE" />
</intent>

Kết nối với dịch vụ nhận dạng lời nói

Nếu ứng dụng của bạn tương tác với một dịch vụ nhận dạng lời nói, hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.speech.RecognitionService" />
</intent>

Kết nối với các dịch vụ trình duyệt nội dung đa phương tiện

Trong ứng dụng trình duyệt nội dung đa phương tiện của khách hàng, hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.media.browse.MediaBrowserService" />
</intent>

Cung cấp chức năng tuỳ chỉnh

Nếu ứng dụng của bạn cần thực hiện các thao tác có thể tuỳ chỉnh hoặc hiển thị thông tin có thể tuỳ chỉnh dựa trên hoạt động tương tác của ứng dụng đó với các ứng dụng khác, thì bạn có thể biểu thị hành vi tuỳ chỉnh đó bằng cách dùng chữ ký bộ lọc ý định trong phần tử <queries> của tệp kê khai. Những phần sau đây cung cấp hướng dẫn chi tiết hơn cho một số trường hợp phổ biến.

Truy vấn các ứng dụng SMS

Nếu ứng dụng của bạn cần tìm hiểu về tập hợp ứng dụng SMS được cài đặt trên một thiết bị, chẳng hạn như để kiểm tra xem ứng dụng nào là trình xử lý SMS mặc định trên thiết bị, hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.SENDTO"/>
  <data android:scheme="smsto" android:host="*" />
</intent>

Tạo trang chia sẻ nội dung tuỳ chỉnh

Bất cứ khi nào có thể, hãy sử dụng trang chia sẻ nội dung do hệ thống cung cấp. Ngoài ra, hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.SEND" />
  <!-- Replace with the MIME type that your app works with, if needed. -->
  <data android:mimeType="image/jpeg" />
</intent>

Quá trình tạo trang chia sẻ nội dung trong logic của ứng dụng, chẳng hạn như lệnh gọi đến queryIntentActivities(), sẽ không thay đổi so với các phiên bản Android trước đây.

Hiển thị thao tác lựa chọn văn bản tuỳ chỉnh

Khi người dùng chọn văn bản trong ứng dụng của bạn, thanh công cụ chọn văn bản sẽ hiển thị tập hợp thao tác có thể thực hiện trên văn bản đã chọn. Nếu thanh công cụ này hiển thị các thao tác tuỳ chỉnh từ những ứng dụng khác, hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.PROCESS_TEXT" />
  <data android:mimeType="text/plain" />
</intent>

Hiển thị hàng dữ liệu tuỳ chỉnh cho một người liên hệ

Các ứng dụng có thể thêm hàng dữ liệu tuỳ chỉnh vào Trình cung cấp danh bạ. Để một ứng dụng danh bạ hiển thị dữ liệu tuỳ chỉnh này, ứng dụng đó cần phải có khả năng thực hiện những việc sau:

  1. Đọc tệp contacts.xml từ các ứng dụng khác.
  2. Tải một biểu tượng tương ứng với loại MIME tuỳ chỉnh.

Nếu ứng dụng của bạn là một ứng dụng danh bạ, hãy thêm phần tử <intent> sau đây vào phần tử <queries> của tệp kê khai:

<!-- Place inside the <queries> element. -->
<!-- Allows the app to read the "contacts.xml" file from the other apps. -->
<intent>
  <action android:name="android.accounts.AccountAuthenticator" />
</intent>
<!-- Allows the app to load an icon corresponding to the custom MIME type. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <data android:scheme="content" android:host="com.android.contacts"
        android:mimeType="vnd.android.cursor.item/*" />
</intent>