Android 10: How to delete MediaStore item and it's associated data on file system programmatically?
Asked Answered
S

5

22

I am updating my app to use Scoped Storage feature introduced in Android 10.

My app works with MediaStore and displays images, videos and audio files and provides ability for user to delete item.

What I did earlier to delete file:

  1. Got path from MediaStore.MediaColumns.DATA
  2. Used new File(path).delete() to delete that file
  3. Manually updating MediaStore

Now that MediaStore.MediaColumns.DATA is not available I migrated to deleting items from MediaStore using ContentResolver.delete()

For example I have uri of the item: content://media/external/images/media/502 (its valid uri, I display it's thumbnail in grid). It doesnt matter whether I inserted this item in MediaStore or some other app did.

I use context.getContentResolver().delete(uri, null, null). It either succeeds in deletion (returns 1 row) or catching RecoverableSecurityException to use startIntentSenderForResult() to get access to current uri and then using the same getContentResolver().delete() to delete it in onActivityResult() and then getting successful deletion.

Either way that item is removed from MediaStore and is neither showing in result when I query MediaStore for images, nor in other applications.

BUT this file exists on file system (checked using SAF and various file managers (Google Files, Total Commander))

Sometimes (depends on Android version and media type) these items are brought back to MediaStore after phone reboot (or after opening Google Photos - it scans file system)

For example: Android 10 on my Google Pixel and Google Pixel 3a XL behaves as described above for Images/Video/Audio, but Android 9 on Xiaomi Mi A2 Lite behaves like this only with Audio files, while deleting Images/Video fine.

I have android:requestLegacyExternalStorage="false" in manifest.

Is there a way to force MediaStore to delete data on file system as well? Why is file on file system left behind?

Saiva answered 16/3, 2020 at 9:0 Comment(0)
T
8

Yes, as you have pointed out that's how we had to delete media files. We have to delete the physical copy of the file by forming a File object and also delete the indexed file in MediaStore using ContentResolver.delete() (or) do a media scan on the deleted file which would remove it's entry in MediaStore.

This is how it used to work in below Android 10 os. And would still work the same in Android 10 as well if you had opted out of scoped storage by specifying it in manifest android:requestLegacyExternalStorage="true"

Now in Android 11 you are forced to use scoped storage. If you want to delete any media file which is not created by you, you have to get the permission from the user. You can get the permission using MediaStore.createDeleteRequest(). This will show a dialog by description what operation users are about to perform, once the permission is granted, android has an internal code to take care of deleting both the physical file and the entry in MediaStore.

private void requestDeletePermission(List<Uri> uriList){
  if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
      PendingIntent pi = MediaStore.createDeleteRequest(mActivity.getContentResolver(), uriList);

      try {
         startIntentSenderForResult(pi.getIntentSender(), REQUEST_PERM_DELETE, null, 0, 0,
                  0);
      } catch (SendIntentException e) { }
    }
}

The above code would do both, requesting the permission to delete, and once permission granted delete the files as well.

And the callback result you would get it in onActivityResult()

Turnstile answered 12/11, 2020 at 8:29 Comment(3)
But I have android:requestLegacyExternalStorage="false" in manifest and rewritten app so that I never use File except in Context.getFilesDir(). I am using createDeleteRequest() for android 11, but android 10 still has problem I describedSaiva
@artman, Strange. Once I had tried to delete of whatever you did in Android 10 for Image type files, deleting it from media store deleted physical copy of the file as well. (And any reason for you to opt-in for scoped storage in Android 10, good thing would be to avoid it in Android 10 and as it is scoped storage in Android 10 and in Android 11 works slightly different, you might be ending up rewriting lot of code)Turnstile
MediaStore.createDeleteRequest uses ContentResolver.delete() function with a permission dialog. If you have the MANAGE_EXTERNAL_STORAGE permission, you can use ContentResolver.delete() to delete files from the file system as well.Miscreance
R
6

Use this function to delete file using display name of the file: This func will delete MediaStore item and it's associated data on file system in Android-10 or Android-Q

Note: In my case I am working with files like MediaStore.Files.FileColumns..

public static boolean deleteFileUsingDisplayName(Context context, String displayName) {

    Uri uri = getUriFromDisplayName(context, displayName);
    if (uri != null) {
        final ContentResolver resolver = context.getContentResolver();
        String[] selectionArgsPdf = new String[]{displayName};

        try {
            resolver.delete(uri, MediaStore.Files.FileColumns.DISPLAY_NAME + "=?", selectionArgsPdf);
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            // show some alert message
        }
    }
    return false;

}

Use this function to get Uri from DisplayName

public static Uri getUriFromDisplayName(Context context, String displayName) {

    String[] projection;
    projection = new String[]{MediaStore.Files.FileColumns._ID};

    // TODO This will break if we have no matching item in the MediaStore.
    Cursor cursor = context.getContentResolver().query(extUri, projection,
            MediaStore.Files.FileColumns.DISPLAY_NAME + " LIKE ?", new String[]{displayName}, null);
    assert cursor != null;
    cursor.moveToFirst();

    if (cursor.getCount() > 0) {
        int columnIndex = cursor.getColumnIndex(projection[0]);
        long fileId = cursor.getLong(columnIndex);

        cursor.close();
        return Uri.parse(extUri.toString() + "/" + fileId);
    } else {
        return null;
    }

}
Responsive answered 24/8, 2020 at 4:38 Comment(3)
What's in extUri?Nutrient
@Nutrient MediaStore.Images.Media.EXTERNAL_CONTENT_URICassaundra
This code is deleting the entry but if once deleted, if the same file needs to be created, how that can be achieved.Townes
P
2

According to my observations there is no force delete.

I usually add several checks if a file has really been deleted on Android Q it is also not possible to delete an entire album without the user manually confirming each file. this makes deleting files on the device uninteresting for me

Pleader answered 16/3, 2020 at 13:44 Comment(1)
Android 11 introduced createDeleteRequest and createTrashRequest to delete multiple files in one request. I also have to show multiple dialogs to delete one album, but I am fine with that. The only problem that they are reappeared in MediaStoreSaiva
A
1

I'm also experiencing the same issue, but with Huawei models where it fails with image & video files.

I've found that there are some reported bugs about it, although Google have discarded 2 of them because they can't reproduce it. I would advice that you star them and add some more details.

https://issuetracker.google.com/issues/157714528

https://issuetracker.google.com/issues/142270549

https://issuetracker.google.com/issues/145348304

Update:

I've created a new issue ticket, as some of the above have been discarded by google as not reproducible:

https://issuetracker.google.com/issues/184355104

Alsoran answered 3/4, 2021 at 8:45 Comment(0)
T
0

I fixed this problem in my app for Android 10 by opting out of scoped storage (android:requestLegacyExternalStorage="true") and requesting WRITE_EXTERNAL_STORAGE for Android 10 as well:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="29"
    tools:ignore="ScopedStorage" />

Then ContentResolver.delete will not trigger RecoverableSecurityException and will also delete the file from disk. Given you requested and was granted Manifest.permission.WRITE_EXTERNAL_STORAGE permission.

Note: If you don't care about deleting files not created by your app or created by previous installations of your app, then you don't need to request that permission.

Taps answered 20/12, 2020 at 4:43 Comment(1)
Unfortunately, this didn't work for me :( fwiw, I'm trying to do this in a UI test.Odyl

© 2022 - 2024 — McMap. All rights reserved.