Compatibilidade com teclado de imagens

Os usuários geralmente querem se comunicar usando emojis, adesivos e outros tipos de conteúdo. Nas versões anteriores do Android, os teclados de software, também conhecidos como editores de métodos de entrada ou IMEs: podiam enviar apenas emojis Unicode para apps. Para conteúdo avançado, os apps criados APIs específicas do app que não podiam ser usadas em outros apps ou que usavam soluções alternativas, como enviar imagens com um compartilhamento simples ou a área de transferência.

Uma imagem mostrando um teclado compatível com a pesquisa de imagens
Figura 1. Exemplo de suporte ao teclado de imagens.

A partir do Android 7.1 (nível 25 da API), o SDK do Android inclui a API Commit API Content, que fornece uma maneira universal para os IMEs enviarem imagens e outros conteúdo avançado diretamente para um editor de texto em um aplicativo. A API também está disponível em Biblioteca de Suporte v13 a partir da revisão 25.0.0. Recomendamos o uso do Suporte Biblioteca, porque contém métodos auxiliares que simplificam a implementação.

Com essa API, é possível criar aplicativos de mensagens que aceitam conteúdo avançado de qualquer teclado, bem como teclados que podem enviar conteúdo avançado para qualquer aplicativo. A página Google Teclado e aplicativos como o Mensagens de Google oferecem suporte à API Commit Content no Android 7.1, como mostrado na figura 1.

Este documento mostra como implementar a API Commit Content em IMEs e apps.

Como funciona

A inserção de imagem do teclado exige a participação do IME e do app. A sequência a seguir descreve cada etapa do processo de inserção de imagem:

  1. Quando o usuário toca em uma EditText, o editor envia uma lista de tipos de conteúdo MIME que ele aceita em EditorInfo.contentMimeTypes

  2. O IME lê a lista de tipos compatíveis e exibe o conteúdo no teclado de software que o editor pode aceitar.

  3. Quando o usuário seleciona uma imagem, o IME chama commitContent() e envia uma InputContentInfo ao editor. A chamada commitContent() é análoga à chamada commitText(), mas para conteúdo avançado. InputContentInfo contém um URI que identifica o conteúdo de uma plataforma de nuvem.

Esse processo é mostrado na Figura 2:

Uma imagem mostrando a sequência do aplicativo ao IME e de volta ao aplicativo
Figura 2. Aplicativo para fluxo do IME para o aplicativo.

Adicionar suporte a imagens nos apps

Para aceitar conteúdo avançado de IMEs, um app precisa informar a eles quais tipos de conteúdo ele aceita e especifica um método de retorno de chamada que é executado quando o conteúdo é recebido. O exemplo a seguir demonstra como criar um EditText que aceite PNG. imagens:

Kotlin

var editText: EditText = object : EditText(this) {
    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
        var ic = super.onCreateInputConnection(outAttrs)
        EditorInfoCompat.setContentMimeTypes(outAttrs, arrayOf("image/png"))
        val mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this)
        if (mimeTypes != null) {
            EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes)
            ic = InputConnectionCompat.createWrapper(this, ic, outAttrs)
        }
        return ic
    }
}

Java

EditText editText = new EditText(this) {
    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        InputConnection ic = super.onCreateInputConnection(outAttrs);
        EditorInfoCompat.setContentMimeTypes(outAttrs, new String[]{"image/png"});
        String[] mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this);
        if (mimeTypes != null) {
            EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes);
            ic = InputConnectionCompat.createWrapper(this, ic, outAttrs);
        }
        return ic;
    }
};

Confira a seguir mais explicações:

Veja a seguir as práticas recomendadas:

  • Os editores que não são compatíveis com conteúdo avançado não chamam setContentMimeTypes() e eles deixam o EditorInfo.contentMimeTypes definido. para null.

  • Os editores vão ignorar o conteúdo se o tipo MIME especificado em InputContentInfo. não corresponde a nenhum dos tipos aceitos.

  • O conteúdo avançado não afeta nem é afetado pela posição do texto. cursor. Os editores podem ignorar a posição do cursor ao trabalhar com o conteúdo.

  • Na guia OnCommitContentListener.onCommitContent(), é possível retornar true de forma assíncrona, mesmo antes de carregar o conteúdo.

  • Ao contrário do texto, que pode ser editado no IME antes de ser confirmado, conteúdo é confirmado imediatamente. Se você quiser permitir que os usuários editem ou excluam implemente a lógica você mesmo.

Para testar o app, confira se o dispositivo ou emulador tem um teclado que pode enviar conteúdo avançado. Você pode usar o Teclado do Google no Android 7.1 ou versões mais recentes.

