Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access photos from external storage in Android Q

I recently upgraded the app's target version to API 29. Due to the scoped storage in Android 10, i used MediaStore API to store and retrieve images from app external storage. Earlier, i used getExternalStoragePublicDirectory to store images taken through camera, now i use MediaStore.Images.Media.EXTERNAL_CONTENT_URI to write file to an external storage location.

Issue i am facing now is, When i open my app and take pictures, it stores under a folder name that i gave 'myapp' and i can retrieve my images through Mediastore cursor and show them in a custom gallery. And when i uninstall my app 'myapp' folder still exists. And when i install my app again and try to read the images from the gallery, cursor is not returning any image. But if i take picture again, then i could load them to my custom gallery. Custom gallery view is just a row of images at the bottom of the screen, so that user doesn't have to browse through the photos folder to load the image to the app.

This is how i store my images in the MediaStore

Content values:

String RELATIVE_PATH = Environment.DIRECTORY_PICTURES + File.separator + "myApp";
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, generateImageName(new Date()));
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, RELATIVE_PATH);

Generate Name method:

int sameSecondCount;
protected String generateName(Date now)
    {
        String result = formatter.format(now);

        long nowMillis = now.getTime();
        if (nowMillis / 1000 == lastMillis / 1000)
        {
            sameSecondCount++;
            result += "_" + sameSecondCount;
        }
        else
            sameSecondCount = 0;

        lastMillis = nowMillis;

        return result + PICTURE_EXTENSION_JPG;
    }
@WorkerThread
    private Uri writePictureToFile(ContentValues contentValues, byte[] bitmapBytes) throws IOException
    {
        final ContentResolver resolver = getApplication().getContentResolver();

        Uri uri = null;
        final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

        try
        {
            uri = resolver.insert(contentUri, contentValues);

            if (uri == null)
                throw new IOException("Failed to create new MediaStore record.");

            OutputStream stream = resolver.openOutputStream(uri);

            if (stream == null)
            {
                throw new IOException("Failed to get output stream.");
            }

            stream.write(bitmapBytes);
        }
        catch (IOException e)
        {
            // Delete the content from the media store
            if (uri != null)
                resolver.delete(uri, null, null);
            throw e;
        }
        return uri;
    } 

Reading images

{
                String selectionMimeType = MediaStore.Files.FileColumns.MIME_TYPE + " in (?,?,?)";
                String[] args = new String[]{
                    MimeTypeMap.getSingleton().getMimeTypeFromExtension("jpg"),
                    MimeTypeMap.getSingleton().getMimeTypeFromExtension("png")};

                Cursor cursor = context.getContentResolver()
                    .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, selectionMimeType, selectionArgs,
                        orderBy + " DESC");
                if (cursor != null)
                {
                    int idColumnIndex = imageCursor.getColumnIndex(MediaStore.Images.Media._ID);
                    imageCursor.moveToFirst();
                    int imageCount = imageCursor.getCount();
                    for (int i = 0; i < imageCount && i < totalCount; i++)
                    {
                        final long imageId = imageCursor.getLong(idColumnIndex);
                        Uri uriImage = Uri.withAppendedPath(uriExternal, "" + imageId);
                        GalleryData galleryImageData = new GalleryImageData(imageId, uriImage); // Custom class with id and Uri
                        galleryViewModelList.add(galleryImageData);
                        imageCursor.moveToNext();
                    }
                    imageCursor.close();
                }

Why the images that i stored in the folder in Mediastore is not being returned by the above code when i reinstall my App. Is it by design or am i missing something?

These are the columns i am retrieving,

final String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, MediaStore.Images.Media.MIME_TYPE };
final String orderBy = MediaStore.Images.Media.DATE_TAKEN; ```

like image 936
Madhan Avatar asked Apr 07 '20 05:04

Madhan


People also ask

How do I manage external storage permissions in Android?

On the Settings > Privacy > Permission manager > Files and media page, each app that has the permission is listed under Allowed for all files. If your app targets Android 11, keep in mind that this access to "all files" is read-only.

Where are the files stored in external storage Android?

Android External Storage Example Code getExternalFilesDir(): It returns the path to files folder inside Android/data/data/application_package/ on the SD card. It is used to store any required files for your app (like images downloaded from web or cache files).


1 Answers

For unclear reasons, Android 10 considers two app installations separated by time to be separate apps, no different than if those were two completely different apps.

As a result, once your app is uninstalled, it loses access to all files that it created... even if the same app is later reinstalled.

So, you need to treat files from a previous installation of your app the same way as you would treat files from completely unrelated apps: request READ_EXTERNAL_STORAGE to be able to query for them and read their contents. The "read their contents" part requires android:requestLegacyExternalStorage="true" on the <application> in the manifest.

like image 131
CommonsWare Avatar answered Sep 27 '22 17:09

CommonsWare