En este documento, se presentan varios ejemplos comunes de casos de uso en los que una app interactúa con otras. En cada sección, se proporciona orientación para administrar la visibilidad de paquetes en otras apps instaladas que debes tener en cuenta si tu aplicación se orienta a Android 11 (nivel de API 30) o versiones posteriores.
Si tu app se orienta a Android 11 y usa un intent para iniciar una actividad en otra app, el enfoque más simple es invocar el intent y procesar la excepción ActivityNotFoundException
si no hay ninguna 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 objeto 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 o versiones posteriores. 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 u 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 procese la
ActivityNotFoundException
si esto ocurre.
Dado que el método startActivity()
no se ve afectado por el comportamiento de visibilidad de paquetes del sistema, no es necesario que agregues 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>
En algunos casos, cuando llamas a queryIntentActivities()
y pasas un intent web como argumento, la lista que se muestra incluye las apps de navegador disponibles. La lista no incluye apps de navegador si el usuario configuró la URL para que se abra en una app sin navegador de forma predeterminada.
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 que se inicie 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 de navegación controlen URL
Incluso si tu app puede abrir URL con pestañas personalizadas, se recomienda que permitas que una app que no es de navegación 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 que se muestre un diálogo de desambiguación
Si no deseas que se muestre el diálogo de desambiguación que los usuarios podrían ver cuando abren una URL, y prefieres procesar la URL por tu cuenta, 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 arroje una ActivityNotFoundException
si se cumple 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 otorgar un acceso de URI
Nota: Esta recomendación se aplica a todas las apps, independientemente de lo siguiente:
- La versión del SDK de destino de tu app
- Si tu app exporta su proveedor de contenido o no
A fin de que las apps que se orientan a Android 11 o versiones posteriores puedan acceder al URI de contenido, el intent de tu app debe declarar permisos de acceso al URI mediante la configuración de al menos una de las siguientes marcas de intent: FLAG_GRANT_READ_URI_PERMISSION
o FLAG_GRANT_WRITE_URI_PERMISSION
.
En Android 11 y versiones posteriores, los permisos de acceso al URI proporcionan las siguientes capacidades a la app que recibe el intent:
- Leer los datos que representa el URI de contenido o escribir en ellos, según los permisos de URI proporcionados
- Obtener visibilidad de la app que contiene el proveedor de contenido que coincide con la autoridad del URI (la app que incluye el proveedor de contenido puede ser diferente de la que envía el intent)
En el siguiente fragmento de código, se muestra cómo agregar una marca de intent para permisos de URI, de modo que otra app orientada a Android 11 o versiones posteriores pueda ver los datos del URI de contenido:
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);
Cómo crear una hoja de Sharesheet personalizada
Siempre que sea posible, usa una hoja de Sharesheet 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 mostrar acciones de selección de texto personalizadas
Cuando los usuarios seleccionan texto en tu app, una barra de herramientas de selección de texto muestra el conjunto de operaciones que pueden realizar en él. Si esta barra de herramientas debe mostrar acciones personalizadas de otras apps, incluye el siguiente elemento <intent>
como parte del elemento <queries>
en tu manifiesto:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.PROCESS_TEXT" /> <data android:mimeType="text/plain" /> </intent>
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 un servicio de reconocimiento de voz
Si tu app interactúa con un servicio de reconocimiento de voz, incluye el siguiente elemento <intent>
como parte del elemento <queries>
de tu manifiesto:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.speech.RecognitionService" /> </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 personalizadas para un contacto
Las apps pueden agregar filas de datos personalizadas al Proveedor de contactos. Para que una app de contactos muestre estos datos personalizados, debe ser capaz de realizar lo siguiente:
- Leer el archivo
contacts.xml
de las otras apps - 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 />
Si incluyes esta declaración, puedes verificar si la app host está instalada e 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.