Выполнение распространенных сценариев использования при ограниченной видимости пакета.

В этом документе представлено несколько распространенных вариантов использования, в которых приложение взаимодействует с другими приложениями. Каждый раздел содержит руководство о том, как реализовать функциональность приложения с ограниченной видимостью пакета, что вам необходимо учитывать, если ваше приложение нацелено на Android 11 (уровень API 30) или выше.

Когда приложение, предназначенное для Android 11 или более поздних версий, использует намерение для запуска активности в другом приложении, наиболее простым подходом является вызов намерения и обработка исключения ActivityNotFoundException , если ни одно приложение не доступно.

Если часть вашего приложения зависит от знания того, может ли вызов startActivity() быть успешным, например, от отображения пользовательского интерфейса, добавьте элемент в элемент <queries> манифеста вашего приложения. Обычно это элемент <intent> .

Открытые URL-адреса

В этом разделе описываются различные способы открытия URL-адресов в приложении, предназначенном для Android 11 или более поздних версий.

Открывайте URL-адреса в браузере или другом приложении

Чтобы открыть URL, используйте намерение, содержащее действие намерения ACTION_VIEW , как описано в руководстве по загрузке веб-URL . После вызова startActivity() с использованием этого намерения происходит одно из следующих событий:

  • URL-адрес открывается в приложении веб-браузера.
  • URL-адрес открывается в приложении, которое поддерживает URL-адрес как глубокую ссылку .
  • Появится диалоговое окно устранения неоднозначности, позволяющее пользователю выбрать, какое приложение откроет URL-адрес.
  • ActivityNotFoundException возникает из-за того, что на устройстве не установлено приложение, которое может открыть URL-адрес. (Это необычно.)

    Рекомендуется, чтобы ваше приложение перехватывало и обрабатывало исключение ActivityNotFoundException , если оно возникнет.

Поскольку метод startActivity() не требует видимости пакета для запуска активности другого приложения, вам не нужно добавлять элемент <queries> в манифест вашего приложения или вносить какие-либо изменения в существующий элемент <queries> . Это справедливо как для неявных, так и для явных намерений, которые открывают URL.

Проверьте, доступен ли браузер

В некоторых случаях вашему приложению может потребоваться проверить, что на устройстве доступен хотя бы один браузер или что определенный браузер является браузером по умолчанию, прежде чем пытаться открыть URL. В этих случаях включите следующий элемент <intent> как часть элемента <queries> в вашем манифесте:

<!-- 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() и передаете веб-намерение в качестве аргумента, возвращаемый список в некоторых случаях включает доступные приложения браузера. Список не включает приложения браузера, если пользователь настроил URL для открытия в небраузерном приложении по умолчанию.

Открытие URL-адресов в пользовательских вкладках

Пользовательские вкладки позволяют приложению настраивать внешний вид и поведение браузера. Вы можете открыть URL-адрес в пользовательской вкладке без необходимости добавлять или изменять элемент <queries> в манифесте вашего приложения.

Однако вам может потребоваться проверить, поддерживает ли устройство браузер с пользовательскими вкладками , или выбрать определенный браузер для запуска с пользовательскими вкладками с помощью CustomTabsClient.getPackageName() . В этих случаях включите следующий элемент <intent> как часть элемента <queries> в вашем манифесте:

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

Позвольте небраузерным приложениям обрабатывать URL-адреса

Даже если ваше приложение может открывать URL-адреса с помощью пользовательских вкладок, рекомендуется, чтобы вы позволяли небраузерному приложению открывать URL-адрес, если это возможно. Чтобы предоставить эту возможность в вашем приложении, попробуйте вызвать startActivity() с помощью намерения, которое устанавливает флаг намерения FLAG_ACTIVITY_REQUIRE_NON_BROWSER . Если система выдает исключение ActivityNotFoundException , ваше приложение может открыть URL-адрес в пользовательской вкладке.

