Users love images, videos, and other expressive content, but inserting and moving this content in apps is not always easy. To make it simpler for apps to receive rich content, Android 12 (API level 31) introduced a unified API that lets your app accept content from any source: clipboard, keyboard, and drag and drop.
You can attach an interface, OnReceiveContentListener
,
to UI components and get a callback when content is inserted through any
mechanism. The callback becomes the single place for your code to handle
receiving all content, from plain and styled text to markup, images, videos,
audio files, and others.
For backward compatibility with previous Android versions, this API is also available in AndroidX (starting from Core 1.7 and Appcompat 1.4), which we recommend you use when implementing this functionality.
Overview
With other existing APIs, each UI mechanism—such as the long-press menu or drag and drop—has its own corresponding API. This means that you have to integrate with each API separately, adding similar code for each mechanism that inserts content:
The OnReceiveContentListener
API consolidates these different code paths by
creating a single API to implement, so you can focus on your app-specific logic
and let the platform handle the rest:
This approach also means that when new ways of inserting content are added to the platform, you don't need to make additional code changes to enable support in your app. If your app needs to implement full customization for a particular use case, you can still use the existing APIs, which will continue to work the same way.
Implementation
The API is a listener interface with a single method,
OnReceiveContentListener
.
To support older versions of the Android platform, we recommend using the
matching
OnReceiveContentListener
interface in the AndroidX Core library.
To use the API, start implementing the listener by specifying what types of content your app can handle:
public class MyReceiver implements OnReceiveContentListener {
public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
// ...
After specifying all the content MIME types that your app supports, implement the rest of the listener:
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;
}
}
If your app already supports sharing with Intents, you can reuse your app-specific logic for handling content URIs. Return any remaining data to delegate handling of that data to the platform.
After implementing the listener, set it on the appropriate UI elements in your app:
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 permissions
Read permissions are granted and released automatically by the platform for any
content URIs
in the payload passed to the OnReceiveContentListener
.
Normally, your app should process content URIs in a service or activity. For long-running processing, use WorkManager. When implementing this, you should extend permissions to the target service or activity by passing the content using Intent.setClipData and setting the flag FLAG_GRANT_READ_URI_PERMISSION.
Alternatively, you can use a background thread within the current context to
process the content. In this case, you must maintain a reference to the
payload
object received by the listener to ensure that permissions are not
revoked prematurely by the platform.
Custom views
If your app uses a custom View
subclass, you need to take care to ensure
that the OnReceiveContentListener
is not bypassed.
If your View
class overrides the
onCreateInputConnection
method, use the Jetpack API
InputConnectionCompat.createWrapper
to configure the InputConnection
.
If your View
class overrides the
onTextContextMenuItem
method, be sure to delegate to super when the menu item is
R.id.paste
or
R.id.pasteAsPlainText
.
Comparison with the keyboard image API
You can think of the OnReceiveContentListener
API as the next version of the
existing keyboard image API. This unified
API supports the functionality of the keyboard image API as well as some
additional features. Device and feature compatibility varies depending on
whether you use the Jetpack library or the native APIs from the Android SDK.
Supported features and API levels: Jetpack
Action or feature | Supported by keyboard image API | Supported by unified API |
---|---|---|
Insert from the keyboard | Yes (API level 13 and higher) | Yes (API level 13 and higher) |
Insert using Paste from long-press menu | No | Yes |
Insert using drag and drop | No | Yes (API level 24 and higher) |
Supported features and API levels: native APIs
Action or feature | Supported by keyboard image API | Supported by unified API |
---|---|---|
Insert from the keyboard | Yes (API level 25 and higher) | Yes (Android 12 and higher) |
Insert using Paste from long-press menu | No | |
Insert using drag and drop | No |