Supporto per la tastiera delle immagini

Gli utenti spesso desiderano comunicare tramite emoji, adesivi e altri tipi di contenuti avanzati. Nelle versioni precedenti di Android, le tastiere flessibili, anche note come Input Method Editor, o IME, potevano inviare solo emoji Unicode alle app. Per i contenuti avanzati, le app creavano API specifiche per le app che non potevano essere utilizzate in altre app o utilizzavano soluzioni alternative come l'invio di immagini tramite una semplice azione di condivisione o gli appunti.

Un'immagine che mostra una tastiera che supporta la ricerca di immagini
Figura 1. Esempio di supporto della tastiera immagine.

A partire da Android 7.1 (livello API 25), l'SDK Android include l'API Commit Content, che consente agli IME di inviare immagini e altri contenuti avanzati direttamente a un editor di testo in un'app. L'API è disponibile anche nella libreria di supporto v13 a partire dalla revisione 25.0.0. Consigliamo di utilizzare la libreria di supporto perché contiene metodi helper che semplificano l'implementazione.

Con questa API puoi creare app di messaggistica che accettano contenuti avanzati da qualsiasi tastiera, nonché tastiere in grado di inviare contenuti avanzati a qualsiasi app. La tastiera Google e le app come Messaggi di Google supportano l'API Commit Content in Android 7.1, come mostrato nella figura 1.

Questo documento mostra come implementare l'API Commit Content sia negli IME sia nelle app.

Come funziona

L'inserimento dell'immagine della tastiera richiede la partecipazione dell'IME e dell'app. La sequenza seguente descrive ogni passaggio del processo di inserimento delle immagini:

  1. Quando l'utente tocca EditText, l'editor invia un elenco dei tipi di contenuti MIME che accetta in EditorInfo.contentMimeTypes.

  2. L'IME legge l'elenco dei tipi supportati e mostra i contenuti nella tastiera software accettati dall'editor.

  3. Quando l'utente seleziona un'immagine, l'IME chiama commitContent() e invia un InputContentInfo all'editor. La chiamata commitContent() è analoga alla chiamata commitText(), ma per i contenuti multimediali. InputContentInfo contiene un URI che identifica i contenuti di un fornitore di contenuti.

Questo processo è illustrato nella Figura 2:

Un'immagine che mostra la sequenza dall'applicazione all'IME e torna all'applicazione
Figura 2. Applicazione all'IME del flusso dell'applicazione.

Aggiungere il supporto delle immagini alle app

Per accettare contenuti avanzati dagli IME, un'app deve indicare agli IME i tipi di contenuti accettati e specificare un metodo di callback da eseguire alla ricezione dei contenuti. L'esempio seguente mostra come creare un elemento EditText che accetta le immagini PNG:

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

Di seguito viene fornita una spiegazione più dettagliata:

Di seguito sono riportate le pratiche consigliate:

  • Gli editor che non supportano i contenuti avanzati non chiamano setContentMimeTypes() e lasciano il campo EditorInfo.contentMimeTypes impostato su null.

  • Gli editor ignorano i contenuti se il tipo MIME specificato in InputContentInfo non corrisponde a nessuno dei tipi accettati.

  • I contenuti avanzati non incidono sulla posizione del cursore del testo e non ne influenzano la posizione. Gli editor possono ignorare la posizione del cursore quando lavorano con i contenuti.

  • Nel metodo OnCommitContentListener.onCommitContent() dell'editor, puoi restituire true in modo asincrono, anche prima di caricare i contenuti.

  • A differenza del testo, che può essere modificato nell'IME prima del commit, il commit dei contenuti avanzati viene eseguito immediatamente. Se vuoi consentire agli utenti di modificare o eliminare i contenuti, implementa autonomamente la logica.

Per testare la tua app, assicurati che il tuo dispositivo o emulatore abbia una tastiera in grado di inviare contenuti avanzati. Puoi utilizzare la tastiera Google in Android 7.1 o versioni successive.

Aggiungere il supporto delle immagini agli IME

Gli IME che vogliono inviare contenuti avanzati alle app devono implementare l'API Commit Content, come mostrato nell'esempio seguente:

  • Esegui l'override di onStartInput() o onStartInputView() e leggi l'elenco dei tipi di contenuti supportati dall'editor di destinazione. Il seguente snippet di codice mostra come verificare se l'editor di destinazione accetta immagini 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.
    }
}

  • Esegui il commit dei contenuti nell'app quando l'utente seleziona un'immagine. Evita di chiamare commitContent() quando componi testo, perché l'editor potrebbe perdere lo stato attivo. Il seguente snippet di codice mostra come eseguire il commit di un'immagine 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);
}

In qualità di autore di IME, molto probabilmente dovrai implementare il tuo fornitore di contenuti per rispondere alle richieste degli URI dei contenuti. Fa eccezione il caso in cui l'IME supporti contenuti di fornitori di contenuti esistenti come MediaStore. Per informazioni sulla creazione di fornitori di contenuti, consulta la documentazione relativa al provider di contenuti e al provider di file.

Se stai creando un tuo fornitore di contenuti, ti consigliamo di non esportarlo impostando android:exported su false. Abilita la concessione delle autorizzazioni nel provider impostando android:grantUriPermission su true. Successivamente, l'IME può concedere le autorizzazioni per accedere all'URI dei contenuti quando viene eseguito il commit dei contenuti. Esistono due metodi per effettuare questa operazione:

  • Su Android 7.1 (livello API 25) e versioni successive, quando chiami commitContent(), imposta il parametro flag su INPUT_CONTENT_GRANT_READ_URI_PERMISSION. Quindi, l'oggetto InputContentInfo ricevuto dall'app può richiedere e rilasciare autorizzazioni di lettura temporanee chiamando requestPermission() e releasePermission().

  • Su Android 7.0 (livello API 24) e versioni precedenti, INPUT_CONTENT_GRANT_READ_URI_PERMISSION viene ignorato, quindi concedi manualmente l'autorizzazione ai contenuti. Un modo per farlo è utilizzare grantUriPermission(), ma puoi implementare un meccanismo che soddisfi i tuoi requisiti.

Per testare il tuo IME, assicurati che il tuo dispositivo o emulatore abbia un'app in grado di ricevere contenuti multimediali. Puoi utilizzare l'app Google Messenger in Android 7.1 o versioni successive.