Cómo recibir datos simples de otras apps

Así como una app puede enviar datos a otras apps, también puede recibirlos de ellas. Piensa en la manera en que los usuarios interactúan con tu aplicación y qué tipos de datos quieres recibir de otras. Por ejemplo, una aplicación de red social podría estar interesada en recibir de otra app contenido de texto, como una URL web interesante.

Los usuarios suelen enviar datos a tu app mediante Android Sharesheet o el agente de resolución de intent. La app que proporciona los datos recibidos establece su tipo de MIME. Tu app puede recibir datos enviados por otra app de las siguientes tres maneras:

  • Un elemento Activity con una etiqueta intent-filter correspondiente en el manifiesto
  • Uno o más objetos ChooserTarget mostrados por tu ChooserTargetService
  • Accesos directos de uso compartido publicados por tu app, que reemplazan a los objetos ChooserTarget (los accesos directos de uso compartido solo están disponibles si tu app ejecuta Android 10, API nivel 29)

Los accesos directos de uso compartido y los objetos ChooserTarget son vínculos directos de Direct Share en un elemento Activity específico dentro de tu aplicación. Suelen representar a una persona, y Android Sharesheet los muestra. Por ejemplo, una aplicación de mensajería puede proporcionar a una persona un acceso directo de uso compartido que contenga un vínculo directo a una conversación con esa persona.

Compatibilidad con tipos de MIME

Tu app debería poder recibir la mayor variedad de tipos de MIME posible. Por ejemplo, una app de mensajería que se usa para enviar texto, imágenes y video debería poder recibir text/*, image/* y video/*. A continuación, puedes ver algunos tipos de MIME comunes que se usan al enviar datos simples en Android.

  • text/*: los remitentes suelen enviar text/plain, text/rtf, text/html y text/json
  • image/*: los remitentes suelen enviar image/jpg, image/png y image/gif
  • video/*: los remitentes suelen enviar video/mp4 y video/3gp
  • Los receptores deberían registrarse para extensiones de archivos compatibles; los remitentes suelen enviar application/pdf

Consulta el registro oficial de la IANA de los tipos de medios MIME. Puedes recibir un tipo de MIME de */*. Sin embargo, no es recomendable, a menos que puedas manejar correctamente cualquier tipo de contenido entrante.

Cómo crear grandes objetivos de uso compartido

Cuando un usuario presiona un objetivo de uso compartido asociado con una actividad específica, debería poder confirmar y editar el contenido compartido antes de usarlo. Esto resulta particularmente importante para datos de texto.

Si presiona en cualquier objetivo de Direct Share, el usuario debería ir directamente a una interfaz en la que se pueda realizar una acción directamente en el asunto del objetivo. Evita mostrar a los usuarios una desambiguación o colocarlos en una interfaz que no está relacionada con el objetivo presionado. En particular, no lleves al usuario a una interfaz de desambiguación de contactos en la que deba confirmar o volver a seleccionar el contacto con quien quiere compartir contenido, ya que ya lo hizo cuando presionó el objetivo en Android Sharesheet. Por ejemplo, en una app de mensajería, presionar un objetivo de Direct Share llevaría al usuario a una vista de conversación con la persona seleccionada. El teclado debería ser visible y el mensaje debería autocompletarse con los datos compartidos.

Cómo recibir datos con una actividad

Actualiza tu manifiesto

Los filtros de intents informan al sistema qué intents está dispuesto a aceptar un componente de la aplicación. Así como construiste un intent con una acción ACTION_SEND en la lección Cómo enviar datos simples a otras apps, puedes crear filtros de intents para poder recibir intents con esta acción. Se define el filtro de intents en tu manifiesto con el elemento <intent-filter>. Por ejemplo, si tu aplicación puede recibir contenido de texto, una sola imagen de cualquier tipo o varias imágenes de cualquier tipo, tu manifiesto se vería de la siguiente manera:

<activity android:name=".ui.MyActivity" >
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND_MULTIPLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
</activity>

Cuando otra aplicación intente compartir cualquiera de estos elementos construyendo un intent y pasándolo a startActivity(), tu aplicación aparecerá como opción en Android Sharesheet o el agente de resolución de intents. Si el usuario selecciona tu aplicación, se iniciará la actividad correspondiente (.ui.MyActivity en el ejemplo anterior). Luego, deberás manejar correctamente el contenido dentro de tu IU y tu código.

