Tiramisu: Photo Picker

Foram Soni
Mindful Engineering
4 min readOct 3, 2023

--

Photo Picker is one of the exciting features of Android Tiramisu. It provides a user-friendly and consistent way for the developers to allow users to select images and videos from their device’s media gallery or cloud storage services. Through the utilization of PhotoPicker, developers can seamlessly integrate media selection capabilities into their apps without having to write complex code which results in reducing development time and effort.

Why do we need PhotoPicker?

  • Permissions are handled automatically.
  • It restricts the number of media items chosen which was not there in the older version.
  • It restricts the types of media items such as image, video, or both.
  • Implement a familiar and consistent user interface, enhancing user experience.

Activity Result Contracts

  1. PickVisualMedia: This activityResultContract is used when the user wants to select a single image/video.
val mediaSingleLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
Log.d("PhotoPicker", "Selected uri: $uri")
}
}
// Launch the photo picker and let the user choose images and videos.
mediaSingleLauncher.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))

2. PickMultipleVisualMedia: This activityResultContract is used when the user wants to select multiple images/videos by setting the maximum number of selectable media files.

 val mediaMultipleLauncher =
rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickMultipleVisualMedia(
5
)
) { uris ->
if (uris.isNotEmpty()) {
Log.d("PhotoPicker", "Number of items selected: ${uris.size}")
}
}
// Launch the photo picker and let the user choose 5 images and videos.
mediaMultipleLauncher.launch(PickVisualMediaRequest(PickVisualMedia.ImageAndVideo))

Enough of talking let's start coding…

Gif from tenor
  1. First, you need to change the targetSdk and compileSdk versions of this app.
android {
// ...
compileSdk 33

defaultConfig {
targetSdk 33
// ...
}
// ...
}

2. Then, write the below code for Picker

@Composable
fun Picker() {
val selectionType = remember {
mutableStateOf(MediaType.NONE)
}
val uri = remember {
mutableStateOf("")
}

Box(modifier = Modifier.weight(2f)) {
when (selectionType.value) {
MediaType.VIDEO -> {
//Show the single video in media player
}

MediaType.MULTIPLE_PHOTO -> {
//Show the multiple photo in image view
}

MediaType.PHOTO_VIDEO -> {
//Show the multiple photo or video in media player
}

MediaType.PHOTO -> {
//Show the single photo in image view
}

MediaType.NONE -> {
//Show dummy image
}
}
}
MediaChooserContainer(
// ...
getMediaUri = { uriValue ->
// Here we get the uri of the image/video
uri.value = uriValue
},
getSelectionType = { type ->
// Here we get the selection type
selectionType.value = type
})
}
}



@Composable
fun MediaChooserContainer(
modifier: Modifier,
getMediaUri: (String) -> Unit,
getSelectionType: (MediaType) -> Unit,
) {

val singleMediaLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
// Here we get the url of single image/video
getMediaUri(uri.toString())
}
}

val multipleMediaLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickMultipleVisualMedia(
5 // Set the selection limit to 5
)
) { uris ->
if (uris.isNotEmpty()) {
//Here we get the url of multiple image/video
getMediaUri(uris.toString())
}
}

Box(
//...
) {
Column() {
// Button for Single Photo Selection
MediaItemButton(R.drawable.ic_single_photo,
onClickOption = {
getSelectionType(MediaType.PHOTO)
singleMediaLauncher.launch(
PickVisualMediaRequest(
mediaType = ActivityResultContracts.PickVisualMedia.ImageOnly
)
)
})
// Button for Single Video Selection
MediaItemButton(R.drawable.ic_single_video,
onClickOption = {
getSelectionType(MediaType.VIDEO)
singleMediaLauncher.launch(
PickVisualMediaRequest(
mediaType = ActivityResultContracts.PickVisualMedia.VideoOnly
)
)
})

// Button for Single Photo/Video Selection
MediaItemButton(R.drawable.ic_photovideo,
onClickOption = {
getSelectionType(MediaType.PHOTO_VIDEO)
singleMediaLauncher.launch(
PickVisualMediaRequest(
mediaType = ActivityResultContracts.PickVisualMedia.ImageAndVideo
)
)
})
// Button for Multiple Photo/Video Selection
MediaItemButton(R.drawable.ic_multiple,
onClickOption = {
getSelectionType(MediaType.MULTIPLE_PHOTO)
multipleMediaLauncher.launch(
PickVisualMediaRequest(
mediaType = ActivityResultContracts.PickVisualMedia.ImageOnly
)
)
})
}
}
}
}
}
enum class MediaType {
PHOTO, VIDEO, MULTIPLE_PHOTO, PHOTO_VIDEO, NONE
}

Here, MediaChooserContainer composable function contains four custom buttons named Photo, Video, Photo/Video, and Multiple. When a user clicks on the Photo, the first activityResultContract launcher for a single photo selection is launched where we get the URI of the image/video which is then passed through the getMediaUri callback function. Also, the selection type is passed through the getSelectionType callback.

Moreover, in Picker composable function when we get the URI and selection type we call the respective function to show video and image.

When you press the Photo Media Button photo picker activityResultContract is launched which is shown below screenshot. From this photo picker user can select a single image which returns the URI of the image

Background Photo Access

Suppose your app functions as a photo uploading application or a photo backup application then you may find it necessary to request continuous access to photos, even after the device has been rebooted. To achieve this, utilizing the takePersistableUriPermission method would be a straightforward approach. For more information check the official document.

val flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(uri, flag)

Google’s initiative of providing this feature has simplified the development process of the user experience by providing a consistent and user-friendly way to choose images and videos from various sources. I hope that this blog would aid you in adding this awesome feature to your app.

Happy coding. :)

--

--