Some problems with Android saving pictures of custom paths

The first is the issue of permissions

After android 10, the permissions will be further updated, which will further reduce the path of the files we save. At present, most of the storage directories are these
1. Internal storage space of the application (only the current APP can access data under this path, but other apps cannot access it):
The directory path is: data/data / package name, which will be generated after the app is created. The images stored by default will be saved here. However, the images here cannot be accessed in the gallery (i.e. android mobile photo album), because the app (photo album) cannot access the private directory of other apps
2. Shared storage:
Sharing is a folder publicly shared by Android. For pictures, there are two folders, one is DCIM directory and the other is pictures directory. Pictures can be stored in these two places
(Note: the version after KitKat no longer supports the user's operations such as writing to the external SDcard(Secondary Storage). If the user wants to copy files to the mobile phone, they can only be stored in the internal memory, but not in the external sdcard). There are 9 common directories in external storage (the first one in the table is the external storage path):
Environment.getExternalStorageDirectory()
Directory file path: / storage/sdcard/0
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS)
Directory file path: / storage/sdcard/0/Alarms
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
Directory file path: / storage/sdcard/0/DCIM
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS
Directory file path: / storage/sdcard/0/Download
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
Directory file path: / storage/sdcard/0/Movies
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)
Directory file path: / storage/sdcard/0/Music
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS)
Directory file path: / storage/sdcard/0/Notifications
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
Directory file path: / storage/sdcard/0/Pictures
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS)
Directory file path: / storage/sdcard/0/Podcasts
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES)
Directory file path: / storage/sdcard/0/Ringtones

Then there is the code for saving pictures. There are many Baidu, but there is no more explanation. Put a code for Android 10 and above:

    //Identification of whether the picture was saved successfully
    private final int SAVE_IMAGE_SUCCESS = 1;
    private final int SAVE_IMAGE_ERROR = 2;

    /**
     * Take a screenshot of the picture and save it to the SD card
     * Asynchronous operation can be used here, and it is best to use asynchronous operation, because there will be time-consuming operations
     */
    private void saveBitmap() {
      //Judge version
     LogUtil.e("Current mobile version:" + Build.VERSION.SDK_INT);
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
          saveImage(bitmap);
      } else {
         // setImageToNine(bitmap);
      }
      bitmap.recycle();
     } 

    /**
     * Save pictures and use them in SDK 10 and above
     *
     * @param bitmap
     */
    private void saveImage(Bitmap bitmap) {
        //Stored path
        String imageSaveFilePath = Environment.DIRECTORY_DCIM + File.separator + "com.demo";
//       String imageSaveFilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + "com.demo";
        LogUtil.e("Storage path:" + imageSaveFilePath);
        //New folder
        mkDir(imageSaveFilePath);

        // file name
        String mImageFileName = DateFormat.format("yyyyMMdd_hhmmss", new Date()).toString();
        // File path
        String mImageFilePath = imageSaveFilePath + File.separator + mImageFileName + ".png";

        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.TITLE, mImageFileName);
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName);
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
        contentValues.put(MediaStore.MediaColumns.DATE_TAKEN, mImageFileName);
        //Absolute path under application
        contentValues.put(MediaStore.MediaColumns.DATA, mImageFilePath);
        //The relative path of the media item in the storage device where the media item will be retained
        contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, imageSaveFilePath);


        Uri uri = null;
        OutputStream outputStream = null;
        ContentResolver localContentResolver = getContentResolver();

        try {
            uri = localContentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

            outputStream = localContentResolver.openOutputStream(uri);

            //Save the bitmap image to the data node corresponding to Uri
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);

            outputStream.flush();
            outputStream.close();
            
            mHandler.obtainMessage(SAVE_IMAGE_SUCCESS, mImageFilePath).sendToTarget();
        } catch (Exception e) {
            e.printStackTrace();
            if (uri != null) {
                localContentResolver.delete(uri, null, null);
            }
            mHandler.obtainMessage(SAVE_IMAGE_ERROR, e.toString()).sendToTarget();
        } finally {

            bitmap.recycle();

            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }


    //create folder
    public static void mkDir(String dirPath) {
        String[] dirArray = dirPath.split("/");
        String pathTemp = "";
        for (int i = 1; i < dirArray.length; i++) {
            pathTemp = pathTemp + "/" + dirArray[i];
            File newF = new File(dirArray[0] + pathTemp);
            if (!newF.exists()) {
                newF.mkdir();
            }
        }
    }


    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
          
            String dataObj = (String) msg.obj;
            LogUtil.e("Saved results:" + dataObj);
            if (msg.what == SAVE_IMAGE_SUCCESS) {
                Toast.makeText(BeautyActivity.this, "Saved address:" + dataObj, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(BeautyActivity.this, "Save failed:" + dataObj, Toast.LENGTH_SHORT).show();
            }
            return false;
        }
    });

The next is the problem

1. The code is executed, but the scanning is not successful, and the gallery cannot be viewed

getBaseContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(dataObj))));

This problem occurs in versions below 10: for problems that are saved to other directories but not displayed in the gallery, you can use the following code to try. The code has been tested

       //Send a notification to update the picture library
            ContentValues videoValues = new ContentValues();
            videoValues.put(MediaStore.Images.Media.TITLE, imageName);//imageName picture name
            videoValues.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
            videoValues.put(MediaStore.Images.Media.DATA, savePathFile.getAbsolutePath()); //Picture path

            getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, videoValues);

There is also a way to update the gallery. This way has some small problems. See the notes for details

    //Insert the file into the system Gallery, but there will be one more picture in the gallery, one is the picture of the original storage path, and the other is the picture under the Pictures path        
   MediaStore.Images.Media.insertImage(getContentResolver(), savePathFile.getAbsolutePath(), imageName, null);

2. Path problems above android 10

    //Stored path
        String imageSaveFilePath = Environment.DIRECTORY_DCIM + File.separator + "com.demo";
//       String imageSaveFilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + "com.demo";

The difference between the two paths
The former is' DCIM / com demo'
The latter path is' / storage / emulated / 0 / DCIM / com demo'

For the latter, a BUG will appear when the above code runs:
'java.lang.IllegalArgumentException: Primary directory (invalid) not allowed for content://media/external/images/media; allowed directories are [DCIM, Pictures]'
Prompt: main directory of content (invalid): mediaexternalimages media; The allowed directories are [DCIM, pictures]

But the former code will not have this problem. There is no answer to this problem. If the boss with the answer can see it, he can say it to me. Thank you!!!

Added by blackandwhite on Thu, 10 Feb 2022 09:35:31 +0200