Nota: Para obtener más información sobre los filtros y la resolución de intents, lee Intents y filtros de intents.

Cómo administrar el contenido entrante

A fin de controlar el contenido publicado por un Intent, llama a getIntent() para obtener el objeto Intent. Cuando lo tengas, podrás examinar su contenido a fin de determinar qué hacer luego. Ten en cuenta que, si esta actividad puede iniciarse desde otras partes del sistema, como el selector, tendrás que tenerlo presente cuando examines el intent.

Presta especial atención a revisar los datos entrantes, ya que nunca se sabe qué podría enviarte otra aplicación. Por ejemplo, podría estar configurado el tipo de MIME incorrecto o la imagen enviada podría ser extremadamente grande. También recuerda procesar los datos binarios en un subproceso independiente en lugar de en el subproceso principal ("IU").

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    when {
        intent?.action == Intent.ACTION_SEND -> {
            if ("text/plain" == intent.type) {
                handleSendText(intent) // Handle text being sent
            } else if (intent.type?.startsWith("image/") == true) {
                handleSendImage(intent) // Handle single image being sent
            }
        }
        intent?.action == Intent.ACTION_SEND_MULTIPLE
                && intent.type?.startsWith("image/") == true -> {
                handleSendMultipleImages(intent) // Handle multiple images being sent
        }
        else -> {
            // Handle other intents, such as being started from the home screen
        }
    }
    ...
}

private fun handleSendText(intent: Intent) {
    intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
        // Update UI to reflect text being shared
    }
}

private fun handleSendImage(intent: Intent) {
    (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)?.let {
        // Update UI to reflect image being shared
    }
}

private fun handleSendMultipleImages(intent: Intent) {
    intent.getParcelableArrayListExtra<Parcelable>(Intent.EXTRA_STREAM)?.let {
        // Update UI to reflect multiple images being shared
    }
}

Java

void onCreate (Bundle savedInstanceState) {
    ...
    // Get intent, action and MIME type
    Intent intent = getIntent();
    String action = intent.getAction();
    String type = intent.getType();

    if (Intent.ACTION_SEND.equals(action) && type != null) {
        if ("text/plain".equals(type)) {
            handleSendText(intent); // Handle text being sent
        } else if (type.startsWith("image/")) {
            handleSendImage(intent); // Handle single image being sent
        }
    } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
        if (type.startsWith("image/")) {
            handleSendMultipleImages(intent); // Handle multiple images being sent
        }
    } else {
        // Handle other intents, such as being started from the home screen
    }
    ...
}

void handleSendText(Intent intent) {
    String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
    if (sharedText != null) {
        // Update UI to reflect text being shared
    }
}

void handleSendImage(Intent intent) {
    Uri imageUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
    if (imageUri != null) {
        // Update UI to reflect image being shared
    }
}

void handleSendMultipleImages(Intent intent) {
    ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
    if (imageUris != null) {
        // Update UI to reflect multiple images being shared
    }
}

La actualización de la IU después de recibir los datos puede ser tan simple como completar un elemento EditText o puede resultar más complicada, como cuando se aplica un filtro de foto interesante a una imagen. Tú decides qué sucede luego.

Cómo asegurarte de que los usuarios reconozcan tu app

Tu app aparece representada con el ícono y la etiqueta en Android Sharesheet y en el agente de resolución de intents. Ambos se definen en el manifiesto. Puedes establecer etiquetas de filtros de intent o actividad para brindar más contexto.

A partir de Android 10 (API nivel 29), Android Sharesheet solo usará íconos configurados en el manifiesto en tu etiqueta application y se ignorarán los configurados en las etiquetas intent-filter y activity.

Nota: Los mejores objetivos de uso compartido no necesitan una etiqueta y un ícono en la actividad o el filtro de intents asociados. El nombre de la app receptora y el ícono bastan para que los usuarios entiendan lo que sucederá después de iniciar el uso compartido.

Cómo proporcionar objetivos de Direct Share

Direct Share se incorporó a Android 6.0 (API nivel 23) y permitía a las apps proporcionar objetos ChooserTarget mediante un objeto ChooserTargetService. Los resultados se recuperaban de manera reactiva a pedido, lo que hacía que los objetivos se cargaran lentamente.

