ユーザーは画像、動画、その他の表現力豊かなコンテンツを好みますが、アプリでこのコンテンツを挿入したり移動したりするのは必ずしも簡単ではありません。Android 12(API レベル 31)には、アプリがリッチ コンテンツをより簡単に受信できるよう、任意のソース(クリップボード、キーボード、ドラッグ)からコンテンツを受け取れる Unified API が導入されました。
`OnReceiveContentListener` などのインターフェースを UI コンポーネントにアタッチすると、任意のメカニズムでコンテンツが挿入されたときにコールバックを受け取ることができます。OnReceiveContentListenerコードでは、このコールバック 1 か所で、プレーン テキストや書式付きテキストからマークアップ、画像、動画、音声ファイルまで、あらゆるコンテンツの受信を処理できるようになります。
以前の Android バージョンとの下位互換性を維持するため、この API は AndroidX(Core 1.7 およびAppcompat 1.4 以降)でも使用できます。この機能を実装する場合は、AndroidX を使用することをおすすめします。
概要
他の既存の API では、長押しメニューやドラッグなどの UI メカニズムごとに、それに対応する固有の API があります。これは、コンテンツを挿入するメカニズムごとに類似のコードを追加して、各 API を個別に統合する必要があることを意味します。
OnReceiveContentListener API は、実装する API を 1 つにすることで、これらのさまざまなコードパスを統合します。これにより、アプリ固有のロジックに集中し、それ以外の処理はプラットフォームに委ねることができます。
このアプローチでは、コンテンツを挿入する新しい方法をプラットフォームに追加する際に、アプリでサポートを有効にするためにコードを変更する必要がありません。特定のユースケース用のフル カスタマイズをアプリに実装する必要がある場合も、同様に動作する既存の API を引き続き使用できます。
実装
この API は、単一メソッド
OnReceiveContentListenerとのリスナーインターフェースです。
古いバージョンの Android プラットフォームをサポートするには、それに対応する
matching
OnReceiveContentListener
interface in the AndroidX Core library.
この API を使用するには、アプリが処理できるコンテンツのタイプを指定してリスナーを実装します。
Kotlin
object MyReceiver : OnReceiveContentListener { val MIME_TYPES = arrayOf("image/*", "video/*") // ... override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? { TODO("Not yet implemented") } }
Java
public class MyReceiver implements OnReceiveContentListener { public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; // ... }
アプリがサポートするすべてのコンテンツ MIME タイプを指定した後、リスナーの残りの部分を実装します。
Kotlin
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/*") } }
Java
public class MyReceiver implements OnReceiveContentListener { public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; @Override public ContentInfoCompat onReceiveContent(View view, ContentInfoCompat contentInfo) { Pair<ContentInfoCompat, ContentInfoCompat> 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 を処理するアプリ固有のロジックを再利用できます。残りすべてのデータを結果から取得し、そのデータの処理をプラットフォームに委ねます。
リスナーを実装したら、アプリの適切な UI 要素でリスナーを設定します。
Kotlin
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()) } }
Java
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 権限
読み取り権限は、OnReceiveContentListener に渡された
ペイロード内の
コンテンツ URI に対して、プラットフォームにより自動的に付与、解放されます。
通常、アプリはサービスまたはアクティビティでコンテンツ 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のときはスーパーに委ねてください。
Keyboard Image API との比較
OnReceiveContentListener API は、既存の Keyboard Image API の次期バージョンと考えることができます。この Unified API は、Keyboard Image API の機能に加えて、いくつかの追加機能をサポートします。デバイスと機能の互換性は、Jetpack ライブラリを使用するか、Android SDK
のネイティブ API を使用するかによって異なります。
| アクションまたは機能 | Keyboard Image API によるサポート | Unified API によるサポート |
|---|---|---|
| キーボードからの挿入 | あり(API レベル 13 以上) | あり(API レベル 13 以上) |
| 長押しメニューからの貼り付けによる挿入 | なし | はい |
| ドラッグ&ドロップによる挿入 | なし | あり(API レベル 24 以上) |
| アクションまたは機能 | Keyboard Image API によるサポート | Unified API によるサポート |
|---|---|---|
| キーボードからの挿入 | あり(API レベル 25 以上) | あり(Android 12 以降) |
| 長押しメニューからの貼り付けによる挿入 | なし | |
| ドラッグ&ドロップによる挿入 | なし |