Пользователи часто хотят общаться с помощью смайлов, стикеров и другого разнообразного контента. В предыдущих версиях Android программные клавиатуры, также известные как редакторы методов ввода или IME, могли отправлять в приложения только смайлы Unicode. Для насыщенного контента приложения создавали API-интерфейсы для конкретных приложений, которые нельзя было использовать в других приложениях, или использовали обходные пути, такие как отправка изображений с помощью простого действия «Поделиться» или буфера обмена.
![Изображение, показывающее клавиатуру, поддерживающую поиск изображений.](https://developer.android.com/static/images/guide/topics/text/image-keyboard-sample.png?authuser=19&hl=ru)
Начиная с Android 7.1 (уровень API 25), Android SDK включает API фиксации контента, который предоставляет IME универсальный способ отправки изображений и другого насыщенного контента непосредственно в текстовый редактор в приложении. API также доступен в библиотеке поддержки версии 13 начиная с версии 25.0.0. Мы рекомендуем использовать библиотеку поддержки, поскольку она содержит вспомогательные методы, упрощающие реализацию.
С помощью этого API вы можете создавать приложения для обмена сообщениями, которые принимают расширенный контент с любой клавиатуры, а также клавиатуры, которые могут отправлять расширенный контент в любое приложение. Клавиатура Google и такие приложения, как «Сообщения от Google», поддерживают API Commit Content в Android 7.1, как показано на рисунке 1.
В этом документе показано, как реализовать API Commit Content как в IME, так и в приложениях.
Как это работает
Вставка изображения клавиатуры требует участия IME и приложения. Следующая последовательность описывает каждый шаг процесса вставки изображения:
Когда пользователь нажимает
EditText
, редактор отправляет список типов контента MIME, которые он принимает вEditorInfo.contentMimeTypes
.IME считывает список поддерживаемых типов и отображает на программной клавиатуре содержимое, которое может принять редактор.
Когда пользователь выбирает изображение, IME вызывает
commitContent()
и отправляетInputContentInfo
в редактор. ВызовcommitContent()
аналогичен вызовуcommitText()
, но для расширенного контента.InputContentInfo
содержит URI, идентифицирующий контент в поставщике контента .
Этот процесс изображен на рисунке 2:
![Изображение, показывающее последовательность действий от приложения к IME и обратно к приложению.](https://developer.android.com/static/images/guide/topics/text/image-keyboard-diagram.png?authuser=19&hl=ru)
Добавьте поддержку изображений в приложения
Чтобы принимать расширенный контент от IME, приложение должно сообщить IME, какие типы контента оно принимает, и указать метод обратного вызова, который выполняется при получении контента. В следующем примере показано, как создать EditText
, который принимает изображения PNG:
Котлин
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 } }
Ява
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
. Вызовитеsuper.onCreateInputConnection()
передsetContentMimeTypes()
.callback
выполняется всякий раз, когда IME фиксирует содержимое. МетодonCommitContent()
имеет ссылку наInputContentInfoCompat
, которая содержит URI контента.- Запросите и отпустите разрешения, если ваше приложение работает на уровне API 25 или выше и флаг
INPUT_CONTENT_GRANT_READ_URI_PERMISSION
установлен IME. В противном случае у вас уже есть доступ к URI контента, поскольку он предоставлен IME или поставщик контента не ограничивает доступ. Дополнительные сведения см. в разделе Добавление поддержки изображений в IME .
- Запросите и отпустите разрешения, если ваше приложение работает на уровне API 25 или выше и флаг
createWrapper()
оборачиваетInputConnection
, измененныйEditorInfo
и обратный вызов в новыйInputConnection
и возвращает его.
Ниже приведены рекомендуемые практики:
Редакторы, которые не поддерживают расширенный контент, не вызывают
setContentMimeTypes()
и оставляют дляEditorInfo.contentMimeTypes
значениеnull
.Редакторы игнорируют содержимое, если тип MIME, указанный в
InputContentInfo
не соответствует ни одному из принимаемых ими типов.Богатое содержимое не влияет и не зависит от положения текстового курсора. Редакторы могут игнорировать положение курсора при работе с контентом.
В методе редактора
OnCommitContentListener.onCommitContent()
вы можете вернутьtrue
асинхронно, даже до загрузки контента.В отличие от текста, который можно редактировать в IME перед фиксацией, расширенное содержимое фиксируется немедленно. Если вы хотите разрешить пользователям редактировать или удалять контент, реализуйте логику самостоятельно.
Чтобы протестировать свое приложение, убедитесь, что на вашем устройстве или эмуляторе есть клавиатура, которая может отправлять богатый контент. Вы можете использовать клавиатуру Google в Android 7.1 или более поздней версии.
Добавить поддержку изображений в IME
IME, которые хотят отправлять богатый контент в приложения, должны реализовать API Commit Content, как показано в следующем примере:
- Переопределите
onStartInput()
илиonStartInputView()
и прочитайте список поддерживаемых типов контента из целевого редактора. В следующем фрагменте кода показано, как проверить, принимает ли целевой редактор изображения GIF.
Котлин
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. } }
Ява
@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.
Котлин
// 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) }
Ява
// 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()
установите для параметра flag значениеINPUT_CONTENT_GRANT_READ_URI_PERMISSION
. Затем объектInputContentInfo
, который получает приложение, может запрашивать и освобождать временные разрешения на чтение, вызываяrequestPermission()
иreleasePermission()
.В Android 7.0 (уровень API 24) и более ранних версиях
INPUT_CONTENT_GRANT_READ_URI_PERMISSION
игнорируется, поэтому вручную предоставьте разрешение на доступ к содержимому. Один из способов сделать это — с помощьюgrantUriPermission()
, но вы можете реализовать свой собственный механизм, удовлетворяющий вашим собственным требованиям.
Чтобы проверить свой IME, убедитесь, что на вашем устройстве или эмуляторе есть приложение, которое может получать расширенный контент. Вы можете использовать приложение Google Messenger на Android 7.1 или более поздней версии.