6

I'm unable to store captured image in (getExternalFilesDir(Environment.DIRECTORY_PICTURES)) Android 11 device.

I have added <uses-permissionandroid:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> in manifest and all file access also. But it's not working.

if (Build.VERSION.SDK_INT >= 30) {
            if (!Environment.isExternalStorageManager()) {
                try {
                    val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
                    intent.addCategory("android.intent.category.DEFAULT")
                    intent.data = Uri.parse(String.format("package:%s", applicationContext.packageName))
                    startActivityForResult(intent, 2296)
                } catch (e: Exception) {
                    val intent = Intent()
                    intent.action = Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
                    startActivityForResult(intent, 2296)
                }
            }
        }

This code is working below Android 11 device. But on Android 11 file is not creating File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) .toString() + "/" + FolderName )

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Pritham Bnr
  • 839
  • 2
  • 9
  • 16

3 Answers3

4

Your phone's camera doesnot have permission to write in the specified location. So to fix this, you need to use file provider and give it appropriate permissions so that the camera can write the image to your file.

To do that,

  1. create a FileProvider. In your manifest file, add:
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />        // <-------- see this
        </provider>

Now create a files.xml file in your res/xml folder. In it, write some code:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <cache-path
        name="camera"
        path="Camera/" />
    <cache-path
        name="cache"
        path="/" />
    <files-path
        name="files"
        path="." />
    <external-path
        name="external"
        path="." />
    <external-files-path
        name="my_images"
        path="/"/>
// todo: add necessary folders according to your requirements...
// also, this is an old example. Consider googling for the latest style. I'm just copying from an old project I have, and it kinda works...
</paths>

So here we are giving the FileProvider the folders that can be shared with external apps. 2. Now create a uri where you want to store the photo. in your activity:

Context applicationContext = getApplicationContext();
File root = getCachedDir(); // consider using getExternalFilesDir(Environment.DIRECTORY_PICTURES); you need to check the file_paths.xml
        File capturedPhoto = new File(root, "some_photo.jpeg");
        if(!photoFile.exists()) {
            photoFile.mkdirs();
        }
        Uri photoURI = FileProvider.getUriForFile(applicationContext, applicationContext.getPackageName() + ".fileprovider", capturedPhoto);

Please note that my project needed to save picture temporarily, so I had used cachedDir. If you save photo permanently, use getExternalFilesDir(Environment.DIRECTORY_PICTURES); and modify file_paths.xml properly.

  1. Now that we have the correct uri, we can call the camera intent:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,photoURI);
        startActivityForResult(takePictureIntent, REQUEST_CODE);
  1. Finally, in activty result, do something:
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
  if(requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
    // todo: maybe show photo in an imageView
}
}

I hope this works.

Edit

If you are using this app in production, relying on android's default camera app is a bad idea. Our app previously used this way, and it works with, say, samsung's defaul camera. But a lot of our users used third party apps, such as PixArt, which doesnot save photo to our given location. So we had to implement a builtin camera using CameraX. So consider using CameraX or some other camera library.

Qazi Fahim Farhan
  • 2,066
  • 1
  • 14
  • 26
  • getting error - Caused by: java.io.IOException: No such file or directory – Pritham Bnr Jul 31 '21 at 11:03
  • try this``` File capturedPhoto = new File(root, "some_photo.jpeg"); if(!photoFile.exists()) { photoFile.mkdirs(); } Uri photoURI = FileProvider.getUriForFile(applicationContext, applicationContext.getPackageName() + ".fileprovider", capturedPhoto);``` – Qazi Fahim Farhan Jul 31 '21 at 11:12
  • 1
    `Due to scoped storage issues, your phone's camera doesnot have permission to write in the specified location.` That has nothing to do with scoped storage. Below Android Q any app can write to that location. But since Android N/7 you cannot use a file uri/path in the intent to start the camera app. Android N/7 up forces you to use a file provider then. – blackapps Jul 31 '21 at 13:06
1

First Thing is "android.permission.MANAGE_EXTERNAL_STORAGE" Permission has no relation with saving image. after Android 11 google say you should do your business in your space. that mean you cant get or save image or any file as you did before Android 11. you can only save in Shared folder or in your application storage data/App packagename/.....

if you want to access other app files then you need "android.permission.MANAGE_EXTERNAL_STORAGE" but google say this must be app prior functionality like filemanager or virus scanner like app.

As far as your App concern you havent provide save code. in Android 11 i am suggestion using Media Api

     ContentResolver resolver = mContext.getContentResolver();
                ContentValues contentValues = new ContentValues();
                contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, s);
                contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
                contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + getResources().getString(R.string.app_name) + File.separator + "imgfolder");
                contentValues.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
                contentValues.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
                contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
                Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
                fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
 
                try {
                    mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
                    fos.close(); 
                } catch (Exception e) {

                    e.printStackTrace();
                } finally {
                    contentValues.clear();
                    contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
                    resolver.update(imageUri, contentValues, null, null);
                }

this is for image file

0

use this code for save captured image

String mPath = Environment.getExternalStorageDirectory() + "/Print";
    Bitmap tmp = BitmapFactory.decodeFile(mPath);

 File imageFile = new File(mPath);


    FileOutputStream outStream;

    try
    {
        outStream = new FileOutputStream(imageFile);

        try
        {
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outStream);
            outStream.flush();
            outStream.close();
        } catch (IOException e)
        {
            e.printStackTrace();
        }

    } catch (Exception e)
    {
        e.printStackTrace();
    }
Pouria Hemi
  • 706
  • 5
  • 15
  • 30