Receber conteúdo avançado

Figura 1. A API unificada oferece um único local para processar o conteúdo recebido, independente do mecanismo da interface específico, como colar do menu "Tocar e pressionar" ou usar 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 (nível 31 da API) introduz uma API unificada que permite que o app aceite conteúdo de qualquer fonte: área de transferência, teclado ou arrastar.

Você pode 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 oferecer compatibilidade com versões anteriores do Android, essa API também está disponível no AndroidX, no Core 1.7 e no Appcompat 1.4, que recomendamos usar ao implementar essa funcionalidade.

Informações gerais

Com outras APIs, cada mecanismo de interface tem a própria API correspondente, como o menu "tocar e pressionar" ou arrastar. 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. Anteriormente, os apps implementavam uma API diferente para cada mecanismo de interface para inserção de 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:

Imagem que mostra 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 outras mudanças no código para oferecer suporte ao seu app. Se o app precisar implementar personalização completa para um caso de uso específico, você ainda poderá usar as APIs existentes, que continuam funcionando da mesma maneira.

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 o 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 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;
     }
 }

Se o app já oferece suporte ao 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 passados para o OnReceiveContentListener.

Normalmente, o app processa URIs de conteúdo em um serviço ou uma atividade. Para processamento de longa duração, use o WorkManager. Ao implementar isso, estenda as permissões para o serviço ou a atividade de destino transmitindo o conteúdo com Intent.setClipData e configurando 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

Caso seu app use uma subclasse View personalizada, verifique se a OnReceiveContentListener não é ignorada.

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 para super quando o item de 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 e níveis de API compatíveis com o 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 usando o recurso "Colar" a partir do menu "Tocar e pressionar" Não Sim
Inserir usando o recurso de arrastar e soltar Não Sim (nível 24 da API e versões mais recentes)
Tabela 2. Recursos e níveis de API compatíveis com 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 usando o recurso "Colar" a partir do menu "Tocar e pressionar" Não
Inserir conteúdo usando o recurso de arrastar e soltar Não