Search code examples
androidandroid-cameraandroid-jetpack-compose

How to use stock camera app to take photo with Jetpack Compose?


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.


Solution

  • 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"
                    )
                }
            }
        }
    }