# Ask other apps for photos, files and more using ActivityResultContracts

> This tutorial will teach you how to open other apps and ask for data using the ActivityResultContract in Jetpack Compose.

Published 26 January 2023 by Alex Styl.

<div className="flex gap-x-2">
  <a target="_blank" href="https://androidweekly.net/issues/issue-555">
    <img
      src="https://androidweekly.net/issues/issue-555/badge"
      alt="Featured in Android Weekly #555"
    />
  </a>
  <a target="_blank" href="https://jetc.dev/issues/147.html">
    <img
      src="https://img.shields.io/badge/As_Seen_In-jetc.dev_Newsletter_Issue_%23147-blue?logo=Jetpack+Compose&amp;logoColor=white"
      alt="Featured in Jetpack Compose Newsletter #147"
    />
  </a>
</div>

This tutorial will teach you how to open other apps and ask for data using the `ActivityResultContract` in Jetpack Compose. This API is often used for scenarios where you want to ask other apps for common actions, such as taking a photo, selecting a contact, or selecting files.

The great thing about this API is that you do not need to request any permission in your app. All implementation is handled by the calling application.

## What is the ActivityResultContract API

The `startActivityForResult()` function and `onActivityResult()` callback were deprecated in recent versions of Android. Its successor is the `ActivityResultContracts` API. As opposed to `startActivityForResult` API, `ActivityResultContracts` are type safe.

The way `ActivityResultContracts` works is by registering a launcher using the `rememberLauncherForActivityResult()` composable function. You pass the action you want to perform and the callback that will be called as soon as you receive the result. Calling this function will return an `ActivityLauncher` object, which you can use to start the activity with any required arguments.

Here is a quick sample of how you can use this API to select a Contact stored in your device:

```kotlin
@Composable
fun ContactPicker(
    onContactSelected: (Uri) -> Unit,
    onCancelled: () -> Unit,
) {
    val launcher = rememberLauncherForActivityResult(PickContact()) { uri ->
        if (uri != null) {
            onContactSelected(uri)
        } else {
            onCancelled()
        }
    }
    IconButton(onClick = { launcher.launch() }) {
        Icon(Icons.Rounded.AccountCircle, contentDescription = "Pick Contact")
    }
}
```

This will bring up the contacts app installed to the device and let the user select a contact. Once the user selects a contact or cancels the operation (that's when `finish()` is called on the contact app's activity) the callback passed in your `rememberLauncherForActivityResult()` is called. Within the callback you will receive a `Uri` if the user has selected a contact, or `null` if they decided to exit without selecting one.

## How to use the ActivityResultContract API

