I know it seems like a very basic question, but it's specifically for Android Q.
I just want to get an image from Gallery and compress it and send to the server. But because of the Android Q's Scoped Storage, it's harder than I thought. I'll first explain what I did with code:
First I send out the intent to pick the image.
fun openGallery(fragment: Fragment){
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.type = "*/*"
val mimeTypes = arrayOf("image/*")
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
fragment.startActivityForResult(intent, REQUEST_IMAGE_PICK)
}
It works fine, and I'm able to get the image in the onActivityResult method
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_IMAGE_PICK && resultCode == Activity.RESULT_OK && null != data) {
val selectedImage = data.data
val source = ImageDecoder.createSource(activity!!.contentResolver, selectedImage)
val bitmap = ImageDecoder.decodeBitmap(source)
mBinding.circularProfileImage.setImageBitmap(bitmap)
}
}
Okay now the question is how can I access this image in File format, so I can further process/compress it.
Following things I've tried:
val mImagePath = getImagePathFromUri(activity!!, selectedImage)
This is the path I've got:
/storage/emulated/0/DCIM/Camera/IMG_20191022_152437.jpg
I created a file from it, in the following way:
val file = File(mImagePath)
And Following is my custom logic to compress and upload image:
val mNewFile = MediaCompressor.compressCapturedImage(activity!!, file, "ProfilePictures")
uploadProfile(mNewFile)
In this custom logic, I have a method to handle sampling and rotation of the image as follows:
fun handleSamplingAndRotationBitmap(context: Context, selectedImage: File, reqWidth: Int, reqHeight: Int): Bitmap {
val mUri = Uri.fromFile(selectedImage)
// First decode with inJustDecodeBounds=true to check dimensions
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
var imageStream = context.contentResolver.openInputStream(mUri)
BitmapFactory.decodeStream(imageStream, null, options)
imageStream!!.close()
// Calculate inSampleSize
options.inSampleSize =
calculateInSampleSize(options, reqWidth, reqHeight)
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false
imageStream = context.contentResolver.openInputStream(mUri)
var img = BitmapFactory.decodeStream(imageStream, null, options)
img = rotateImageIfRequired(context, img!!, mUri)
return img
}
But when I'm trying to open the stream using context.contentResolver.openInputStream I get the following error:
java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG_20191022_152437.jpg: open failed: EACCES (Permission denied)
I know I'm getting this because in Android 10 we don't have the permission to directly access files from external storage.
So, please help me figure this out, how can I use the image from external storage as a file in Android 10.
Note: I've all the required permissions, so that's not the issue
Following things I've tried:
There is no possible reliable getImagePathFromUri()
implementation.
In this custom logic, I have a method to handle sampling and rotation of the image as follows:
You do not need a File
in that function. After all, your very first statement in that function goes and creates a Uri
from that File
. So, replace the File
parameter with the Uri
that you have, and skip the Uri.fromFile()
call.
how can I use the image from external storage as a file in Android 10.
You can't. And, as demonstrated above, you do not need it for what you are doing.
If you find yourself in some situation where you are stuck using some library or API that absolutely positively must have a File
:
InputStream
on the content, using contentResolver.openInputStream()
, as you are doing todayInputStream
to some FileOutputStream
on a file that you can read/write (e.g., getCacheDir()
on Context
)File