Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Casos prácticos para visibilidad de paquetes en Android 11

En este documento se presentan varios ejemplos comunes de casos prácticos en los que una app interactúa con otras. En cada sección se proporciona orientación sobre cómo debes actualizar tu app para que sea compatible con el cambio de comportamiento de visibilidad del paquete que se implementa en Android 11.

Cuando tu app se orienta a Android 11 y usa un intent implícito para iniciar una actividad en otra app, el enfoque más directo es invocar el intent y controlar la excepción ActivityNotFoundException si no hay una app disponible.

Si para una parte de tu app es necesario saber si la llamada a startActivity() puede completarse correctamente, como cuando se muestra una IU, agrega un elemento al elemento <queries> del manifiesto de tu app. Por lo general, este nuevo elemento es un elemento <intent>.

Cómo abrir URL

En esta sección, se describe cómo abrir URL en una app orientada a Android 11. Sigue uno de los ejemplos de las siguientes subsecciones, según la manera en la que tu app abre las URL.

Cómo abrir URL en un navegador o en otra app

Para abrir una URL, usa un intent que contenga la acción de intent ACTION_VIEW, como se describe en la sección sobre cómo cargar una URL web. Después de llamar a startActivity() con este intent, sucede una de las siguientes opciones:

  • Se abre la URL en una app de navegador web.
  • Se abre la URL en una app que admite la URL como un vínculo directo.
  • Aparece un diálogo de desambiguación que permite al usuario elegir qué app abre la URL.
  • Se produce una ActivityNotFoundException porque no hay ninguna app instalada en el dispositivo que pueda abrir la URL (este no es un caso común).

    Se recomienda que tu app detecte y controle la ActivityNotFoundException si esto ocurre.

Debido a que tu app usa un intent implícito para abrir la URL, no necesitas agregar un elemento <queries> al manifiesto de tu app.

Cómo comprobar si hay un navegador disponible

En algunos casos, es posible que, antes de intentar abrir una URL, tu app quiera verificar si hay al menos un navegador disponible en el dispositivo o si un navegador específico es el predeterminado. En ese caso, incluye el siguiente elemento <intent> como parte del elemento <queries> de tu manifiesto:

<!-- 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>

Luego, cuando llamas a queryIntentActivities() y pasas un intent web como argumento, la lista que se muestra incluye las apps de navegador disponibles.

Cómo abrir URL en pestañas personalizadas

Las pestañas personalizadas permiten que una app personalice el aspecto y la experiencia del navegador. Puedes abrir una URL en una pestaña personalizada sin necesidad de agregar ni cambiar el elemento <queries> en el manifiesto de tu app.

Sin embargo, tal vez quieras verificar si el dispositivo es compatible con las pestañas personalizadas o seleccionar un navegador específico para iniciar con pestañas personalizadas mediante CustomTabsClient.getPackageName(). En estos casos, incluye el siguiente elemento <intent> como parte del elemento <queries> de tu manifiesto:

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

Cómo permitir que las apps que no son navegadores controlen URL

Incluso si tu app puede abrir URL con pestañas personalizadas, se recomienda que permitas que una app que no es un navegador abra una URL determinada, si es posible. Para proporcionar esta función en tu app, llama a startActivity() usando un intent que establezca la marca de intent FLAG_ACTIVITY_REQUIRE_NON_BROWSER. Si el sistema muestra una ActivityNotFoundException, tu app puede abrir la URL en una pestaña personalizada.

Si un intent incluye esta marca, una llamada a startActivity() hace que se genere una ActivityNotFoundException en alguna de las siguientes condiciones:

  • La llamada habría iniciado directamente una app de navegador.
  • La llamada le habría mostrado al usuario un diálogo de desambiguación, en el que las únicas opciones son apps de navegador.

En el siguiente fragmento de código, se muestra cómo actualizar la lógica para usar la marca de intent FLAG_ACTIVITY_REQUIRE_NON_BROWSER:

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

Cómo evitar un diálogo de desambiguación

