圖片鍵盤支援

使用者通常會想使用表情符號、貼圖和其他種類的多媒體內容溝通。在先前的 Android 版本中,螢幕鍵盤 (也稱為輸入法編輯器或 IME) 只能傳送萬國碼 (Unicode) 表情符號至應用程式。以多媒體內容來說,應用程式建構的應用程式專屬 API 無法在其他應用程式中使用,或是使用了其他解決方法,例如透過簡易分享動作或剪貼簿傳送圖片。

圖片:顯示支援圖片搜尋功能的鍵盤
圖 1. 圖片鍵盤支援範例。

從 Android 7.1 (API 級別 25) 開始,Android SDK 包含 Commit Content API,這可讓 IME 透過通用方式,將圖片和其他多媒體內容直接傳送至應用程式中的文字編輯器。自修訂版本 25.0.0 起,該 API 也可在 v13 支援資料庫中提供。建議您使用支援資料庫,因為其中包含可簡化實作的輔助方法。

透過這個 API,您可以建構能接受任何鍵盤支援多媒體內容的訊息應用程式,以及可將多媒體內容傳送至任何應用程式的鍵盤。Google 鍵盤Messages by 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。

這項程序如圖 2 所示:

顯示應用程式至 IME 以及返回應用程式的順序圖片
圖 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

  • 文字與文字不同,可在提交前於輸入法編輯器編輯,系統會立即提交多媒體內容。如要讓使用者編輯或刪除內容,請自行實作邏輯。

如要測試應用程式,請確認裝置或模擬器具備可傳送豐富內容的鍵盤。您可以在 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 應用程式。