En Android 10 (API nivel 29), las API ChooserTargetService de Direct Share se reemplazaron por la nueva API de accesos directos de uso compartido. En lugar de obtener resultados de manera reactiva a pedido, esta API permite que las apps publiquen con anticipación objetivos directos de uso compartido. El mecanismo ChooserTargetService de Direct Share seguirá funcionando, pero los objetivos proporcionados de esta manera podrían tener una prioridad inferior a los que usen la API de accesos directos de uso compartido.

ShortcutManagerCompat es una API de AndroidX que proporciona accesos directos de uso compartido que son retrocompatibles con la antigua API ChooserTargetService de Direct Share. Esta es la manera preferida de publicar accesos directos de uso compartido y ChooserTargets. Consulta las instrucciones que se muestran a continuación.

Cómo publicar objetivos de Direct Share

Solo puedes usar accesos directos dinámicos para publicar objetivos de Direct Share. Sigue estos pasos para publicar objetivos de Direct Share con la nueva API:

  1. Declara los elementos de los objetivos de uso compartido en el archivo XML de recursos de la app. Para obtener más información, consulta la sección Cómo declarar un objetivo de uso compartido a continuación.
  2. Publica accesos directos dinámicos cuyas categorías coincidan con los objetivos de uso compartido declarados. Con ShortcutManager o ShortcutManagerCompat en AndroidX, es posible agregar accesos directos, actualizarlos, quitarlos y acceder a ellos. El método preferido consiste en usar una biblioteca de compatibilidad en AndroidX, ya que proporciona retrocompatibilidad con versiones anteriores de Android.

API de accesos directos de uso compartido

ShortcutInfo.Builder incluye métodos nuevos y mejorados que proporcionan información adicional sobre el objetivo de uso compartido:

setCategories()
Este no es un método nuevo; sin embargo, ahora también se usan las categorías para filtrar los accesos directos que pueden administrar intents o acciones de uso compartido. Para obtener más información, consulta Cómo declarar un objetivo de uso compartido a continuación. Este campo es obligatorio para los accesos directos que se deben usar como objetivos de uso compartido.
setLongLived()

Especifica si un acceso directo es válido o no cuando la app anula su publicación o lo oculta (ya sea un acceso directo dinámico o fijo). Si un acceso directo es permanente, se puede almacenar en la caché de diversos servicios del sistema, incluso en el caso de que se anule su publicación como acceso directo dinámico.

Hacer que un acceso directo sea permanente puede mejorar su clasificación. Para obtener más información, consulta Cómo obtener la mejor clasificación.

setPerson(), setPersons()

Asocia uno o más objetos Person con el acceso directo. Se puede usar para comprender mejor el comportamiento del usuario en diferentes apps y ayudar a los servicios de predicción potenciales del marco de trabajo a brindar mejores sugerencias en ShareSheet. Agregar información de Person a un acceso directo es algo opcional, pero lo recomendamos si el objetivo de uso compartido puede asociarse a un usuario. Ten en cuenta que algunos objetivos de uso compartido, como la nube, no se pueden asociar a una persona.

Incluir un objeto Person específico con una clave única en un objetivo de uso compartido y en notificaciones relacionadas puede mejorar su clasificación. Para obtener más información, consulta Cómo obtener la mejor clasificación.

En el caso de una app de mensajería típica, se debería publicar un acceso directo de uso compartido separado para cada contacto, y el campo Person debería contener la información del contacto. Si el objetivo se puede asociar con varias personas (como un chat en grupo), agrega varios objetos Person a un único objetivo de uso compartido.

Cuando publiques un acceso directo para una persona en particular, incluye su nombre completo en setLongLabel() y cualquier nombre corto, como un sobrenombre o un nombre de pila en setShortLabel().

En código de muestra de accesos directos de uso compartido puedes ver un ejemplo de cómo publicarlos.

Cómo obtener la mejor clasificación

Android Sharesheet muestra una cantidad fija de objetivos de Direct Share. Estas sugerencias están ordenadas según su clasificación. Puedes hacer lo siguiente para mejorar potencialmente la clasificación de tus accesos directos:

  • Asegúrate de que todos los shortcutIds sean únicos y que nunca se reutilicen para diferentes objetivos.
  • Asegúrate de que el acceso directo sea permanente. Para ello, llama a setLongLived(true).
  • Proporciona una clasificación que pueda usarse para comparar accesos directos de tu app en caso de que no haya datos de preparación. Consulta setRank(). Una clasificación más baja significa que el acceso directo es más importante.

