This document presents several common use cases in which an app interacts with other apps. Each section provides guidance on how to accomplish the app’s functionality with limited package visibility, which you need to consider if your app targets Android 11 (API level 30) or higher.
When an app that targets Android 11 or higher uses an intent to
start an activity in another app, the most straightforward approach is to invoke
the intent and handle the
ActivityNotFoundException
exception if no app is available.
If part of your app depends on knowing whether the call to
startActivity()
can succeed, such as showing a UI, add an element to the
<queries>
element of your app's
manifest. Typically, this is an <intent>
element.
Open URLs
This section describes various ways to open URLs in an app that targets Android 11 or higher.
Open URLs in a browser or other app
To open a URL, use an intent that contains the
ACTION_VIEW
intent action, as
described in the guide to loading a web
URL. After you call startActivity()
using this intent, one of the following happens:
- The URL opens in a web browser app.
- The URL opens in an app that supports the URL as a deep link.
- A disambiguation dialog appears, which lets the user choose which app opens the URL.
An
ActivityNotFoundException
occurs because there isn't an app installed on the device that can open the URL. (This is unusual.)It's recommended that your app catch and handle the
ActivityNotFoundException
if it occurs.
Because the startActivity()
method doesn't require package visibility to
start another application’s activity, you don't need to add a <queries>
element to your app's manifest or make any changes to an existing <queries>
element. This is true for both implicit and explicit intents that open a URL.
Check whether a browser is available
In some cases, your app might want to verify that there's at least one browser
available on the device, or that a specific browser is the default browser,
before attempting to open a URL. In those cases, include the following
<intent>
element as part of the <queries>
element in your manifest:
<!-- 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>
When you call queryIntentActivities()
and pass a web intent as an argument,
the returned list includes the available browser apps in some cases. The list
doesn't include browser apps if the user has configured the URL to open in a
non-browser app by default.
Open URLs in Custom Tabs
Custom Tabs let an
app customize how the browser looks and feels. You can open a URL in
a Custom Tab
without needing to add or change the <queries>
element in your app manifest.
However, you might want to check whether the device has a browser that supports
Custom Tabs
or select a specific browser to launch with Custom Tabs using
CustomTabsClient.getPackageName()
.
In those cases, include the following <intent>
element as part of the
<queries>
element in your manifest:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.support.customtabs.action.CustomTabsService" /> </intent>
Let non-browser apps handle URLs
Even if your app can open URLs using Custom Tabs, it's recommended that you
let a non-browser app open a URL if possible. To provide this
capability in your app, attempt a call to startActivity()
using an intent
that sets the
FLAG_ACTIVITY_REQUIRE_NON_BROWSER
intent flag. If the system throws an ActivityNotFoundException
, your app can
then open the URL in a Custom Tab.
If an intent includes this flag, a call to startActivity()
causes an
ActivityNotFoundException
to be thrown when either of the following
conditions occurs:
- The call would have launched a browser app directly.
- The call would have shown the user a disambiguation dialog where the only options are browser apps.
The following code snippet shows how to update your logic to use the
FLAG_ACTIVITY_REQUIRE_NON_BROWSER
intent flag:
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); }
Avoid a disambiguation dialog
If you want to avoid showing the disambiguation dialog that users might see when
they open a URL, and instead prefer to handle the URL yourself in these
situations, you can use an intent that sets the
FLAG_ACTIVITY_REQUIRE_DEFAULT
intent flag.
If an intent includes this flag, a call to startActivity()
causes an
ActivityNotFoundException
to be thrown when the call would have shown a
disambiguation dialog to the user.
If an intent includes both this flag and the
FLAG_ACTIVITY_REQUIRE_NON_BROWSER
intent flag, a call to startActivity()
causes an ActivityNotFoundException
to be thrown when either of the following conditions occurs:
- The call would have launched the browser app directly.
- The call would have shown a disambiguation dialog to the user.
The following code snippet shows how to use the FLAG_ACTIVITY_REQUIRE_NON_BROWSER
and FLAG_ACTIVITY_REQUIRE_DEFAULT
flags together:
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); }
Open a file
If your app handles files or attachments, such as checking whether a device can
open a given file, it's usually easiest to try to start an activity that can
handle the file. To do so, use an intent that includes the ACTION_VIEW
intent
action and the URI that represents the specific file. If no app is available on
the device, your app can catch the ActivityNotFoundException
. In your
exception-handling logic, you can either show an error or try to handle the file
yourself.
If your app must know in advance whether another app can open a given file,
include the <intent>
element in the following code snippet as part of the
<queries>
element in your manifest. Include the file type if you already know
what it is at compile time.
<!-- 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>
You can then check whether an app is available by calling resolveActivity()
with your intent.
Grant URI access
Note: Declaring URI access permissions as described in this section is required for apps that target Android 11 (API level 30) or higher and recommended for all apps, regardless of their target SDK version and whether they export their content providers.
For apps that target Android 11 or higher to
access the content URI, your app's intent must declare URI access
permissions
by setting one or both of the following intent flags:
FLAG_GRANT_READ_URI_PERMISSION
and
FLAG_GRANT_WRITE_URI_PERMISSION
.
On Android 11 and higher, the URI access permissions give the following capabilities to the app that receives the intent:
- Read from, or write to, the data that the content URI represents, depending on the given URI permissions.
- Gain visibility into the app containing the content provider that matches the URI authority. The app that contains the content provider might be different from the app that sends the intent.
The following code snippet demonstrates how to add a URI permissions intent flag so that another app that targets Android 11 or higher can view the data in the content 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);
Connect to services
If your app needs to interact with a service that isn't visible
automatically, you can declare the
appropriate intent action within a <queries>
element. The following sections
give examples using commonly accessed services.
Connect to a text-to-speech engine
If your app interacts with a text-to-speech (TTS) engine, include the following
<intent>
element as part of the <queries>
element in your manifest:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.TTS_SERVICE" /> </intent>
Connect to a speech recognition service
If your app interacts with a speech recognition service, include the following
<intent>
element as part of the <queries>
element in your manifest:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.speech.RecognitionService" /> </intent>
Connect to media browser services
If your app is a client media browser
app, include
the following <intent>
element as part of the <queries>
element in your
manifest:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.media.browse.MediaBrowserService" /> </intent>
Provide custom functionality
If your app needs to perform customizable actions or show customizable
information based on its interactions with other apps, you can represent that
custom behavior using intent filter
signatures as
part of the <queries>
element in your manifest. The following sections provide
detailed guidance for several common scenarios.
Query for SMS apps
If your app needs information about the set of SMS apps that are installed on a
device, for example to check which app is the device's default SMS handler,
include the following <intent>
element as part of the <queries>
element in
your manifest:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.SENDTO"/> <data android:scheme="smsto" android:host="*" /> </intent>
Create a custom sharesheet
Whenever possible, use a system-provided
sharesheet. Alternatively,
include the following <intent>
element as part of the <queries>
element in
your manifest:
<!-- 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>
The process of building the sharesheet in your app's logic, such as the call to
queryIntentActivities()
, otherwise remains unchanged compared to
versions of Android earlier than Android 11.
Show custom text selection actions
When users select text in your app, a text selection
toolbar
shows the set of possible operations to perform on the selected text. If this
toolbar shows custom actions from other apps, include the following
<intent>
element as part of the <queries>
element in your manifest:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.PROCESS_TEXT" /> <data android:mimeType="text/plain" /> </intent>
Show custom data rows for a contact
Apps can add custom data rows to the Contacts Provider. For a contacts app to show this custom data, it needs to be able to do the following:
- Read the
contacts.xml
file from the other apps. - Load an icon corresponding to the custom MIME type.
If your app is a contacts app, include the following <intent>
elements as part
of the <queries>
element in your manifest:
<!-- 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>