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

В этом документе представлены несколько распространенных случаев использования, в которых приложение взаимодействует с другими приложениями. В каждом разделе представлены инструкции о том, как реализовать функциональность приложения с ограниченной видимостью пакетов, что необходимо учитывать, если ваше приложение предназначено для 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>

Создайте собственный общий лист

По возможности используйте предоставленный системой общий лист . Альтернативно, включите следующий элемент <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>

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

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

  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>