Получайте богатый контент

Рис. 1. Унифицированный API предоставляет единое место для обработки входящего контента независимо от конкретного механизма пользовательского интерфейса, например вставки из меню касания и удержания или использования перетаскивания.

Пользователи любят изображения, видео и другой выразительный контент, но вставлять и перемещать этот контент в приложениях не всегда легко. Чтобы приложениям было проще получать разнообразный контент, в Android 12 (уровень API 31) представлен унифицированный API, который позволяет вашему приложению принимать контент из любого источника: буфера обмена, клавиатуры или перетаскивания.

Вы можете прикрепить интерфейс, например OnReceiveContentListener , к компонентам пользовательского интерфейса и получать обратный вызов, когда контент вставляется с помощью любого механизма. Обратный вызов становится единственным местом, где ваш код обрабатывает получение всего контента: от простого и стилизованного текста до разметки, изображений, видео, аудиофайлов и т. д.

Для обратной совместимости с предыдущими версиями Android этот API также доступен в AndroidX, начиная с Core 1.7 и Appcompat 1.4 , которые мы рекомендуем использовать при реализации этой функциональности.

Обзор

В случае с другими существующими API каждый механизм пользовательского интерфейса, например меню касания и удержания или перетаскивание, имеет свой собственный соответствующий API. Это означает, что вам придется интегрироваться с каждым API отдельно, добавляя аналогичный код для каждого механизма вставки контента:

Изображение, показывающее различные действия и соответствующий API для реализации.
Рисунок 2. Раньше приложения реализовывали разные API для каждого механизма пользовательского интерфейса для вставки контента.

API OnReceiveContentListener объединяет эти различные пути кода, создавая единый API для реализации, поэтому вы можете сосредоточиться на логике, специфичной для вашего приложения, и позволить платформе позаботиться обо всем остальном:

Изображение, показывающее упрощенный унифицированный API.
Рисунок 3. Унифицированный API позволяет реализовать единый API, поддерживающий все механизмы пользовательского интерфейса.

Этот подход также означает, что когда на платформу добавляются новые способы вставки контента, вам не нужно вносить дополнительные изменения в код, чтобы включить поддержку в вашем приложении. И если вашему приложению необходимо реализовать полную настройку для конкретного варианта использования, вы все равно можете использовать существующие API, которые продолжают работать так же.

Выполнение

API представляет собой интерфейс прослушивателя с одним методом OnReceiveContentListener . Для поддержки более старых версий платформы Android мы рекомендуем использовать соответствующий интерфейс OnReceiveContentListener в библиотеке AndroidX Core.

Чтобы использовать API, реализуйте прослушиватель, указав, какие типы контента может обрабатывать ваше приложение:

Котлин

object MyReceiver : OnReceiveContentListener {
    val MIME_TYPES = arrayOf("image/*", "video/*")
    
    // ...
    
    override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? {
        TODO("Not yet implemented")
    }
}

Ява

public class MyReceiver implements OnReceiveContentListener {
     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
     // ...
}

После указания всех типов MIME контента, которые поддерживает ваше приложение, реализуйте остальную часть прослушивателя:

Котлин

class MyReceiver : OnReceiveContentListener {
    override fun onReceiveContent(view: View, contentInfo: ContentInfoCompat): ContentInfoCompat {
        val split = contentInfo.partition { item: ClipData.Item -> item.uri != null }
        val uriContent = split.first
        val remaining = split.second
        if (uriContent != null) {
            // App-specific logic to handle the URI(s) in uriContent.
        }
        // Return anything that your app didn't handle. This preserves the
        // default platform behavior for text and anything else that you aren't
        // implementing custom handling for.
        return remaining
    }

    companion object {
        val MIME_TYPES = arrayOf("image/*", "video/*")
    }
}

Ява

 public class MyReceiver implements OnReceiveContentListener {
     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};

     @Override
     public ContentInfoCompat onReceiveContent(View view, ContentInfoCompat contentInfo) {
         Pair split = contentInfo.partition(
                 item -> item.getUri() != null);
         ContentInfo uriContent = split.first;
         ContentInfo remaining = split.second;
         if (uriContent != null) {
             // App-specific logic to handle the URI(s) in uriContent.
         }
         // Return anything that your app didn't handle. This preserves the
         // default platform behavior for text and anything else that you aren't
         // implementing custom handling for.
         return remaining;
     }
 }

Если ваше приложение уже поддерживает совместное использование с намерениями, вы можете повторно использовать логику, специфичную для приложения, для обработки URI контента. Верните все оставшиеся данные, чтобы делегировать обработку этих данных платформе.

После реализации прослушивателя установите его в соответствующих элементах пользовательского интерфейса вашего приложения:

Котлин

class MyActivity : Activity() {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        val myInput = findViewById(R.id.my_input)
        ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, MyReceiver())
    }
}

Ява

public class MyActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         // ...

         AppCompatEditText myInput = findViewById(R.id.my_input);
         ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, new MyReceiver());
     }
}

Разрешения URI

Разрешения на чтение предоставляются и освобождаются платформой автоматически для любых URI контента в полезных данных, передаваемых в OnReceiveContentListener .

Обычно ваше приложение обрабатывает URI контента в службе или действии. Для длительной обработки используйте WorkManager . При реализации этого расширите разрешения для целевой службы или действия, передав содержимое с помощью Intent.setClipData и установив флаг FLAG_GRANT_READ_URI_PERMISSION .

Альтернативно вы можете использовать фоновый поток в текущем контексте для обработки содержимого. В этом случае вы должны сохранить ссылку на объект payload , полученный прослушивателем, чтобы гарантировать, что разрешения не будут преждевременно отозваны платформой.

Пользовательские представления

Если ваше приложение использует пользовательский подкласс View , позаботьтесь о том, чтобы OnReceiveContentListener не был обойден.

Если ваш класс View переопределяет метод onCreateInputConnection , используйте Jetpack API InputConnectionCompat.createWrapper для настройки InputConnection .

Если ваш класс View переопределяет метод onTextContextMenuItem , делегируйте его суперу, когда пункт меню — R.id.paste или R.id.pasteAsPlainText .

Сравнение с API изображения клавиатуры

Вы можете думать об API OnReceiveContentListener как о следующей версии существующего API изображения клавиатуры . Этот унифицированный API поддерживает функциональность API изображения клавиатуры, а также некоторые дополнительные функции. Совместимость устройств и функций зависит от того, используете ли вы библиотеку Jetpack или собственные API из Android SDK.

Таблица 1. Поддерживаемые функции и уровни API для Jetpack.
Действие или функция Поддерживается API изображения клавиатуры. Поддерживается унифицированным API
Вставка с клавиатуры Да (уровень API 13 и выше) Да (уровень API 13 и выше)
Вставьте, используя вставку из меню касания и удержания. Нет Да
Вставка с помощью перетаскивания Нет Да (уровень API 24 и выше)
Таблица 2. Поддерживаемые функции и уровни API для собственных API.
Действие или функция Поддерживается API изображения клавиатуры. Поддерживается унифицированным API
Вставка с клавиатуры Да (уровень API 25 и выше) Да (Android 12 и выше)
Вставьте, используя вставку из меню касания и удержания. Нет
Вставка с помощью перетаскивания Нет