Search code examples
androidkotlinandroid-jetpack-composeandroid-cameraandroid-gallery

How to crop an image from camera and gallery directly in Jetpack Compose


I would like to implement a functionality where the user can choose between the camera and gallery options when they click on a button. Once the user selects an image from either option, I want to enable them to crop the selected image. Unfortunately, I haven't been able to find a suitable library for this task, and even if one exists, the documentation appears to be lacking.

I also tried some libraries for Kotlin (XML) and they did not work, as well.


Solution

  • You can use this library to enable crop feature once user selects image from gallery and clicks image from camera: https://github.com/CanHub/Android-Image-Cropper

    Sample implementation:

    
      var openImageChooser = rememeber { mutableStateOf(false) }
    
    
     // For gallery image chooser
     val imageCropLauncher = rememberLauncherForActivityResult(
            contract = CropImageContract()
        ) { result ->
            if (result.isSuccessful) {
                // Got image data. Use it according to your need
            } else {
                // There is some error while choosing image -> show error accordingly(result.error)
            }
        }
    
        val imagePickerLauncher =
            rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) { uri: Uri? ->
                val cropOptions = CropImageContractOptions(uri, CropImageOptions())
                imageCropLauncher.launch(cropOptions)
            }
    
    
        // For camera image
        val cameraLauncher = rememberLauncherForActivityResult(
            contract = ActivityResultContracts.TakePicturePreview()
        ) { cameraBitmap ->
            cameraBitmap?.let {
                val fileName = "IMG_${System.currentTimeMillis()}.jpg"
                val imageFile = File(context.filesDir, fileName)
                try {
                    val out = FileOutputStream(imageFile)
                    it.compress(Bitmap.CompressFormat.JPEG, 100, out)
                    out.flush()
                    out.close()
                } catch (e: Exception) {
                    e.printStackTrace()
                }
                // Got image data. Use it according to your need(imageFile)
            }
        }
    
    
        if (openImageChooser) {
            UploadImageAlertDialog(
                onCameraClick = {
                    cameraLauncher.launch()
                    openImageChooser = false
                },
                onGalleryClick = {
                    imagePickerLauncher.launch("image/*")
                    openImageChooser = false
                },
                onDismissClick = { openImageChooser = false }
            )
        }
    
        Button(onClick = { openImageChooser = true }){
           ...
        }
    
    @Composable
    fun UploadImageAlertDialog(
        onCameraClick: () -> Unit,
        onGalleryClick: () -> Unit,
        onDismissClick: () -> Unit
    ) {
        Dialog(
            onDismissRequest = { onDismissClick() },
            DialogProperties(usePlatformDefaultWidth = false)
        ) {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 20.dp)
                    .clip(RoundedCornerShape(28.dp))
                    .background(AppTheme.colorScheme.containerNormalOnSurface),
                contentAlignment = Alignment.Center
            ) {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(horizontal = 24.dp)
                ) {
                    Text(
                        text = "Camera",
                        style = AppTheme.appTypography.subtitle2,
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(top = 24.dp)
                            .clickable { onCameraClick() }
                    )
                    Text(
                        text = "Gallery",
                        style = AppTheme.appTypography.subtitle2,
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(top = 24.dp)
                            .clickable {
                                onGalleryClick()
                            }
                    )
                    Spacer(modifier = Modifier.height(24.dp))
                }
            }
        }
    }
    

    Add this in AndroidManifest.xml

    <activity android:name="com.canhub.cropper.CropImageActivity"
                android:theme="@style/Base.Theme.AppCompat"/>