Adicionar suporte a imagens a IMEs

Os IMEs que quiserem enviar conteúdo avançado para apps precisarão implementar a API Commit Content. API, conforme mostrado no exemplo a seguir:

  • Substituir onStartInput() ou onStartInputView() e leia a lista de tipos de conteúdo compatíveis com o destino editor. O snippet de código a seguir mostra como verificar se o editor de destino aceita imagens GIF.

Kotlin

override fun onStartInputView(editorInfo: EditorInfo, restarting: Boolean) {
    val mimeTypes: Array<String> = EditorInfoCompat.getContentMimeTypes(editorInfo)

    val gifSupported: Boolean = mimeTypes.any {
        ClipDescription.compareMimeTypes(it, "image/gif")
    }

    if (gifSupported) {
        // The target editor supports GIFs. Enable the corresponding content.
    } else {
        // The target editor doesn't support GIFs. Disable the corresponding
        // content.
    }
}

Java

@Override
public void onStartInputView(EditorInfo info, boolean restarting) {
    String[] mimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);

    boolean gifSupported = false;
    for (String mimeType : mimeTypes) {
        if (ClipDescription.compareMimeTypes(mimeType, "image/gif")) {
            gifSupported = true;
        }
    }

    if (gifSupported) {
        // The target editor supports GIFs. Enable the corresponding content.
    } else {
        // The target editor doesn't support GIFs. Disable the corresponding
        // content.
    }
}

  • Confirme o conteúdo no app quando o usuário selecionar uma imagem. Evitar chamadas commitContent() quando há algum texto sendo composto, porque pode fazer com que o editor perca o foco. O snippet de código a seguir mostra como para fazer commit de uma imagem GIF.

Kotlin

// Commits a GIF image.

// @param contentUri = Content URI of the GIF image to be sent.
// @param imageDescription = Description of the GIF image to be sent.

fun commitGifImage(contentUri: Uri, imageDescription: String) {
    val inputContentInfo = InputContentInfoCompat(
            contentUri,
            ClipDescription(imageDescription, arrayOf("image/gif")),
            null
    )
    val inputConnection = currentInputConnection
    val editorInfo = currentInputEditorInfo
    var flags = 0
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
        flags = flags or InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
    }
    InputConnectionCompat.commitContent(inputConnection, editorInfo, inputContentInfo, flags, null)
}

Java

// Commits a GIF image.

// @param contentUri = Content URI of the GIF image to be sent.
// @param imageDescription = Description of the GIF image to be sent.

public static void commitGifImage(Uri contentUri, String imageDescription) {
    InputContentInfoCompat inputContentInfo = new InputContentInfoCompat(
            contentUri,
            new ClipDescription(imageDescription, new String[]{"image/gif"}),
            null
    );
    InputConnection inputConnection = getCurrentInputConnection();
    EditorInfo editorInfo = getCurrentInputEditorInfo();
    Int flags = 0;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
        flags |= InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
    }
    InputConnectionCompat.commitContent(
            inputConnection, editorInfo, inputContentInfo, flags, null);
}

Como autor de um IME, você provavelmente precisará implementar seu próprio provedor de conteúdo para responder a solicitações de URI de conteúdo. A exceção é se o IME oferecer suporte a conteúdos de provedores de conteúdo, como MediaStore Para informações sobre criar provedores de conteúdo, consulte a documentação provedor e file do provedor.

Se você estiver criando seu próprio provedor de conteúdo, recomendamos não exportá-lo. definindo android:exported para false. Em vez disso, ative a concessão de permissões no provedor configurando android:grantUriPermission para true. Depois disso, seu IME poderá conceder permissões para acessar o URI de conteúdo quando o conteúdo for confirmado. Há duas maneiras de fazer isso:

  • No Android 7.1 (nível 25 da API) e versões mais recentes, ao chamar commitContent(), defina o parâmetro de flag como INPUT_CONTENT_GRANT_READ_URI_PERMISSION. Em seguida, o objeto InputContentInfo que o app recebe pode solicitar e liberar permissões temporárias de leitura chamando requestPermission() e releasePermission()

  • No Android 7.0 (nível 24 da API) e versões anteriores, INPUT_CONTENT_GRANT_READ_URI_PERMISSION é ignorado. Portanto, conceda manualmente permissão ao conteúdo. Uma maneira de fazer isso é com grantUriPermission(), mas você pode implementar seu próprio mecanismo que e satisfaça seus próprios requisitos.

Para testar o IME, verifique se o dispositivo ou emulador tem um app que possa receber conteúdo avançado. Você pode usar o app Google Messenger no Android 7.1 ou versões mais recentes.