`ActivityResultContracts` are part of the [androidx.activity](https://developer.android.com/jetpack/androidx/releases/activity) dependency but as we are using Jetpack Compose, we need to use the `android-compose` version of it. The compose version of the dependency is what brings in the `rememberLauncherForActivityResult()` function, along with all available contracts.

In your `app/build.gradle` file, include the AndroidX Activity-Compose dependency:

```gradle
dependencies {
    implementation 'androidx.activity:activity-compose:1.6.1'
}
```

The cool thing about `ActivityResultContracts` is that it comes with a plethora of predefined actions you can use straight away such as picking files, selecting photos, selecting contacts from the address book, taking photos and much more.

You can find the full list of available actions [by clicking here](https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts).

Here is how to use each one of them:

### How to create new files using the `CreateDocument` action

Use this action to let the user choose a location to create a new file. You can then use the uri provided to populate the file without having to declare or request any permissions.

Here is how to save the text _"Hello Android"_ into a file called `hello.txt`:

```kotlin
val contentResolver = LocalContext.current.contentResolver

val launcher = rememberLauncherForActivityResult(CreateDocument("text/plain")) { selectedUri ->
    if (selectedUri != null) {
        contentResolver.openOutputStream(selectedUri)?.use {
            val bytes = "Hello Android".toByteArray()
            it.write(bytes)
        }
    } else {
        println("No file was selected")
    }
}
LaunchedEffect(Unit) {
    launcher.launch("hello.txt")
}
```

### How to pick a file to read its contents using the `GetContent` action

Use the `GetContent` contract when you want to let your user select a single file. Specify the mimetype you need via the launcher.

Once the user selects a file, you will have access to read it temporarily:

```kotlin
val launcher = rememberLauncherForActivityResult(GetContent()) { selectedUri ->
    if (selectedUri != null) {
        println("File selected = $selectedUri")
    } else {
        println("No file was selected")
    }
}
LaunchedEffect(Unit) {
    launcher.launch("image/jpeg")
}
```

Need to select multiple files? `GetMultipleContents` is also available. You can use it the same way as `GetContent` but it returns a list of selected `Uri`s instead.

### tl;dr Mimetypes

Mimetypes is a label that describes the combination of a file type and extension. It is not an Android or Jetpack Compose specific concept but you will see it being referenced all over Android, especially when you need to handle files such as audio and images.

A mimetype is described as a `type/subtype`. You may use a wildcard (`*`) as the subtype to specify you want any kind of file matching the type.

Common mimetypes include:

- image/jpeg: JPEG image files
- audio/mpeg: MP3 audio files
- text/plain: plain text files
- video/mp4: MP4 video files
- image/\*: any image file
- audio/\*: any audio file
- video/\*: any video file

Learn more about mimetypes [on developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types).

### How to pick a file for read/write using the `OpenDocument` action

Use this action to let the user pick a file from their device. You can then write into this uri or read its contents.

The following sample lets the user select an image from their device. The image will be displayed on the screen as soon as the user selects it. I am using [Coil-compose](https://coil-kt.github.io/coil/compose/) to display the image on the screen, as it handles asynchronous image loading, mapping the uri to a `Bitmap` and also recycling it when not needed:

```kotlin
var selectedUri by remember { mutableStateOf<Uri?>(null) }
val context = LocalContext.current

val launcher = rememberLauncherForActivityResult(OpenDocument()) { uri ->
    selectedUri = uri
}
AsyncImage(
    modifier = Modifier.fillMaxSize(),
    contentScale = ContentScale.Crop,
    model = ImageRequest.Builder(context)
        .data(selectedUri)
        .build(),
    contentDescription = null
)
LaunchedEffect(Unit) {
    launcher.launch(arrayOf("image/*"))
}
```

Need to open multiple documents? `OpenMultipleDocuments()` is also available.

### How to select a directory using the `OpenDocumenTree` action

Use this action to let the user pick a directory from their device. The returned Uri is the uri of the selected directory. According to the documentation "_Apps can fully manage documents within the returned directory._".

Some directories might not be accessible starting [Android 11](https://developer.android.com/about/versions/11/privacy/storage#directory-access).

```kotlin
val launcher = rememberLauncherForActivityResult(OpenDocumentTree()) { directoryUri ->
    if (directoryUri != null) {
        println("Selected $directoryUri")
    } else {
        println("No directory selected")
    }
}
LaunchedEffect(Unit) {
    launcher.launch(null)
}
```

### How to pick a contact using the `PickContact` action

Use this action to let the user select a contact. The returned uri points to the selected contact.

The contact uri returned is part of the [Contacts Provider API](https://developer.android.com/guide/topics/providers/contacts-provider).

```kotlin
val launcher = rememberLauncherForActivityResult(PickContact()) { contactUri ->
    if (contactUri != null) {
        // TODO use the Contacts Provider API to query information about the contact
        println("Contact selected = $contactUri")
    } else {
        println("No contact selected")
    }
}
LaunchedEffect(Unit) {
    launcher.launch()
}
```

### How to select images and videos using the `PickVisualMedia` action (Android 13 Photo Picker)

Android 13 introduced the [Photo Picker](https://developer.android.com/about/versions/13/features#photo-picker) feature. You can use it to let users choose specific photos and videos which can be read by your app instead of giving you access to all their files of the device.

If the Photo Picker feature is not available on the Android version you are running, the `OpenDocument()` action will be used instead. You can specify what kind of media you need (images, videos or both) via the launcher:

```kotlin
val launcher = rememberLauncherForActivityResult(PickVisualMedia()) { imageUri ->
    if (imageUri != null) {
        println("Images Selected = $imageUri")
    } else {
        println("No image selected")
    }
}

LaunchedEffect(Unit) {
    launcher.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))
}
```

Need to select multiple photos and videos? `PickMultipleVisualMedia` is also available. Pass the number of media you need via the constructor.

### How to select permissions using the RequestPermission action

Use the `RequestPermission` contract to request the runtime permission you need. As soon as the user grants or denies the permission, the provided callback will be called:

> **[Click here to learn how to properly handle permissions in your Android app](https://composables.com/blog/permissions.md)**

Here is how to request the `CAMERA` permission:

```kotlin
val launcher = rememberLauncherForActivityResult(RequestPermission()) { granted ->
    if (granted) {
        println("Camera permission granted")
    } else {
        println("Permission denied")
    }
}

LaunchedEffect(Unit) {
    launcher.launch(android.Manifest.permission.CAMERA)
}
```

Need to select multiple permissions at once? `RequestMultiplePermissions` is also available.

### How take photos and videos using `TakePhoto` and `CaptureVideo`

These actions can be used to ask the Camera app to take a photo or video. You need to provide the `Uri` of a file to store the data of the captured media:

Keep in mind that both `TakePhoto` and `CaptureVideo` require the `CAMERA` permission to work.

```kotlin
val launcher = rememberLauncherForActivityResult(TakePicture()) { captured ->
    if (captured) {
        println("Photo captured.")
    } else {
        println("Photo was not captured.")
    }
}
LaunchedEffect(Unit) {
    launcher.launch(photoUri)
}
```

The following sample demonstrates how to use request the `CAMERA` permission, then ask the user for a file to save a new video, then opens the camera app to record a video:

```kotlin
var videoCaptured by remember { mutableStateOf<Uri?>(null) }

val captureVideoLauncher = rememberLauncherForActivityResult(CaptureVideo()) { captured ->
    if (captured) {
        println("Video captured . Uri = $videoCaptured")
    } else {
        videoCaptured = null
    }
}

val selectFileLauncher =
    rememberLauncherForActivityResult(CreateDocument("video/mp4")) { videoUri ->
        if (videoUri != null) {
            videoCaptured = videoUri
            captureVideoLauncher.launch(videoUri)
        }
    }

val permissionLauncher = rememberLauncherForActivityResult(RequestPermission()) { granted ->
    if (granted) {
        selectFileLauncher.launch("video_${System.currentTimeMillis()}.mp4")
    }
}

LaunchedEffect(Unit) {
    permissionLauncher.launch(Manifest.permission.CAMERA)
}
```

### How to take a thumbnail photo using the `TakePicturePreview`

The `TakePicturePreview` action can be used to take a small photo.

Not entirely sure what you can do with this one other than sampling. You could potentially use this to take a small photo, blur it and scaling it up to create a nice looking visual (as a background).

Keep in mind that `TakePicturePreview` requires the `CAMERA` permission to work.

Make sure to recycle the loaded `Bitmap` like the sample below:

```kotlin
var capturedBitmap by remember { mutableStateOf<Bitmap?>(null) }

val launcher = rememberLauncherForActivityResult(TakePicturePreview()) { bitmap ->
    capturedBitmap?.recycle()
    capturedBitmap = bitmap
}
DisposableEffect(Unit) {
    onDispose {
        capturedBitmap?.recycle()
    }
}

if (capturedBitmap != null) {
    Image(
        capturedBitmap!!.asImageBitmap(),
        contentDescription = null,
        contentScale = ContentScale.Crop
    )
}

LaunchedEffect(Unit) {
    launcher.launch()
}
```

### How to start activity for result in Jetpack Compose

The `ActivityResultContract`s API relies on a powerful mechanism of Android called `Intent`s.

It is the API that allows apps to launch other apps and return data back to them. Before the ActivityResultContracts API, you would need to use `Activity.startActivityForResult()` to start the other app, and then override the `onActivityResult()` function to receive the result.

You can use the `StartActivityForResult()` action to start any kind of `Intent` you need, that might not be part of the existing predefined `ActivityResultContracts`:

```kotlin
val launcher = rememberLauncherForActivityResult(StartActivityForResult()) { activityResult ->
    when (activityResult.resultCode) {
        Activity.RESULT_OK -> {
            val data = activityResult.data
            println("Result was OK. Data received = $data")
        }
        Activity.RESULT_CANCELED -> println("Result was CANCELED")
    }
}

LaunchedEffect(Unit) {
    launcher.launch(Intent("com.example.yourpackage.CUSTOM_ACTION"))
}
```

> Make sure to include the namespace of your package if you are creating a new public Intent action. Actions are public and are exposed in the Android system.

Working with `IntentSender`? `StartIntentSenderForResult` is also available.

## How to create your own custom ActivityResultContract

You can create your custom ActivityResultContracts to reuse the same code in multiple places in your own app.

A custom `ActivityResultContract` is a good candidate if you are building a library or SDK, and you want to expose a simple entry point API to the consumers of your library.

Custom `ActivityResultContracts` need to implement the `ActivityResultContract<I,O>` abstract class. `I` stands for the input, and `O` stands for the returned data:

The following sample showcases how to

```kotlin
class CustomActivityContract : ActivityResultContract<String, Float?>() {
    override fun createIntent(context: Context, input: String): Intent {
        return Intent("com.example.yourpackage.CUSTOM_ACTION").apply {
            putExtra("com.example.yourpackage.EXTRA_PARAMETER", input)
        }
    }

    override fun parseResult(resultCode: Int, intent: Intent?): Float? {
        return when (resultCode) {
            Activity.RESULT_OK -> {
                val requiredIntent = requireNotNull(intent)
                requiredIntent.getFloatExtra("com.example.yourpackage.EXTRA_VALUE", -1f)
            }
            else -> null
        }
    }
}
```

You can then pass your custom action into the `rememberLauncherForActivityResult()` like any other action.

## When you should use ActivityResultContract

As a developer you want to focus on features and capabilities that makes your app unique. Your application should focus only on the functionality that is vital and/or unique to your app.

Consider asking other apps using `ActivitResultContract`s to do work for you that you do not want to implement yourself.

Building and maintaining functionality in your app that is not related to your business might end up being more expensive that you might think. Because of this using other apps to handle the load for you might be the simplest way to focus on the parts your apps shine.

If you are developing a library or an SDK, consider providing a custom `ActivityResultContract` to reduce the friction of entry to your SDK.

In this tutorial you learnt all about `ActivityResultContract`. More specifically you learn what `ActivityResultContracts` are, how they let you launch other applications to request for data using the predefined actions. All available `ActivityResultContract` were covered along with considerations when to use them.

---

**Related resources**

- [A comprehensive guide to Android runtime permissions using Jetpack Compose](https://composables.com/blog/permissions.md) on composables.co
- [A list of all available ActivityResultContracts](https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts) on developer.android.com

---

**Here is how I can help you**

- [View to Composable](https://viewtocomposable.com/): Includes more than 100+ code snippets of how to do things you already know from Android Views to Jetpack Compose. [Join 750+ developers](https://viewtocomposable.com/)
