Android Image Capture action (aka ActivityResultContracts.TakePicture()) always returns landscape photos

1.2k views Asked by At

I am trying to capture photos in my app using standard camera app intent (I am NOT interested in using JetpackX or other library to have a viewfinder in my app).

When I had the code in my Fragment like so:

// This is in response to user clicking a button
fun startPhotoTaking() {
    val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    resultLauncher.launch(takePictureIntent)
}

private var resultLauncher =
    registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val photo = result.data?.extras?.get("data") as? Bitmap
            photo?.let {
                // ... do whatever
            }
        }
    }

Then the photo Bitmap came back tiny as apparently Android caps intents at 1 Mb , but the orientation was correct.

Since I actually need the original large image, I have modified the code like so:

// This is in response to user clicking a button
fun startPhotoTaking() {
    lastUri = getTmpFileUri()
    if (lastUri != null) {
        resultLauncher.launch(lastUri)
    }
}
private fun getTmpFileUri(): Uri {
    requireContext().cacheDir.listFiles()?.forEach { it.delete() }
    val tmpFile = File
        .createTempFile("tmp_image_file", ".jpg", requireContext().cacheDir)
        .apply {
            createNewFile()
        }
    return FileProvider.getUriForFile(
        MyApplication.instance.applicationContext,
        "${BuildConfig.APPLICATION_ID}.provider",
        tmpFile
    )
}
var lastUri: Uri? = null
private var resultLauncher =
    registerForActivityResult(ActivityResultContracts.TakePicture()) { result ->
        if (result) {
            val photoUri = lastUri
            if (photoUri != null) {
                val stream = MyApplication.instance.applicationContext.contentResolver
                        .openInputStream(photoUri)
                val photo = BitmapFactory.decodeStream(stream)
                stream?.close()

                // ... do whatever
               // If i try ExifInterface(photoUri.path!!)
            }
        }
    }

Now I do receive the actual large photo, but it is always landscape :(

I tried creating an instance of ExifInterface(photoUri.path) but that throws an exception for me (which I don't quite understand as I am only writing/reading to my own app's cache directory?):

java.io.FileNotFoundException: /cache/tmp_image_file333131952730914647.jpg: open failed: EACCES (Permission denied)

How can I get my photo to retain orientation when saved to file and/or get access to read EXIF parameters so I can rotate it myself?

Update

As a workaround, this did the trick but it's just... ungodly. So am very keen to find better solutions.

val stream = MyApplication.instance.applicationContext.contentResolver
                        .openInputStream(photoUri)
if (stream != null) {
    val tempFile = File.createTempFile("tmp", ".jpg", requireContext().cacheDir)
                                        .apply { createNewFile() }
    val fos = FileOutputStream(tempFile)
    val buf = ByteArray(8192)
    var length: Int
    while (stream.read(buf).also { length = it } > 0) {
        fos.write(buf, 0, length)
    }
    fos.close()
    val exif = ExifInterface(tempFile.path)
    val orientation =
        exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1)
    val matrix = Matrix()
    if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
        matrix.postRotate(90f)
    } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
        matrix.postRotate(180f)
    } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
        matrix.postRotate(270f)
    
    var photo = BitmapFactory.decodeFile(tempFile.path)
    photo = Bitmap.createBitmap(
        photo,
        0,
        0,
        photo.width,
        photo.height,
        matrix,
        true
    )
}
2

There are 2 answers

2
Abdul Rahman On

You can rotate image to the particular angle like this,

// To rotate image
private fun rotateImage(source: Bitmap, angle: Float): Bitmap? {
            val matrix = Matrix()
            matrix.postRotate(angle)
            return Bitmap.createBitmap(
                source, 0, 0, source.width, source.height,
                matrix, true
            )
        }

In your case set angle to 90f to get image in portrait orientation.

2
blackapps On

ExifInterface(photoUri.path)

That isnot an existing path as you have seen.

Better use:

ExifInterface( tmpFile.getAbsolutePath())