使用者通常會想使用表情符號、貼圖和其他種類的多媒體內容溝通。在先前的 Android 版本中,螢幕鍵盤 (也稱為輸入法編輯器或 IME) 只能傳送萬國碼 (Unicode) 表情符號至應用程式。以多媒體內容來說,應用程式建構的應用程式專屬 API 無法在其他應用程式中使用,或是使用了其他解決方法,例如透過簡易分享動作或剪貼簿傳送圖片。
從 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 和應用程式參與。以下順序說明圖片插入程序中的每個步驟:
當使用者輕觸
EditText
時,編輯器會在EditorInfo.contentMimeTypes
中傳送其接受的 MIME 內容類型清單。IME 會讀取支援的類型清單,並在編輯器可接受的螢幕鍵盤中顯示內容。
使用者選取圖片時,IME 會呼叫
commitContent()
,並將InputContentInfo
傳送至編輯器。commitContent()
呼叫與commitText()
呼叫類似,但適用於多媒體內容。InputContentInfo
包含用於識別內容供應器內容的 URI。
這項程序如圖 2 所示:
為應用程式新增圖片支援
如要接受來自 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; } };
以下進一步說明:
這個範例使用支援資料庫,因此有一些對
android.support.v13.view.inputmethod
的參照,而非android.view.inputmethod
。這個範例會建立
EditText
並覆寫其onCreateInputConnection(EditorInfo)
方法,以修改InputConnection
。InputConnection
是 IME 與接收其輸入內容的應用程式之間的通訊管道。呼叫
super.onCreateInputConnection()
會保留內建行為 (傳送和接收文字),並為您提供InputConnection
的參照。setContentMimeTypes()
會將支援的 MIME 類型清單新增至EditorInfo
。請在setContentMimeTypes()
之前呼叫super.onCreateInputConnection()
。每當 IME 提交內容時,系統就會執行
callback
。onCommitContent()
方法具有InputContentInfoCompat
的參照,其中包含內容 URI。- 如果應用程式在 API 級別 25 以上版本執行,且
INPUT_CONTENT_GRANT_READ_URI_PERMISSION
標記是由 IME 設定,請要求和釋出權限。如果不是的話,由於內容 URI 是由 IME 授予,或者內容供應器不會限制存取權,因此您可以存取這些內容 URI。詳情請參閱「為 IME 新增圖片支援」。
- 如果應用程式在 API 級別 25 以上版本執行,且
createWrapper()
會將InputConnection
、修改後的EditorInfo
和回呼納入新的InputConnection
中並傳回。
以下是建議做法:
如果編輯器不支援多媒體內容,則不會呼叫
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 應用程式。