图像键盘支持

用户通常希望使用表情符号、贴纸和其他类型的富媒体内容进行交流。在之前的Android版本中,软键盘(也称为输入法编辑器或IME)只能向应用发送Unicode表情符号。对于富媒体内容,应用构建了特定于应用的API,这些API无法在其他应用中使用,或者使用了诸如通过简单的分享操作或剪贴板发送图像的变通方法。

An image showing a keyboard that support image search
图1.图像键盘支持示例。

从Android 7.1(API级别25)开始,Android SDK包含Commit Content API,它为IME直接向应用中的文本编辑器发送图像和其他富媒体内容提供了一种通用方法。从25.0.0修订版开始,该API也可在v13支持库中使用。我们建议使用支持库,因为它包含简化实现的辅助方法。

使用此API,您可以构建接受来自任何键盘的富媒体内容的即时通讯应用,以及可以向任何应用发送富媒体内容的键盘。Google键盘Google消息等应用在Android 7.1中支持Commit Content API,如图1所示。

本文档介绍如何在IME和应用中实现Commit Content API。

工作原理

键盘图像插入需要IME和应用的参与。以下序列描述了图像插入过程中的每个步骤

  1. 当用户点击EditText时,编辑器会发送其在EditorInfo.contentMimeTypes中接受的MIME内容类型列表。

  2. IME读取支持的类型列表,并在软键盘中显示编辑器可以接受的内容。

  3. 当用户选择图像时,IME调用commitContent()并将InputContentInfo发送到编辑器。 commitContent()调用类似于commitText()调用,但用于富媒体内容。InputContentInfo包含一个URI,该URI在内容提供程序中标识内容。

此过程如图2所示

An image showing the sequence from Application to IME and back to Application
图2.应用程序到IME到应用程序流程。

向应用添加图像支持

为了接受来自IME的富媒体内容,应用必须告诉IME它接受什么内容类型,并指定在收到内容时执行的回调方法。以下示例演示如何创建一个接受PNG图像的EditText

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

以下是进一步的解释

以下是推荐的实践

  • 不支持富媒体内容的编辑器不会调用setContentMimeTypes(),并且它们将其EditorInfo.contentMimeTypes设置为null

  • 如果InputContentInfo中指定的MIME类型与编辑器接受的任何类型都不匹配,则编辑器会忽略该内容。

  • 富媒体内容不会影响文本光标的位置,也不会受其影响。编辑器在处理内容时可以忽略光标位置。

  • 在编辑器的OnCommitContentListener.onCommitContent()方法中,你可以在加载内容之前异步返回true

  • 与可以在IME中编辑后再提交的文本不同,富媒体内容会立即提交。如果你希望让用户编辑或删除内容,请自己实现逻辑。

要测试你的应用,请确保你的设备或模拟器具有可以发送富媒体内容的键盘。你可以在Android 7.1或更高版本中使用Google键盘。

向IME添加图像支持

想要向应用发送富媒体内容的IME必须实现Commit Content API,如下例所示

  • 覆盖onStartInput()onStartInputView()并从目标编辑器读取支持的内容类型列表。以下代码片段显示如何检查目标编辑器是否接受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.
    }
}
  • 当用户选择图像时,将内容提交到应用。避免在撰写任何文本时调用commitContent(),因为它可能会导致编辑器失去焦点。以下代码片段显示如何提交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);
}

作为 IME 开发者,您很可能需要实现自己的内容提供程序来响应内容 URI 请求。除非您的 IME 支持来自现有内容提供程序(例如MediaStore)的内容,否则需要这样做。有关构建内容提供程序的信息,请参阅内容提供程序文件提供程序文档。

如果您正在构建自己的内容提供程序,我们建议您不要通过将android:exported设置为false来导出它。相反,请通过将android:grantUriPermission设置为true来启用提供程序中的权限授予。然后,您的 IME 可以在提交内容时授予访问内容 URI 的权限。有两种方法可以做到这一点。

  • 在 Android 7.1(API 级别 25)及更高版本上,调用commitContent()时,将标志参数设置为INPUT_CONTENT_GRANT_READ_URI_PERMISSION。然后,应用程序收到的InputContentInfo对象可以通过调用requestPermission()releasePermission()来请求和释放临时读取权限。

  • 在 Android 7.0(API 级别 24)及更低版本上,INPUT_CONTENT_GRANT_READ_URI_PERMISSION将被忽略,因此请手动授予内容的权限。一种方法是使用grantUriPermission(),但您可以实现满足自身需求的机制。

要测试您的 IME,请确保您的设备或模拟器拥有能够接收富媒体内容的应用程序。您可以在 Android 7.1 或更高版本上使用 Google Messenger 应用程序。