Cómo recibir contenido enriquecido

Figura 1: La API unificada proporciona un único lugar para controlar el contenido entrante, independientemente del mecanismo de IU específico, como pegar desde el menú de mantener presionado o arrastrar y soltar.

A los usuarios les encantan las imágenes, los videos y otros contenidos expresivos, pero insertar y mover este contenido en apps no siempre es fácil. Para que las apps puedan recibir contenido enriquecido de manera más sencilla, Android 12 (nivel de API 31) introduce una API unificada que permite que tu app acepte contenido de cualquier fuente: portapapeles, teclado o arrastre.

Puedes adjuntar una interfaz, como OnReceiveContentListener, a los componentes de la IU y obtener una devolución de llamada cuando se inserte contenido a través de cualquier mecanismo. La devolución de llamada se convierte en el único lugar para que tu código controle la recepción de todo el contenido, desde texto sin formato y estilo hasta lenguaje de marcado, imágenes, videos, archivos de audio y otros elementos.

Para ofrecer retrocompatibilidad con versiones anteriores de Android, esta API también está disponible en AndroidX, a partir de Core 1.7 y Appcompat 1.4, que te recomendamos que uses cuando implementes esta funcionalidad.

Descripción general

Con otras APIs existentes, cada mecanismo de la IU, como el menú de mantener presionado o el arrastre, tiene su propia API correspondiente. Eso significa que debes realizar la integración con cada API por separado, lo que agrega código similar para cada mecanismo que inserte contenido:

Una imagen que muestra las diferentes acciones y la API relativa que se debe implementar
Figura 2: Anteriormente, las apps implementaban una API diferente para cada mecanismo de IU con el objetivo de insertar contenido.

La API de OnReceiveContentListener consolida estas rutas de código diferentes creando una sola API para que puedas enfocarte en la lógica específica de tu app y permitir que la plataforma controle el resto:

Una imagen que muestra la API unificada simplificada
Figura 3: La API unificada te permite implementar una sola API que admita todos los mecanismos de la IU.

Este enfoque también significa que cuando se agregan nuevas formas de insertar contenido a la plataforma, no necesitas realizar cambios de código adicionales para habilitar la compatibilidad en tu app. Si tu app necesita implementar una personalización completa para un caso de uso en particular, puedes usar las APIs existentes, que continúan funcionando de la misma manera.

Implementación

La API es una interfaz de objeto de escucha con un único método, OnReceiveContentListener. Para admitir versiones anteriores de la plataforma de Android, recomendamos el uso de la interfaz OnReceiveContentListener coincidente en la biblioteca de AndroidX Core.

Para usar la API, implementa el objeto de escucha especificando qué tipos de contenido puede controlar tu app:

Kotlin

object MyReceiver : OnReceiveContentListener {
    val MIME_TYPES = arrayOf("image/*", "video/*")
    
    // ...
    
    override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? {
        TODO("Not yet implemented")
    }
}

Java

public class MyReceiver implements OnReceiveContentListener {
     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
     // ...
}

Después de especificar todos los tipos de MIME de contenido que admite tu app, implementa el resto del objeto de escucha:

Kotlin

class MyReceiver : OnReceiveContentListener {
    override fun onReceiveContent(view: View, contentInfo: ContentInfoCompat): ContentInfoCompat {
        val split = contentInfo.partition { item: ClipData.Item -> item.uri != null }
        val uriContent = split.first
        val remaining = split.second
        if (uriContent != null) {
            // App-specific logic to handle the URI(s) in uriContent.
        }
        // Return anything that your app didn't handle. This preserves the
        // default platform behavior for text and anything else that you aren't
        // implementing custom handling for.
        return remaining
    }

    companion object {
        val MIME_TYPES = arrayOf("image/*", "video/*")
    }
}

Java

 public class MyReceiver implements OnReceiveContentListener {
     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};

     @Override
     public ContentInfoCompat onReceiveContent(View view, ContentInfoCompat contentInfo) {
         Pair split = contentInfo.partition(
                 item -> item.getUri() != null);
         ContentInfo uriContent = split.first;
         ContentInfo remaining = split.second;
         if (uriContent != null) {
             // App-specific logic to handle the URI(s) in uriContent.
         }
         // Return anything that your app didn't handle. This preserves the
         // default platform behavior for text and anything else that you aren't
         // implementing custom handling for.
         return remaining;
     }
 }

Si tu app ya admite el uso compartido con intents, puedes volver a usar la lógica específica de la app para controlar los URI de contenido. Muestra los datos restantes para delegar su control a la plataforma.

Después de implementar el objeto de escucha, configúralo en los elementos de la IU adecuados de tu app:

Kotlin

class MyActivity : Activity() {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        val myInput = findViewById(R.id.my_input)
        ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, MyReceiver())
    }
}

Java

public class MyActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         // ...

         AppCompatEditText myInput = findViewById(R.id.my_input);
         ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, new MyReceiver());
     }
}

Permisos de URI

La plataforma otorga y libera los permisos de lectura automáticamente para cualquier URI de contenido en la carga útil que se pasa a OnReceiveContentListener.

Por lo general, tu app procesa los URI de contenido en un servicio o una actividad. Para un procesamiento de larga duración, usa WorkManager. Cuando implementes esto, extiende los permisos al servicio o la actividad de destino pasando el contenido con Intent.setClipData y configurando la marca FLAG_GRANT_READ_URI_PERMISSION.

De manera alternativa, puedes usar un subproceso en segundo plano dentro del contexto actual para procesar el contenido. En este caso, debes mantener una referencia al objeto payload que recibe el objeto de escucha para garantizar que la plataforma no revoque los permisos de manera prematura.

Vistas personalizadas

Si tu app usa una subclase View personalizada, asegúrate de que no se omita el OnReceiveContentListener.

Si tu clase View anula el método onCreateInputConnection, usa la API de Jetpack InputConnectionCompat.createWrapper para configurar InputConnection.

Si tu clase View anula el método onTextContextMenuItem, delega a super cuando el elemento de menú sea R.id.paste o R.id.pasteAsPlainText.

Comparación con la API de imagen de teclado

Puedes considerar la API de OnReceiveContentListener como la próxima versión de la API de imagen de teclado existente. Esta API unificada admite la funcionalidad de la API de imagen de teclado y algunas funciones adicionales. La compatibilidad con los dispositivos y las funciones varía según si usas la biblioteca de Jetpack o las API nativas desde el SDK de Android.

Tabla 1: Funciones y niveles de API compatibles con Jetpack.
Acción o función Compatible con la API de imágenes de teclado Compatible con la API unificada
Insertar desde el teclado Sí (nivel de API 13 y versiones posteriores) Sí (nivel de API 13 y versiones posteriores)
Insertar con la función de pegar desde el menú de mantener presionado No
Insertar con la función de arrastrar y soltar No Sí (nivel de API 24 y versiones posteriores)
Tabla 2: Funciones y niveles de API admitidos para las APIs nativas.
Acción o función Compatible con la API de imágenes de teclado Compatible con la API unificada
Insertar desde el teclado Sí (nivel de API 25 y versiones posteriores) Sí (Android 12 y versiones posteriores)
Insertar con la función de pegar desde el menú de mantener presionado No
Insertar con la función de arrastrar y soltar No