有限制瀏覽權限的常見用例

本文件說明與其他應用程式互動的幾個常見用例。本節會說明如何「宣告其他已安裝應用程式的套件瀏覽權限」,如果應用程式是 Android 11 (API 級別 30) 或以上的版本,則必須列入考量。

如果應用程式指定 Android 11 或以上的版本,並要使用意圖在其他應用程式中啟動活動,最直接的做法是叫用意圖並處理 ActivityNotFoundException 如果沒有應用程式,則不適用。

如果應用程式的部分功能需要知道是否成功呼叫 startActivity() (例如顯示 UI),請將元素新增至 <queries> 應用程式的資訊清單。一般來說,這個新元素是 <intent> 元素。

開啟網址

本節將說明如何在 Android 11 或以上版本的應用程式中開啟網址。請根據您的應用程式開啟網址的方式,按照後續小節列舉的任一範例操作。

在瀏覽器或其他應用程式中開啟網址

如要開啟網址,請使用包含 ACTION_VIEW 的意圖,詳情請參閱「載入網址」一節。在使用這個意圖呼叫 startActivity() 後,將發生下列其中一種情況:

  • 網址隨即在網路瀏覽器中開啟。
  • 網址隨即會在支援該網址的應用程式中開啟,做為深層連結
  • 畫面上隨即會顯示消歧對話方塊,讓使用者選擇要用哪個應用程式開啟網址。
  • 裝置上沒有任何可開啟網址的應用程式時,會產生 ActivityNotFoundException 的問題。(此為異常現象)。

    如果應用程式出現 ActivityNotFoundException,建議應用程式擷取並處理相關問題。

由於 startActivity() 不受系統瀏覽權限行為的影響,所以您不需要新增 <queries> 變更資訊清單,或對現有的資訊清單進行變更 <queries>

檢查是否有可用的瀏覽器

在某些情況下,您的應用程式可能會確認裝置上至少有一個瀏覽器可正常運作,或是指定某個瀏覽器為預設瀏覽器,然後再嘗試開啟網址。在這種情況下,請在資訊清單中的 <queries> 元素中加入下列 <intent> 元素:

<!-- 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>

當您呼叫 queryIntentActivities() 並將網路意圖以引數傳送時,在某些情況下傳回清單會含有可用的瀏覽器應用程式資訊。如果使用者將網址預設為在非瀏覽器應用程式中開啟,則清單就不會包含瀏覽器應用程式。

在自訂分頁中開啟網址

自訂分頁可讓應用程式自訂瀏覽器的外觀與風格。您可以在自訂分頁中開啟網址,無須在應用程式資訊清單中新增或變更 <queries> 元素。

不過,建議您用檢查裝置瀏覽器是否支援自訂分頁,或透過 CustomTabsClient.getPackageName() 選擇特定瀏覽器並開啟自訂分頁。在這種情況下,請在資訊清單的 <queries> 元素中加入下列 <intent> 元素:

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

允許非瀏覽器的應用程式處理網址

即使您的應用程式可使用自訂分頁開啟網址,但仍建議您盡可能允許非瀏覽器的應用程式開啟指定網址。如要在應用程式中提供這項功能,請使用設定 FLAG_ACTIVITY_REQUIRE_NON_BROWSER 的意圖來呼叫 startActivity()。如果系統擲回 ActivityNotFoundException,應用程式就可以在自訂分頁中開啟網址。

如果意圖包含此旗標,當發生下列任一情況時,會對 startActivity() 的呼叫會造成 ActivityNotFoundException 擲回:

  • 呼叫將直接啟動瀏覽器應用程式。
  • 唯一的選項是瀏覽器應用程式時,該呼叫會顯示消歧對話方塊。

下列程式碼片段說明如何更新邏輯以使用 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);
}

避免消歧對話方塊

為避免使用者開啟網址時,但仍顯示消歧對話方塊,所以最好先處理網址,因此您可以先設定 FLAG_ACTIVITY_REQUIRE_DEFAULT 的意圖。

如果意圖中包含這個標記,則呼叫會出現消歧對話框給使用者,因為呼叫 startActivity() 會觸發 ActivityNotFoundException

如果意圖同時包含這個標記和 FLAG_ACTIVITY_REQUIRE_NON_BROWSER,要呼叫 startActivity() 觸發 ActivityNotFoundException,而發生在下列任一情況時,系統會觸發這個事件:

  • 呼叫將直接啟動瀏覽器應用程式。
  • 此呼叫會顯示消歧對話方塊。

