在套件瀏覽權限有限制的情況下執行常見用途

本文件說明與其他應用程式互動的幾個常見用途。本節會說明如何在有限的套件瀏覽權限中完成應用程式功能,如果應用程式指定目標為 Android 11 (API 級別 30) 或以上版本,則必須列入考量。

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

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

開啟網址

本節說明在指定 Android 11 或以上版本的應用程式中開啟網址的各種方法。

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

如要開啟網址,請使用包含 ACTION_VIEW 的意圖動作,詳情請參閱「載入 web 網址」指南。在使用這個意圖叫用 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 {
    // 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 {
    // 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 存取權

注意事項:如果應用程式指定目標為 Android 11 (API 級別 30) 或以上版本,且向所有應用程式提出建議,則無論應用程式的目標 SDK 版本為何,您都必須宣告本節所述的 URI 存取權限,以及是否匯出其內容供應者。

如果應用程式指定 Android 11 或以上版本,才能存取內容 URI,則應用程式的意圖必須宣告URI 存取權限透過設定下列一或兩項意圖旗標:FLAG_GRANT_READ_URI_PERMISSIONFLAG_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>

在應用程式邏輯裡,建構 Sharesheet 的程序 (例如呼叫 queryIntentActivities()) 會保持先與 Android 11 之前的 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. -->
<!-- Lets the app read the contacts.xml file from other apps. -->
<intent>
  <action android:name="android.accounts.AccountAuthenticator" />
</intent>
<!-- Lets the app 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>