Google is committed to advancing racial equity for Black communities. See how.

Use cases for package visibility in Android 11

This document presents several common examples of use cases where an app interacts with other apps. Each section provides guidance on how to update your app to be compatible with the package visibility behavior change that takes place in Android 11.

When your app targets Android 11 and 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 new element is an <intent> element.

Open URLs

This section describes how to open URLs in an app that targets Android 11. Follow one of the examples in the following subsections, depending on how your app opens URLs.

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 section about how to load 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 allows the user to choose which app opens the URL.
  • An ActivityNotFoundException occurs because there isn't any 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 is unaffected by the package visibility changes in Android 11, you don't need to add a <queries> element to your app's manifest.

Check if 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 that case, 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>

Then, when you call queryIntentActivities() and pass a web intent as an argument, the returned list includes the available browser apps.

Open URLs in Custom Tabs

Custom Tabs allow an app to 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 allow a non-browser app to open a given 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 a disambiguation dialog to the user, 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 {
    // In order 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 {
    // In order 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 if 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 if an app is available by calling resolveActivity() with your 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 previous versions of Android.

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 should show 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>

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 media browser services

In your 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>

Show custom data rows for a contact

Apps can add custom data rows to the Contacts Provider. In order for a contacts app to show this custom data, it needs to be able to do the following:

  1. Read the contacts.xml file from the other apps.
  2. 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. -->
<!-- Allows the app to read the "contacts.xml" file from the other apps. -->
<intent>
  <action android:name="android.accounts.AccountAuthenticator" />
</intent>
<!-- Allows the app to 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>

Declare package visibility needs in a library

If you develop an Android library, you can declare your package visibility needs by adding a <queries> element in your AAR manifest file. This <queries> element has the same functionality as the element that apps can declare in their own manifests.

If your library involves communication with a "host" app, such as using a bound service, include a <package> element that specifies the host app's package name:

<!-- Place inside the <queries> element. -->
<package android:name=PACKAGE_NAME />

By including this declaration, you can check if the host app is installed and interact with it, such as by calling bindService(). The calling app that uses your library automatically becomes visible to the host app as a result of this interaction.