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

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