1

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
    )
}
zaitsman
  • 8,984
  • 6
  • 47
  • 79
  • 1
    Assuming you are using the [AndroidX ExifInterface](https://developer.android.com/reference/kotlin/androidx/exifinterface/media/ExifInterface) (the only one with bug fixes that work on all devices), you can pass the `InputStream` returned by `contentResolver.openInputStream()` to [its constructor](https://developer.android.com/reference/kotlin/androidx/exifinterface/media/ExifInterface#exifinterface_4)) directly. – ianhanniballake Apr 11 '22 at 04:34
  • Is there a reason you're not using any of the many image loading libraries like Picasso, Glide, or [Coil](https://github.com/coil-kt/coil) that take care of reading the orientation from Exif data for you? – ianhanniballake Apr 11 '22 at 04:41
  • @ianhanniballake I need to TAKE a photo and SEND it to the server, resized to 25% jpeg. What do any of the libraries you mentioned have to do with that? – zaitsman Apr 11 '22 at 04:48
  • @ianhanniballake the suggestion you provided with AndroidX ExifInterface did work in that it actually read my content resolver stream (yay). Except of course once the stream is consumed, I can't figure out how to reset it to create an actual Bitmap out of it :( – zaitsman Apr 11 '22 at 04:49
  • 1
    You should open another stream. – blackapps Apr 11 '22 at 05:57
  • @blackapps Yeah that does work... still feels somewhat wrong. Why can I get the image in the CORRECT rotation if I don't save it on disk? Android is so weird sometimes... – zaitsman Apr 11 '22 at 12:44
  • You do not have to save it again on disk as it is already on disk. See my answer how to use the existing file. – blackapps Apr 11 '22 at 13:43

2 Answers2

0

ExifInterface(photoUri.path)

That isnot an existing path as you have seen.

Better use:

ExifInterface( tmpFile.getAbsolutePath())
blackapps
  • 8,011
  • 2
  • 11
  • 25
-1

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.

Abdul Rahman
  • 107
  • 3