下列程式碼片段顯示如何搭配使用 FLAG_ACTIVITY_REQUIRE_NON_BROWSERFLAG_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);
}

開啟檔案

假如您的應用程式會處理檔案或附件 (例如檢查裝置能否開啟指定檔案),那麼最簡單的步驟就是嘗試啟動可處理檔案的活動。如要這麼做,請使用包含 ACTION_VIEW 的意圖,以及代表特定檔案的 URI。如果裝置上沒有應用程式,應用程式可擷取 ActivityNotFoundException。在例外狀況處理邏輯中,您可以顯示錯誤,或嘗試自行處理檔案。

如果應用程式必須事先知道其他應用程式是否可以開啟指定檔案,請將下列程式碼片段裡的 <intent> 元素加入資訊清單的 <queries> 元素中。如果已知編譯時間,請加入檔案類型。

<!-- 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>

接著,您可以透過意圖呼叫 resolveActivity() 來確認應用程式是否可用。

授予 URI 存取權

注意:無論何種情況,這項建議適用於所有應用程式:

  • 您應用程式的目標 SDK 版本。
  • 您的應用程式是否匯出其內容提供者。

為了讓應用程式指定 Android 11 或以上的版本,才能存取內容 URI,那麼您應用程式的意圖必須宣告URI 存取權限設定至少以下其中一個意圖旗標:FLAG_GRANT_READ_URI_PERMISSION 以及 FLAG_GRANT_WRITE_URI_PERMISSION

在 Android 11 或以上版本中,URI 存取權限會為具備意圖的應用程式提供下列功能:

  • 讀取或寫入 URI 代表的內容資料 (視指定的 URI 權限而定)。
  • 查看含有與 URI 授權相符的內容供應者的應用程式。因為內容供應者的應用程式與傳送意圖的應用程式有可能不同。

下列程式碼片段顯示如何新增 URI 權限意圖旗標,以便指定 Android 11 或以上版本的其他應用程式查看 URI 內容資料:

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

連線至服務

如果應用程式需要與未自動顯示的服務互動,您可以在 <queries> 元素中宣告適當的意圖動作。以下各節將提供使用常用服務的範例。

連線至文字轉語音引擎

如果您的應用程式與文字轉語音 (TTS) 引擎互動,請在資訊清單的 <queries> 元素中加入下列 <intent> 元素:

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

連線至語音辨識服務

如果應用程式與語音辨識服務互動,請在資訊清單的 <queries> 元素中加入下列 <intent> 元素:

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

連線至媒體瀏覽器服務

用戶端媒體瀏覽器應用程式中,在資訊清單的 <queries> 元素中將下列 <intent> 元素加入:

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

提供自訂功能

如果您需要執行可自訂的動作,或根據使用者與其他應用程式的互動顯示可自訂的資訊,您可以使用意圖篩選器簽署來代表資訊清單裡的 <queries> 元素做為自訂行為。下列各節將針對幾種常見情況提供更詳盡的指南。

簡訊應用程式查詢

如果應用程式需要瞭解安裝在裝置上的簡訊應用程式,例如要檢查哪個應用程式是裝置的預設簡訊處理程式,請將下列 <intent> 元素加入資訊清單中的 <queries> 元素:

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

建立自訂共用試算表

請盡可能使用系統提供的分享表。您也可以在資訊清單的 <queries> 元素中加入下列 <intent> 元素:

<!-- 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>

在應用程式邏輯裡,建構共用試算表的程序 (例如呼叫 queryIntentActivities()) 否則保持先與前相同的 Android 版本不變。

顯示自訂文字選取動作

當使用者在應用程式中選取文字時,文字選取工具列會顯示一組可對所選文字執行的作業。如果這個工具列應該顯示在其他應用程式的自訂動作,請在資訊清單的 <queries> 元素中加入下列 <intent> 元素:

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

顯示聯絡人的自訂資料列

應用程式可以在聯絡人提供者中新增自訂資料列。要讓聯絡人應用程式顯示這項自訂資料,請執行下列操作:

  1. 讀取其他應用程式中的 contacts.xml 檔案。
  2. 載入與自訂 MIME 類型對應的圖示。

如果您的應用程式是聯絡人應用程式,請將下列 <intent> 元素加入資訊清單的 <queries> 元素中:

<!-- 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>