Search code examples
androidandroid-jetpack-composeandroid-camera2

The file is saved as empty after app installation or app data clearing, but everything works fine after the second


I have 2 options for uploading a user profile photo:

  • choosing from the gallery using the PhotoPicker
  • take a photo from the camera

When I launch the application for the first time, or after clearing the application data, the photo loading from the gallery works fine. The second time also is fine.

If I run a application without data (after installation or after clearing the app data) and try to upload a photo from the camera - the file length is 0, and the file itself does not open in the explorer through AndroidStudio. That's why it doesn't upload to the server

But on the second attempt, the uploading from the camera works fine!

What wrong with my code?


Here is my code to take image from camera from composable function:

val file = remember { context.createImageFile() }
val uri = FileProvider.getUriForFile(Objects.requireNonNull(context), BuildConfig.APPLICATION_ID + ".provider", file)

val cameraLauncher =
            rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) { imageWasSaved ->
                 if (imageWasSaved) handleFileResult(context, uri, uploadPhoto)
            }

...

cameraLauncher.launch(uri)

Get image from gallery with PhotoPicker:

val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
           contract = ActivityResultContracts.PickVisualMedia(),
           onResult = { uri ->
                handleFileResult(context, uri, uploadPhoto)
           }
)

Code of handleFileResult function:

fun handleFileResult(
    context: Context,
    uri: Uri?,
    onResult: (File) -> Unit,
) {
    uri?.run {
        val parcelFileDescriptor = context.contentResolver.openFileDescriptor(this, "r", null)
        parcelFileDescriptor?.let {
            val inputStream = FileInputStream(it.fileDescriptor)
            val file = File(context.cacheDir, context.contentResolver.getFileName(this))
            val outputStream = FileOutputStream(file)
            val inChannel = inputStream.channel
            val outChannel = outputStream.channel

            try {
                inChannel.transferTo(0, inChannel.size(), outChannel)
                inputStream.close()
                outputStream.close()
                it.close()
                onResult(file)
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
    }
}

Solution

  • I figured out what the problem is with the help of the file manager. It turned out that when the camera was launched, not one, but several files were created, only one of them saved a photo, and the rest remained empty.

    I never understood why it worked after the second camera launch, but it's not that important.

    The problem itself lies in the fact that the context.createImageFile() method is called in every recomposition due to the fact that I did not use remember.

    So, the solution is quite simple and is to wrap these two lines in remember to avoid repeated calls during recomposition:

    val file = remember { context.createImageFile() }
    val uri = remember  { FileProvider.getUriForFile(Objects.requireNonNull(context), BuildConfig.APPLICATION_ID + ".provider", file) }