Parag Pawar
Parag Pawar

Reputation: 867

Android Q: Get image from gallery and process it

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

Upvotes: 7

Views: 11938

Answers (2)

Sanjay S. Gangwar
Sanjay S. Gangwar

Reputation: 1

Create a Directory for data to be stored in Android/data/package name by:

private void createDir() {
    String timeStamp = utils.currentTimeStamp();
    File storageDir = getExternalFilesDir(null);
    File image;
    try {
        image = File.createTempFile(timeStamp, ".png", storageDir);
        Log.i("SANJAY ", "createDir: " + image.getPath());
    } catch (IOException e) {
        e.printStackTrace();
        Log.i("SANJAY ", "createDir: " + e.getMessage());

    }
}

now call the gallery intent:

 intent = new Intent();
 intent.setAction(Intent.ACTION_GET_CONTENT);
 intent.addCategory(Intent.CATEGORY_OPENABLE);
 intent.setType("image/*");

 startActivityForResult(intent, 100);

In onActivityResult():

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        if (requestCode == 100) {
           Uri mediaUri = data.getData();
            
            //display the image
            try {
                InputStream inputStream = getBaseContext().getContentResolver().openInputStream(mediaUri);
                Bitmap bm = BitmapFactory.decodeStream(inputStream);

                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                byte[] byteArray = stream.toByteArray();

                bind.photo.setImageBitmap(bm);
                //Log.i("SANJAY ", "onActivityResult: " + saveBitMap(this, bm));
                uri = Uri.fromFile(saveBitMap(this, bm));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }

        } 
    }
}

the get the Uri from File using this method:

private File saveBitMap(Context context, Bitmap Final_bitmap) {
    File pictureFileDir = new File(Environment.getExternalStorageDirectory()
            + "/Android/data/"
            + getApplicationContext().getPackageName()
            + "/"/*Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), ""*/);
    if (!pictureFileDir.exists()) {
        boolean isDirectoryCreated = pictureFileDir.mkdirs();
        if (!isDirectoryCreated)
            Log.i("SANJAY ", "Can't create directory to save the image");
        return null;
    }
    String filename = pictureFileDir.getPath() + File.separator + System.currentTimeMillis() + ".jpg";
    File pictureFile = new File(filename);
    try {
        pictureFile.createNewFile();
        FileOutputStream oStream = new FileOutputStream(pictureFile);
        Final_bitmap.compress(Bitmap.CompressFormat.PNG, 18, oStream);
        oStream.flush();
        oStream.close();
        Log.i("SANJAY ", "saveBitMap :: Save Image Successfully..");

    } catch (IOException e) {
        e.printStackTrace();
        Log.i("SANJAY", "There was an issue saving the image.");
        Log.i("SANJAY", "Error :: " + e.getLocalizedMessage());
    }
    return pictureFile;
}

Upvotes: 0

CommonsWare
CommonsWare

Reputation: 1007359

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:

  • Open an InputStream on the content, using contentResolver.openInputStream(), as you are doing today
  • Copy the bytes from that InputStream to some FileOutputStream on a file that you can read/write (e.g., getCacheDir() on Context)
  • Use your copy with the library or API that requires a File

Upvotes: 6

Related Questions