Если намерение включает этот флаг, вызов startActivity() приводит к возникновению ActivityNotFoundException при возникновении любого из следующих условий:

  • Вызов мог бы напрямую запустить приложение браузера.
  • В результате вызова пользователю было бы показано диалоговое окно с разрешением неоднозначности, где единственными вариантами выбора были бы приложения браузера.

В следующем фрагменте кода показано, как обновить логику для использования флага намерения FLAG_ACTIVITY_REQUIRE_NON_BROWSER :

Котлин

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

Ява

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

Избегайте диалога, разрешающего неоднозначность

Если вы хотите избежать отображения диалогового окна устранения неоднозначности, которое пользователи могут видеть при открытии URL-адреса, и вместо этого предпочитаете самостоятельно обрабатывать URL-адрес в таких ситуациях, вы можете использовать намерение, которое устанавливает флаг намерения FLAG_ACTIVITY_REQUIRE_DEFAULT .

Если намерение включает этот флаг, вызов startActivity() приводит к возникновению исключения ActivityNotFoundException , хотя вызов должен был бы показать пользователю диалоговое окно разрешения неоднозначности.

Если намерение включает в себя как этот флаг, так и флаг намерения FLAG_ACTIVITY_REQUIRE_NON_BROWSER , вызов startActivity() приводит к возникновению исключения ActivityNotFoundException при возникновении любого из следующих условий:

  • Вызов мог бы запустить приложение браузера напрямую.
  • При вызове пользователю было бы показано диалоговое окно для устранения неоднозначности.

В следующем фрагменте кода показано, как использовать флаги FLAG_ACTIVITY_REQUIRE_NON_BROWSER и FLAG_ACTIVITY_REQUIRE_DEFAULT вместе:

Котлин

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

Ява

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

Примечание: Объявление разрешений на доступ URI, как описано в этом разделе, требуется для приложений, предназначенных для Android 11 (уровень API 30) или выше, и рекомендуется для всех приложений, независимо от их целевой версии 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 контента:

Котлин

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

Ява

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), включите следующий элемент <intent> как часть элемента <queries> в вашем манифесте:

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

Подключитесь к службе распознавания речи

Если ваше приложение взаимодействует со службой распознавания речи, включите следующий элемент <intent> как часть элемента <queries> в вашем манифесте:

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

Подключайтесь к службам медиабраузера

Если ваше приложение представляет собой клиентское приложение-браузер мультимедиа , включите следующий элемент <intent> как часть элемента <queries> в вашем манифесте:

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

Предоставлять индивидуальные функции

Если вашему приложению необходимо выполнять настраиваемые действия или показывать настраиваемую информацию на основе его взаимодействия с другими приложениями, вы можете представить это настраиваемое поведение с помощью сигнатур фильтров намерений как часть элемента <queries> в вашем манифесте. В следующих разделах приведены подробные рекомендации для нескольких распространенных сценариев.

Запрос на SMS-приложения

Если вашему приложению требуется информация о наборе приложений SMS, установленных на устройстве, например, чтобы проверить, какое приложение является обработчиком SMS по умолчанию на устройстве, включите следующий элемент <intent> как часть элемента <queries> в вашем манифесте:

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

Создайте пользовательский лист обмена

По возможности используйте системный sharesheet . В качестве альтернативы включите следующий элемент <intent> как часть элемента <queries> в вашем манифесте:

<!-- 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 до Android 11.

Показать пользовательские действия по выбору текста

Когда пользователи выбирают текст в вашем приложении, панель инструментов выбора текста показывает набор возможных операций для выполнения с выбранным текстом. Если эта панель инструментов показывает пользовательские действия из других приложений, включите следующий элемент <intent> как часть элемента <queries> в вашем манифесте:

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

Показать пользовательские строки данных для контакта

Приложения могут добавлять пользовательские строки данных в Contacts Provider. Чтобы приложение контактов могло отображать эти пользовательские данные, оно должно иметь возможность делать следующее:

  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>