How to save a file (image) in Android and support API levels from 21 to 29?

1.3k views Asked by At

All new apps uploaded to Google Play must target Android 10 (API level 29) or higher. However, when app targets API level 29, the path returned from Environment.getExternalStorageDirectory() is no longer directly accessible to apps.

Documentation suggests using Context.getExternalFilesDir() (not suitable for my use case as the files placed there are internal to the app and are deleted when the app is uninstalled) or MediaStore, which is documented here.

I got stuck at the following snippet:

// Find all image files on the primary external storage device.
// On API <= 28, use VOLUME_EXTERNAL instead.
Uri collection = MediaStore.Images.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL_PRIMARY);

As I am trying to support both API <= 28 and API >= 29 I have tried the following:

    Uri collection;
    if (VERSION.SDK_INT >= VERSION_CODES.Q) {
        collection = MediaStore.Images.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL_PRIMARY);
    } else {
        collection = MediaStore.Images.Media.getContentUri(
            MediaStore.VOLUME_EXTERNAL);
    }

However, I am still getting a warning that MediaStore.VOLUME_EXTERNAL requires API level 29. Also, following the same documentation, I am trying to set MediaColumns.RELATIVE_PATH to provide the system a hint for where to store the newly-written files. The code snippet follows:

ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, file.getFilename());
values.put(MediaStore.Images.Media.RELATIVE_PATH, "My_image_app_folder");

Uri uri = resolver.insert(collection, values);

The same problem here, the field requires API level 29.

To sum it up, how to store a file (image) and support API levels from 21 to 29?

1

There are 1 answers

0
Louis Chabert On

Maybe this code can help you :

My pictures are storage in my assets folder, I don't know if it's a problem for you. At the end, the pictures a stored in the folder "Pictures" of android and are recognized by the gallery.

AssetManager assetManager = Objects.requireNonNull(requireContext()).getAssets();
    Context myContext = requireContext();
//Essential for creating the external storage directory for the first launch
    myContext.getExternalFilesDir(null);

    File photosFolder;
    if (Build.VERSION_CODES.R > Build.VERSION.SDK_INT) {
        photosFolder = new File(Environment.getExternalStorageDirectory(), "Pictures");
    } else {
        photosFolder = new File(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
    }
    if (!photosFolder.exists()) {
        photosFolder.mkdir();
    }

try {
        pictures = assetManager.list("photos/dataset1");
    } catch (IOException e) {
        Log.e("tag", "Failed to get asset file list.", e);
    }
    if (pictures != null) {
        for (String filename : pictures) {
            InputStream in;
            OutputStream out;
            try {
                in = assetManager.open("photos/dataset1/" + filename);
                File outFile = new File(photosFolder, filename);
                out = new FileOutputStream(outFile);
                copyFile(in, out);
                in.close();
                out.flush();
                out.close();
                MediaScannerConnection.scanFile(requireContext(), new String[]{outFile.toString()}, null, new MediaScannerConnection.OnScanCompletedListener() {
                    @Override
                    public void onScanCompleted(String path, Uri uri) {
                        Log.i("External Storage", "Scanned" + path + ":");
                        Log.i("External Storage", "uri " + uri);

                    }
                });
            } catch (IOException e) {
                Log.e("tag", "Failed to copy asset file: " + filename, e);
            }
        }
    } else {
        Log.e("Error NPE", "la variable pictures est null");
    }