Si deseas evitar que se muestre el diálogo de desambiguación que los usuarios podrían ver cuando abran una URL y prefieres controlar la URL en estas situaciones, puedes usar un intent que establezca la marca de intent FLAG_ACTIVITY_REQUIRE_DEFAULT.

Si un intent incluye esta marca, una llamada a startActivity() hace que se genere una ActivityNotFoundException en el momento en el que la llamada le habría mostrado al usuario un diálogo de desambiguación.

Si un intent incluye esta marca y la marca de intent FLAG_ACTIVITY_REQUIRE_NON_BROWSER, una llamada a startActivity() hace que se genere una ActivityNotFoundException en alguna de las siguientes condiciones:

  • La llamada habría iniciado directamente una app de navegador.
  • La llamada le habría mostrado al usuario un diálogo de desambiguación.

En el siguiente fragmento de código, se muestra cómo usar las marcas FLAG_ACTIVITY_REQUIRE_NON_BROWSER y FLAG_ACTIVITY_REQUIRE_DEFAULT juntas:

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

Cómo abrir un archivo

Si tu app controla archivos o archivos adjuntos, como cuando se verifica si un dispositivo puede abrir un archivo determinado, suele ser más fácil intentar iniciar una actividad que pueda controlar el archivo. Para hacerlo, usa un intent que incluya la acción de intent ACTION_VIEW y el URI que representa al archivo específico. Si no hay ninguna app disponible en el dispositivo, tu app puede detectar la ActivityNotFoundException. En tu lógica de control de excepciones, puedes mostrar un error o intentar controlar el archivo por tu cuenta.

Si tu app debe saber con anticipación si otra app puede abrir un archivo determinado, incluye el elemento <intent> en el siguiente fragmento de código como parte del elemento <queries> de tu manifiesto. Incluye el tipo de archivo si ya sabes cuál es durante la compilación.

<!-- 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>

Luego puedes llamar a resolveActivity() con tu intent para verificar si una app está disponible.

Cómo crear una hoja compartida

Siempre que sea posible, usa una hoja compartida proporcionada por el sistema. Como alternativa, puedes incluir el siguiente elemento <intent> como parte del elemento <queries> de tu manifiesto:

<!-- 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>

El proceso de compilación de la hoja compartida en la lógica de tu app, como la llamada a queryIntentActivities(), permanece sin cambios en comparación con las versiones anteriores de Android.

Cómo conectarse a un motor de texto a voz

Si tu app interactúa con un motor de texto a voz (TTS), incluye el siguiente elemento <intent> como parte del elemento <queries> de tu manifiesto:

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

Cómo conectarse a los servicios del navegador multimedia

En tu app de navegador multimedia cliente, incluye el siguiente elemento <intent> como parte del elemento <queries> de tu manifiesto:

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

Cómo mostrar filas de datos personalizados para un contacto

Las apps pueden agregar filas de datos personalizados al Proveedor de contactos. Para que una app de contactos muestre estos datos personalizados, debe ser capaz de realizar lo siguiente:

  1. Leer el archivo contacts.xml de las otras apps
  2. Cargar un ícono correspondiente al tipo de MIME personalizado

Si tu app es una app de contactos, incluye los siguientes elementos <intent> como parte del elemento <queries> de tu manifiesto:

<!-- 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>

Cómo declarar necesidades de visibilidad de paquetes en una biblioteca

Si desarrollas una biblioteca de Android, puedes declarar las necesidades de visibilidad de tu paquete agregando un elemento <queries> en tu archivo de manifiesto AAR. Este elemento <queries> tiene la misma función que el elemento que las apps pueden declarar en sus propios manifiestos.

Si tu biblioteca se comunica con una app "host", como cuando se usa un servicio vinculado, incluye un elemento <package> que especifique el nombre del paquete de la app host:

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

Cuando incluyes esta declaración, puedes verificar si la app host está instalada y, luego, interactuar con ella, por ejemplo, llamando a bindService(). Como resultado de esta interacción, la app que realiza la llamada y usa tu biblioteca se hace visible automáticamente.