Receber conteúdo avançado

Figura 1. A API unificada oferece um único local para processar o conteúdo recebido, independentemente do mecanismo de UI específico, como conteúdo originado da ação de colar após tocar e manter pressionado ou usando o recurso de arrastar e soltar.

Os usuários adoram imagens, vídeos e outros conteúdos expressivos, mas inserir e mover esses conteúdos em apps nem sempre é fácil. Para simplificar o recebimento de conteúdo avançado pelos apps, o Android 12 (API de nível 31) introduziu uma API unificada que permite que o app aceite conteúdo de qualquer fonte: área de transferência, teclado ou recurso de arrastar.

É possível anexar uma interface, como OnReceiveContentListener, aos componentes de IU e receber um callback quando o conteúdo for inserido usando qualquer mecanismo. O callback passa a ser o único local em que o código processa o recebimento de todos os conteúdos, desde textos simples e estilizados até marcações, imagens, vídeos, arquivos de áudio e outros.

Para compatibilidade com versões anteriores do Android, essa API também está disponível no AndroidX, a partir do Core 1.7 e do Appcompat 1.4, que recomendamos usar ao implementar essa funcionalidade.

Visão geral

Com as APIs existentes, cada mecanismo de interface, como o menu de tocar e manter pressionado ou o recurso de arrastar, tem uma API correspondente. Isso significa que você precisa integrar cada API separadamente, adicionando um código semelhante para cada mecanismo que insere conteúdo:

Uma imagem mostrando as diferentes ações e a API relativa a ser implementada
Figura 2. Antes, os apps implementavam uma API diferente para cada mecanismo de IU para inserir conteúdo.

A API OnReceiveContentListener unificada consolida esses diferentes caminhos de código, criando uma única API para implementação. Assim, você pode se concentrar na lógica específica do app e deixar a plataforma cuidar do restante:

Uma imagem mostrando a API unificada simplificada
Figura 3. A API unificada permite implementar uma única API compatível com todos os mecanismos de IU.

Essa abordagem também significa que, quando novas maneiras de inserir conteúdo são adicionadas à plataforma, não é necessário fazer mudanças adicionais no código para ativar o suporte no app. Além disso, se o app precisar implementar personalização completa para um caso de uso específico, ainda será possível usar as APIs atuais, que continuam funcionando da mesma forma.

Implementação

A API é uma interface de listener com um único método, o OnReceiveContentListener. Para oferecer compatibilidade com versões anteriores da plataforma Android, recomendamos o uso da interface OnReceiveContentListener correspondente na biblioteca AndroidX Core.

Para usar a API, implemente o listener especificando os tipos de conteúdo que seu app pode processar:

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/*"};
     // ...
}

Depois de especificar todos os tipos MIME de conteúdo compatíveis com o app, implemente o restante do listener:

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<ContentInfoCompat, ContentInfoCompat> 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;
     }
 }

Caso o app já seja compatível com o compartilhamento com intents, você pode reutilizar a lógica específica do app para processar URIs de conteúdo. Retorne todos os dados restantes para delegar o processamento desses dados à plataforma.

Depois de implementar o listener, defina-o nos elementos de IU adequados no 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());
     }
}

Permissões de URI

As permissões de leitura são concedidas e liberadas automaticamente pela plataforma para todos os URIs de conteúdo no payload transmitidos ao OnReceiveContentListener.

Normalmente, o app processa URIs de conteúdo em um serviço ou uma atividade. Para processamento de longa duração, use WorkManager. Ao implementar isso, estenda as permissões para o serviço ou a atividade de destino transmitindo o conteúdo usando Intent.setClipData e definindo a flag FLAG_GRANT_READ_URI_PERMISSION.

Como alternativa, você pode usar uma linha de execução em segundo plano no contexto atual para processar o conteúdo. Nesse caso, é necessário manter uma referência ao objeto payload recebido pelo listener para garantir que as permissões não sejam revogadas prematuramente pela plataforma.

Visualizações padrão

Se o app usa uma subclasse View personalizada, verifique se o OnReceiveContentListener não é ignorado.

Se a classe View substituir o método onCreateInputConnection, use a API Jetpack InputConnectionCompat.createWrapper para configurar a InputConnection.

Se a classe View substituir o método onTextContextMenuItem, delegue a Super quando o item do menu for R.id.paste ou R.id.pasteAsPlainText.

Comparação com a API de imagem de teclado

Pense na API OnReceiveContentListener como a próxima versão da API de imagem do teclado existente. Essa API unificada é compatível com a funcionalidade da API de imagem do teclado e com alguns outros recursos. A compatibilidade com dispositivos e recursos varia de acordo com o uso da biblioteca do Jetpack ou das APIs nativas do SDK do Android.

Tabela 1. Recursos compatíveis e níveis da API para Jetpack.
Ação ou recurso Compatível com a API de imagem do teclado Compatível com a API unificada
Inserir conteúdo pelo teclado Sim (nível 13 da API e versões mais recentes) Sim (nível 13 da API e versões mais recentes)
Inserir conteúdo colando-o do menu de tocar e manter pressionado Não Sim
Inserir usando o recurso de arrastar e soltar Não Sim (API de nível 24 e versões mais recentes)
Tabela 2. Recursos compatíveis e níveis da API para APIs nativas.
Ação ou recurso Compatível com a API de imagem do teclado Compatível com a API unificada
Inserir conteúdo pelo teclado Sim (nível 25 da API e versões mais recentes) Sim (Android 12 ou versões mais recentes)
Inserir conteúdo colando-o do menu de tocar e manter pressionado Não
Inserir conteúdo usando o recurso de arrastar e soltar Não