Skip to content

Instantly share code, notes, and snippets.

@ianhanniballake
Last active September 10, 2022 18:03
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ianhanniballake/42d8bbf37e6050dd6869229de6606f11 to your computer and use it in GitHub Desktop.
Save ianhanniballake/42d8bbf37e6050dd6869229de6606f11 to your computer and use it in GitHub Desktop.
Gist showing how to write backward compatible ActivityResultContracts for supporting Android 13's new Photo Picker: https://developer.android.com/about/versions/13/features/photopicker
/**
* Use this [ActivityResultContract] to seamlessly switch between
* the new [MediaStore.ACTION_PICK_IMAGES] and [Intent.ACTION_GET_CONTENT]
* based on the availability of the Photo Picker.
*
* Use [PickMultipleImages] if you'd like the user to be able to select multiple
* photos/videos.
*
* Input: the mimeType you'd like to receive. This should generally be
* either `image/\*` or `video/\*` for requesting only images or only videos
* or can be `\*\/\*` to support both types.
*
* Output: the Uri of the chosen image or `null` if no image was selected (i.e.,
* the user cancelled the request).
*
* ```
* private val pickImage = registerForActivityResult(PickImage()) { photoUri ->
* if (photoUri != null) {
* processSelectedUri(photoUri)
* }
* }
* ```
*/
@BuildCompat.PrereleaseSdkCheck
private class PickImage : ActivityResultContracts.GetContent() {
override fun createIntent(context: Context, input: String): Intent {
// Check to see if the ACTION_PICK_IMAGES intent is available
return if (BuildCompat.isAtLeastT()) {
Intent(MediaStore.ACTION_PICK_IMAGES).apply {
if (input != "*/*") {
type = input
}
}
} else {
// For backward compatibility with previous API levels
super.createIntent(context, input).apply {
if (input == "*/*") {
// Ensure that only images and videos are selectable
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*"))
}
}
}
}
}
/**
* Use this [ActivityResultContract] to seamlessly switch between
* the new [MediaStore.ACTION_PICK_IMAGES] and [Intent.ACTION_GET_CONTENT]
* based on the availability of the Photo Picker.
*
* Use [PickImage] if you'd like the user to be able to select multiple
* photos/videos.
*
* Input: the mimeType you'd like to receive. This should generally be
* either `image/\*` or `video/\*` for requesting only images or only videos
* or can be `\*\/\*` to support both types.
*
* Output: a list of Uris representing the chosen images. This list will be empty
* if the user did not select any image (i.e., the user cancelled the request).
*
* ```
* private val pickImages = registerForActivityResult(PickMultipleImages()) { photoUris ->
* processSelectedUris(photoUris)
* }
* ```
*/
@BuildCompat.PrereleaseSdkCheck
private class PickMultipleImages(
private val maxImageCount: Int = if (BuildCompat.isAtLeastT()) {
MediaStore.getPickImagesMaxLimit()
} else {
Integer.MAX_VALUE
}
): ActivityResultContracts.GetMultipleContents() {
override fun createIntent(context: Context, input: String): Intent {
// Check to see if the ACTION_PICK_IMAGES intent is available
if (BuildCompat.isAtLeastT()) {
return Intent(MediaStore.ACTION_PICK_IMAGES).apply {
if (input != "*/*") {
type = input
}
require(maxImageCount > 0) {
"Max Image Count must be at least 1"
}
if (maxImageCount > 1) {
require(maxImageCount <= MediaStore.getPickImagesMaxLimit()) {
"Max Image Count must be at most MediaStore.getPickImagesMaxLimit()"
}
putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxImageCount)
}
}
} else {
// To maintain compatibility for previous releases
return super.createIntent(context, input).apply {
if (input == "*/*") {
// Ensure that only images and videos are selectable
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*"))
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment