Cómo recibir datos simples de otras apps

Al igual que 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.

A menudo, los usuarios envían datos a tu app mediante Android Sharesheet o el agente de resolución de intent. Todos los datos recibidos tienen un tipo de MIME configurado por la app que los proporciona. Tu app puede recibir los datos que envía otra de las siguientes tres maneras:

  • Un elemento Activity con una etiqueta intent-filter que coincida en el manifiesto
  • Uno o más objetos ChooserTarget mostrados por tu ChooserTargetService
  • Accesos directos de uso compartido publicados por tu app, que sustituyen 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 para un elemento Activity específico dentro de tu app. 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 videos, 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 IANA de 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 directo

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.

Al presionar en cualquier objetivo de Direct Share, el usuario debería ir directamente a una interfaz en la que una acción puede realizarse 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. Específicamente, no lleves al usuario a una interfaz de desambiguación de contactos en la que deban confirmar o volver a seleccionar el contacto con quien quieren compartir contenido, ya que lo hicieron al presionar 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 autcompletarse con los datos compartidos.

Cóm recibir datos con una actividad

Actualiza tu manifiesto

Los filtros de intents informan al sistema qué intents estará 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. El filtro de intents se define 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 mediante la construcción de un intent y el paso del mismo 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, dependerá de ti que manejes correctamente el contenido dentro de tu IU y código.

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

Cómo administrar el contenido entrante

Para administrar el contenido enviado 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, 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 hacerlo 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 complicado, como aplicar un filtro de foto interesante a una imagen. Tú decides qué sucede luego.

Asegúrate de que los usuarios reconozcan tu app

Tu app aparece representada con su ícono y etiqueta en Android Sharesheet y el agente de resolución de intents. Ambos se definen en el manifiesto. Puedes configurar la actividad o las etiquetas de filtro de intents para proporcionar más contexto.

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

Nota: Los mejores objetivos de uso compartido no necesita una etiqueta y un ícono en la actividad asociada ni el filtro de intent. 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 cargarán 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, la API de accesos directos de uso compartido les permite a las apps publicar objetivos de uso compartido directos con anticipación. El mecanismo ChooserTargetService de Direct Share seguirá funcionando, pero los objetivos proporcionados de esta manera podrían tener una prioridad inferior a la de los objetivos que usen la API de accesos directos de uso compartido.

ShortcutManagerCompat es una API de AndroidX que brinda retrocompatibilidad con la antigua API ChooserTargetService de Direct Share. Esta es la manera preferida de publicar accesos directos de uso compartido y ChooserTargets. Observa las instrucciones 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 uso compartido 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. Puedes acceder a los accesos directos, agregarlos, actualizarlos y quitarlos mediante ShortcutManager o ShortcutManagerCompat en AndroidX. El uso de una biblioteca de compatibilidad en AndroidX es el método preferido, ya que proporciona retrocompatibilidad con versiones anteriores de Android.

API de accesos directos de uso compartido

ShortcutInfo.Builder incluye nuevos métodos mejorados que brindan 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 la sección Cómo declarar un objetivo de uso compartido a continuación. Este campo es obligatorio para los accesos directos que deben usarse 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, puede almacenarse 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. Puede usarse 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 pueden asociarse 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 típica app de mensajes, debería publicarse un acceso directo de uso compartido separado para cada contacto, y el campo Person debería contener la información del contacto. Si se puede asociar el objetivo con varias personas (como un chat en grupo), agrega varios elementos Person a un único objetivo de uso compartido.

Al publicar un acceso directo para una persona, incluye su nombre completo en setLongLabel() y cualquier nombre breve, como un sobrenombre o el nombre de pila, en setShortLabel()

Para ver un ejemplo de accesos directos de uso compartido, consulta el código de ejemplo de accesos directos de uso compartido.

Cómo obtener la mejor clasificación

Android Sharesheet muestra una cantidad fija de objetivos de Direct Share. Estas sugerencias se ordenan 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 aumentar la clasificación, recomendamos que las aplicaciones sociales lleven a cabo todo lo mencionado anteriormente y lo siguiente:

  • Reemplaza los objetos Person relevantes con una clave establecida en tu acceso directo, consulta setPerson(), setPersons() y setKey().
  • Vincula tu acceso directo a notificaciones relevantes de la misma persona o el mismo grupo de personas, consulta setShortcutId(). El valor de shortcutIds puede ser para 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, brindar un URI válido para un contacto asociado del 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 modificarse. Para garantizar 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 para 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 coincidentes y la clase de objetivo que controlará el intent de uso 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 intent. 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 definidos por app aleatorios.

Si el usuario selecciona un objetivo de uso compartido (acceso directo) en Sharesheet que coincide con el target-share 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 deberás incluir el ID manualmente si lo necesitas.

Uso de AndroidX para proporcionar accesos directos de uso compartido y ChooserTargets

Para poder trabajar con la biblioteca de compatibilidad, el manifiesto de la app debe contener el conjunto meta-data + chooser-target-service + intent-filters. Consulta la API ChooserTargetService de Direct Share actual.

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, debe tomarse 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, que ya se define 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 usa un modelo externo, en comparación con el modelo interno que usaba la antigua API de Direct Share. De esta manera, el proceso de recuperar objetivos de Direct Share es mucho más rápido al preparar 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 mensajes).

¿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 de Direct Share 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 lanzador)?

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).