Para mejorar aún más la clasificación, recomendamos que las aplicaciones sociales lleven a cabo todo lo mencionado anteriormente y lo siguiente:

  • Proporciona objetos Person relevantes con una clave configurada en el acceso directo; consulta setPerson(), setPersons() y setKey().
  • Vincula tu acceso directo con notificaciones relevantes de la misma persona o el mismo grupo de personas; consulta setShortcutId().. El shortcutId puede ser de cualquier acceso directo publicado anteriormente con setLongLived(true).

Para obtener la clasificación máxima, las aplicaciones sociales pueden hacer todo lo mencionado arriba y lo siguiente:

  • En los objetos Person proporcionados, brinda un URI válido a un contacto asociado en el dispositivo; consulta setUri().

A continuación, puedes ver un acceso directo de ejemplo con potenciales mejoras de clasificación.

Kotlin

val person = Person.Builder()
  ...
  .setName(fullName)
  .setKey(staticPersonIdentifier)
  .setUri("tel:$phoneNumber") // alternatively "mailto:$email" or CONTENT_LOOKUP_URI
  .build()

val shortcutInfo = ShortcutInfoCompat.Builder(myContext, staticPersonIdentifier)
  ...
  .setShortLabel(firstName)
  .setLongLabel(fullName)
  .setPerson(person)
  .setLongLived(true)
  .setRank(personRank)
  .build()

Java

Person person = Person.Builder()
  ...
  .setName(fullName)
  .setKey(staticPersonIdentifier)
  .setUri("tel:"+phoneNumber) // alternatively "mailto:"+email or CONTENT_LOOKUP_URI
  .build();

ShortcutInfoCompat shortcutInfo = ShortcutInfoCompat.Builder(myContext, staticPersonIdentifier)
  ...
  .setShortLabel(firstName)
  .setLongLabel(fullName)
  .setPerson(person)
  .setLongLived(true)
  .setRank(personRank)
  .build();

Kotlin

val notif = NotificationCompat.Builder(myContext, channelId)
  ...
  .setShortcutId(staticPersonIdentifier)
  .build()

Java

Notification notif = NotificationCompat.Builder(myContext, channelId)
  ...
  .setShortcutId(staticPersonIdentifier)
  .build();

Cómo proporcionar imágenes para accesos directos

Para crear un acceso directo de uso compartido, tendrás que agregar una imagen mediante setIcon().

El acceso directo de uso compartido puede aparecer en varias superficies del sistema y se puede modificar. Para asegurarte de que tu acceso directo se vea como corresponde, proporciona un mapa de bits adaptable mediante IconCompat.createWithAdaptiveBitmap().

Los mapas de bits adaptables deberían seguir las mismas pautas y dimensiones configuradas para íconos adaptables. La forma más común de lograrlo es ajustar el mapa de bits cuadrado pretendido a 72 x 72 dp y centrarlo dentro de un recuadro transparente de 108 x 108 dp.

No proporciones imágenes enmascaradas en una forma específica. Por ejemplo, en Android 10 (API nivel 29), era común proporcionar avatares de usuarios para objetos ChooserTarget de Direct Share que estaban enmascarados en un círculo. Android Sharesheet y otras superficies del sistema de Android 10 ahora ofrecen formas y temas para las imágenes de accesos directos. El método preferido para proporcionar accesos directos de uso compartido, mediante ShortcutManagerCompat, dará forma automáticamente a fin de brindar retrocompatibilidad con objetos ChooserTarget de Direct Share para círculos.

Cómo declarar un objetivo de uso compartido

Los objetivos de uso compartido deben declararse en el archivo de recursos de la app, de manera similar a las definiciones de accesos directos estáticos. Agrega definiciones de objetivos de uso compartido dentro del elemento raíz <shortcuts> en el archivo de recursos, junto con otras definiciones de accesos directos estáticos. Cada elemento <share-targets> contiene información sobre el tipo de datos compartidos, las categorías que coinciden y la clase de objetivo que manejará el intent compartido. El código XML se verá así:

<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
  <share-target android:targetClass="com.example.android.sharingshortcuts.SendMessageActivity">
    <data android:mimeType="text/plain" />
    <category android:name="com.example.android.sharingshortcuts.category.TEXT_SHARE_TARGET" />
  </share-target>
</shortcuts>

El elemento de datos en un objetivo de uso compartido es similar al de la especificación de datos en un filtro de intents. Cada objetivo de uso compartido puede tener varias categorías, que solo se usan para hacer coincidir los accesos directos publicados de una app con sus definiciones de objetivo de uso compartido. Las categorías pueden tener valores aleatorios definidos por las apps.

