Search code examples
androidmediastoreandroid-camera-intent

Correct way to launch camera via TakePicture contract and have the image saved in the Gallery


In my Android app, I need to let the user capture a photo using the camera for use by the app, but also ensure that the photo is saved into the device's shared gallery.

I am using the TakePicture activity result contract, and passing it a Uri that I build using the MediaStore API. The code in my Activity looks like this:

    private var takePictureUri: Uri? = null
    val takePictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) {
        result: Boolean ->
        val uri = takePictureUri
        if(result && uri != null) {
            // Do something with the captured image
        }
    }

    fun takePicture() {
        val collectionUri = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
            MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        } else {
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        }

        val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US)
            .format(System.currentTimeMillis())
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, timestamp)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
        }

        takePictureUri = contentResolver.insert(collectionUri, contentValues)
        takePictureLauncher.launch(takePictureUri)
    }

This all seems to work so far, which is great!

However, all the examples I could find of using the TakePicture activity supply a Uri using the FileProvider API, not the MediaStore API, so I am worried this may be a incorrect or poorly supported approach.

In particular, I am concerned about the fact that this approach requires me to specify a mime type before launching the TakePicture activity – how can I be sure the TakePicture activity will return a JPEG and not, say, a HEIF image?

Is there a more correct way to achieve my goal?


Solution

  • Your approach using the MediaStore API to save images directly into the gallery is correct and aligns with modern Android storage practices. However, to ensure consistency in image format, you should be mindful that the TakePicture activity may return different formats like JPEG or HEIF, depending on the camera app used. To enforce JPEG format, you can check the MIME type of the captured image using contentResolver.getType() and verify that it returns "image/jpeg". If not, you could convert the image to JPEG by compressing it using Bitmap.compress().

    While your current solution works well, an alternative approach involves using FileProvider for temporary file storage. You can create a temporary file using FileProvider and pass the URI to the TakePicture contract. After the image is captured, you can then move the image to MediaStore by inserting it into the gallery and compressing it to JPEG if needed. This approach avoids pre-specifying the MIME type and gives you more control over the image format.

    For devices running Android 10 (API 29) and above, it's essential to follow Scoped Storage guidelines, which limit direct file system access. The MediaStore API is the recommended approach, and if you're targeting Android 11+ (API 30), be mindful of access media permissions like ACCESS_MEDIA_LOCATION for files that need location metadata.

    In summary, your current solution is valid for saving images directly to the gallery, but adding format checks and compression ensures consistency across devices. If needed, using FileProvider for temporary storage followed by moving the file to MediaStore is a flexible approach.