3

In my app using Jetpack Compose, how can I use the existing stock photo app to take a picture and store it? Google's documentation mentions the depreciated Camera API by using an Intent, but they're using the old view system. And it seems like the newer Camera2 and CameraX APIs both are intended for creating custom camera interfaces directly in the app.

Raj Narayanan
  • 2,443
  • 4
  • 24
  • 43

2 Answers2

7

You have to use the activity contracts, see this article for details

class ComposeFileProvider : FileProvider(
    R.xml.filepaths
) {
    companion object {
        fun getImageUri(context: Context): Uri {
            val directory = File(context.cacheDir, "images")
            directory.mkdirs()
            val file = File.createTempFile(
                "selected_image_",
                ".jpg",
                directory,
            )
            val authority = context.packageName + ".fileprovider"
            return getUriForFile(
                context,
                authority,
                file,
            )
        }
    }
}

@Composable
fun ImagePicker(
    modifier: Modifier = Modifier,
) {
    var hasImage by remember {
        mutableStateOf(false)
    }
    var imageUri by remember {
        mutableStateOf<Uri?>(null)
    }

    val imagePicker = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent(),
        onResult = { uri ->
            hasImage = uri != null
            imageUri = uri
        }
    )

    val cameraLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.TakePicture(),
        onResult = { success ->
            hasImage = success
        }
    )

    val context = LocalContext.current
    Box(
        modifier = modifier,
    ) {
        if (hasImage && imageUri != null) {
            AsyncImage(
                model = imageUri,
                modifier = Modifier.fillMaxWidth(),
                contentDescription = "Selected image",
            )
        }
        Column(
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .padding(bottom = 32.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            Button(
                onClick = {
                    imagePicker.launch("image/*")
                },
            ) {
                Text(
                    text = "Select Image"
                )
            }
            Button(
                modifier = Modifier.padding(top = 16.dp),
                onClick = {
                    val uri = ComposeFileProvider.getImageUri(context)
                    imageUri = uri
                    cameraLauncher.launch(uri)
                },
            ) {
                Text(
                    text = "Take photo"
                )
            }
        }
    }
}
Francesc
  • 25,014
  • 10
  • 66
  • 84
4

You're a bit confused. There's 3 ways to take a picture.

1)Intent. This does not use views- it launches the camera app to take the picture. If you don't need tighter control, use this, it's the easiest way.

2)Camera 1 API. This is deprecated. This does not use views, because it doesn't require you to display anything. Displaying something is your option.

3)Camera 2 API. This is the current API way to do it. Once again, it doesn't require views, because it doesn't require you to display anything.

Also, you did know that you can wrap and view into Jetpack using AndroidView, right? So even if you did want to use a camera built in display view, you can use that. Get used to doing things like that if you go down the compose route, very few libraries use compose.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • By "the old view system", I meant they were using the view layout system for the app, not for the camera interface. So, can you launch the external, stock camera app with the `Camera2` API in Jetpack Compose? And I want to launch the external photo app, not embed the camera into the app with `AndroidView`. – Raj Narayanan Jun 05 '22 at 22:55
  • @rajndev Compose has nothing to do with launching other apps. So yes. Compose is only a replacement for the UI, it doesn't change anything else. Imagine that there's a normal android view called a ComposeView that parses compose data and displays it- that's how it actually works. Calling setContent creates a ComposeView, sets it as your root view, and loads the content into it using setContent on the ComposeView. Nothing changed, you just have 1 view with a lot of subnodes. – Gabe Sechan Jun 05 '22 at 22:56
  • Ok. Can you use an `Intent` with `Camera2` or `CameraX` to launch the external camera app? Your list above implies that this is not possible. – Raj Narayanan Jun 05 '22 at 23:00
  • 1
    You just use an Intent. The Camera and Camera2 APIs are for taking the picture yourself. Intents are used to open other apps. – Gabe Sechan Jun 05 '22 at 23:03
  • https://stackoverflow.com/questions/2729267/android-camera-intent – Gabe Sechan Jun 05 '22 at 23:05
  • Ok. I'll try using the SO question in the link above to use it with Jetpack Compose. Thanks. – Raj Narayanan Jun 05 '22 at 23:07