Receber conteúdo avançado

Figura 1. A API unificada oferece um único local para processar o conteúdo recebido, independentemente do mecanismo da interface específico, como colar do menu de 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 facilitar 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 ação de arrastar.

É possível anexar uma interface, como OnReceiveContentListener, a componentes de interface 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 da AppCompat 1.4, que recomendamos que você use ao implementar essa funcionalidade.

Informações gerais

Com outras APIs já existentes, cada mecanismo de interface, como o menu de tocar e pressionar ou de arrastar, tem a própria 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 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:

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

Essa abordagem também significa que, quando novas maneiras de inserir conteúdo forem adicionadas à plataforma, não será necessário fazer outras mudanças no código para ativar o suporte ao seu app. Se o app precisar implementar uma personalização completa para um caso de uso específico, você ainda poderá usar as APIs já existentes, 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 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 transmitido ao 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 ao serviço ou à 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 ajudar a 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 a 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 com suporte do 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 "Colar" do menu "Tocar e manter pressionado" 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 com suporte 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 usando "Colar" do menu "Tocar e manter pressionado" Não
Inserir conteúdo usando o recurso de arrastar e soltar Não