Compatibilité avec le clavier d'images

Les utilisateurs souhaitent souvent communiquer à l'aide d'emoji, d'autocollants et d'autres types de contenus enrichis. Dans les versions précédentes d'Android, les claviers virtuels, également appelés éditeurs de mode de saisie, ou IME, ne pouvaient envoyer que des emoji Unicode aux applications. Pour le contenu enrichi, les applications développaient des API spécifiques qui ne pouvaient pas être utilisées dans d'autres applications ou utilisaient des solutions de contournement telles que l'envoi d'images via une action de partage simple ou le presse-papiers.

Image montrant un clavier compatible avec la recherche d'images
Figure 1 : Exemple de compatibilité avec le clavier d'images.

À partir d'Android 7.1 (niveau d'API 25), le SDK Android inclut Commit Content API, qui offre aux IME un moyen universel d'envoyer des images et d'autres contenus enrichis directement à un éditeur de texte dans une application. L'API est également disponible dans la bibliothèque Support v13 à partir de la révision 25.0.0. Nous vous recommandons d'utiliser la bibliothèque Support, car elle contient des méthodes d'assistance qui simplifient l'implémentation.

Cette API vous permet de créer des applications de messagerie qui acceptent le contenu enrichi de n'importe quel clavier, ainsi que des claviers pouvant envoyer du contenu enrichi à n'importe quelle application. Le clavier Google et des applications telles que Messages par Google sont compatibles avec Commit Content API dans Android 7.1, comme illustré dans la figure 1.

Ce document explique comment implémenter Commit Content API dans les IME et les applications.

Fonctionnement

L'insertion d'images au clavier nécessite la participation de l'IME et de l'application. La séquence suivante décrit chaque étape du processus d'insertion d'images:

  1. Lorsque l'utilisateur appuie sur un EditText, l'éditeur envoie une liste des types de contenu MIME acceptés dans EditorInfo.contentMimeTypes.

  2. L'IME lit la liste des types compatibles et affiche dans le clavier virtuel le contenu que l'éditeur peut accepter.

  3. Lorsque l'utilisateur sélectionne une image, l'IME appelle commitContent() et envoie un InputContentInfo à l'éditeur. L'appel commitContent() est analogue à l'appel commitText(), mais pour du contenu enrichi. InputContentInfo contient un URI qui identifie le contenu dans un fournisseur de contenu.

Ce processus est illustré dans la figure 2:

Image montrant la séquence de l'application à l'IME, puis de retour à l'application
Figure 2 : Flux d'application vers un IME et un flux d'application

Ajouter la prise en charge des images aux applications

Pour accepter le contenu enrichi provenant des IME, une application doit indiquer aux IME les types de contenu acceptés et spécifier une méthode de rappel à exécuter lors de la réception du contenu. L'exemple suivant montre comment créer un EditText qui accepte les images 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;
    }
};

Voici une explication supplémentaire:

Voici les pratiques recommandées:

  • Les éditeurs qui n'acceptent pas le contenu enrichi n'appellent pas setContentMimeTypes() et laissent leur EditorInfo.contentMimeTypes défini sur null.

  • Les éditeurs ignorent le contenu si le type MIME spécifié dans InputContentInfo ne correspond à aucun des types qu'ils acceptent.

  • Le contenu enrichi n'a pas d'incidence ni d'impact sur la position du curseur de texte. Les éditeurs peuvent ignorer la position du curseur lorsqu'ils manipulent du contenu.

  • Dans la méthode OnCommitContentListener.onCommitContent() de l'éditeur, vous pouvez renvoyer true de manière asynchrone, même avant de charger le contenu.

  • Contrairement au texte, qui peut être modifié dans l'IME avant d'être validé, le contenu enrichi est validé immédiatement. Si vous souhaitez autoriser les utilisateurs à modifier ou supprimer du contenu, implémentez la logique vous-même.

Pour tester votre application, assurez-vous que votre appareil ou votre émulateur dispose d'un clavier capable d'envoyer du contenu enrichi. Vous pouvez utiliser le clavier Google sur Android 7.1 ou version ultérieure.

Ajouter la prise en charge des images dans les IME

Les IME qui souhaitent envoyer du contenu enrichi aux applications doivent implémenter l'API Commit Content, comme illustré dans l'exemple suivant:

  • Remplacez onStartInput() ou onStartInputView(), puis lisez la liste des types de contenu compatibles à partir de l'éditeur cible. L'extrait de code suivant montre comment vérifier si l'éditeur cible accepte les images 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.
    }
}

  • Validez le contenu dans l'application lorsque l'utilisateur sélectionne une image. Évitez d'appeler commitContent() lorsqu'un texte est en cours de rédaction, car cela pourrait empêcher l'éditeur d'être sélectionné. L'extrait de code suivant montre comment valider une image 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);
}

En tant qu'auteur IME, vous devrez probablement implémenter votre propre fournisseur de contenu pour répondre aux requêtes d'URI de contenu. Il existe une exception si votre IME prend en charge le contenu de fournisseurs de contenu existants tels que MediaStore. Pour en savoir plus sur la création de fournisseurs de contenu, consultez la documentation du fournisseur de contenu et du fournisseur de fichiers.

Si vous créez votre propre fournisseur de contenu, nous vous recommandons de ne pas l'exporter en définissant android:exported sur false. À la place, activez l'attribution d'autorisations dans le fournisseur en définissant android:grantUriPermission sur true. Votre IME peut ensuite accorder des autorisations pour accéder à l'URI de contenu lorsque le contenu est validé. Pour cela, vous pouvez procéder de deux façons :

  • Sur Android 7.1 (niveau d'API 25) ou version ultérieure, lorsque vous appelez commitContent(), définissez le paramètre d'indicateur sur INPUT_CONTENT_GRANT_READ_URI_PERMISSION. Ensuite, l'objet InputContentInfo reçu par l'application peut demander et libérer des autorisations de lecture temporaires en appelant requestPermission() et releasePermission().

  • Sur Android 7.0 (niveau d'API 24) et versions antérieures, INPUT_CONTENT_GRANT_READ_URI_PERMISSION est ignoré. Vous devez donc accorder manuellement l'autorisation au contenu. Pour ce faire, vous pouvez utiliser grantUriPermission(), mais vous pouvez implémenter votre propre mécanisme répondant à vos propres besoins.

Pour tester votre IME, assurez-vous que votre appareil ou votre émulateur dispose d'une application pouvant recevoir du contenu enrichi. Vous pouvez utiliser l'application Google Messenger sous Android 7.1 ou version ultérieure.