Si el usuario selecciona un acceso directo de uso compartido en Android Sharesheet que coincide con el objetivo del ejemplo anterior, la app obtendrá el siguiente intent de uso compartido:

Action: Intent.ACTION_SEND
ComponentName: {com.example.android.sharingshortcuts /
                com.example.android.sharingshortcuts.SendMessageActivity}
Data: Uri to the shared content
EXTRA_SHORTCUT_ID: <ID of the selected shortcut>

Si el usuario abre el objetivo de uso compartido desde los accesos directos del selector, la app obtendrá el intent que se haya creado al agregar el acceso directo de uso compartido a ShortcutManagerCompat. Como es un intent diferente, Intent.EXTRA_SHORTCUT_ID no estará disponible, por lo que tendrás que pasar el ID manualmente si lo necesitas.

Cómo usar AndroidX para proporcionar accesos directos de uso compartido y ChooserTargets

Para poder trabajar con la biblioteca de compatibilidad de AndroidX, el manifiesto de la app debe contener el conjunto meta-data + chooser-target-service + intent-filters. Obtén información sobre la API ChooserTargetService de Direct Share.

Este servicio ya está declarado en la biblioteca de compatibilidad, por lo que no es necesario que el usuario lo declare en el manifiesto de la app. Sin embargo, se debe tomar el vínculo de actividad de uso compartido con el servicio como proveedor para el selector de objetivos.

En el siguiente ejemplo, la implementación de ChooserTargetService es androidx.core.content.pm.ChooserTargetServiceCompat y ya está definida en AndroidX:

<activity
    android:name=".SendMessageActivity"
    android:label="@string/app_name"
    android:theme="@style/SharingShortcutsDialogTheme">
    <!-- This activity can respond to Intents of type SEND -->
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    <!-- Only needed if you import the sharetarget AndroidX library that
         provides backwards compatibility with the old DirectShare API.
         The activity that receives the Sharing Shortcut intent needs to be
         taken into account with this chooser target provider. -->
    <meta-data
        android:name="android.service.chooser.chooser_target_service"
        android:value="androidx.sharetarget.ChooserTargetServiceCompat" />
</activity>

Preguntas frecuentes sobre accesos directos de uso compartido

¿Cuáles son las principales diferencias entre la API de accesos directos de uso compartido actual y la antigua API de ChooserTargetService de Direct Share?

La API de accesos directos de uso compartido utiliza un modelo push, en comparación con el modelo pull que se usaba en la antigua API de Direct Share. De esta manera, el proceso de recuperar objetivos de Direct Share es mucho más rápido cuando se prepara Sharesheet. Desde el punto de vista del desarrollador de la app, al usar la nueva API, la app debe proporcionar la lista de objetivos de uso compartido directo con anticipación y, potencialmente, actualizar la lista de accesos directos cada vez que cambie su estado interno (por ejemplo, si se agrega un nuevo contacto en una app de mensajería).

¿Qué sucede si no realizo la migración para usar las API de accesos directos de uso compartido?

En Android 10 (API nivel 29) y versiones posteriores, Android Sharesheet proporcionará mayor prioridad a los objetivos de uso compartido que se proporcionan mediante ShortcutManager con la API de accesos directos de uso compartido. Por lo tanto, los objetivos de uso compartido publicados podrían quedar ocultos por los de otras apps y quizás nunca aparezcan durante el uso compartido.

¿Puedo usar las API de ChooserTargetService y de accesos directos de uso compartido en mi app para brindar retrocompatibilidad?

No lo hagas. En su lugar, usa las API que se proporcionan en la biblioteca de asistencia ShortcutManagerCompat. Si combinas ambos conjuntos de API, es posible que notes comportamientos inesperados o no deseados durante la obtención de objetivos de uso compartido.

¿En qué se diferencian los accesos directos publicados para objetivos de uso compartido de los accesos directos de selector (el típico uso de accesos directos por el cual se mantienen presionados los íconos de las apps en el selector)?

Los accesos directos que se publiquen para un "objetivo de uso compartido" también serán accesos directos del selector y se mostrarán en el menú cuando se mantenga presionado el ícono de tu app. El límite máximo de accesos directos por actividad también se aplica a la cantidad total de accesos directos que publica una app (incluye objetivos de uso compartido y accesos directos de selector heredados).