How to delete an Image in Android 10 Scoped Storage (Mediastore Entry & File)
Asked Answered
C

3

7

When I try to delete an image that has been contributed/owned by my app it can be deleted easily. But for shared media inside the Pictures folder that my app has not owned, I show users a prompt by catching RecoverableSecurityException. But even after ALLOWING, I'm not able to delete that particular image file.

Here's the code I'm using, please point out what am I doing wrong.

  • The Image doesn't appear in Gallery or my app after deletion but it stays inside the File Manager and appears back into the Gallery after the phone reboots (only the MediaStore entry gets deleted I guess)
  • The code works fine for Android 11 devices.
  • The result from startIntentSenderForResult(intentSender, 12, null, 0, 0, 0, null); is RESULT_OK

For fetching files: (This code is in My Activity)

   try {
            String DIRECTORY_NAME = "%Pictures/App_Name%";
            String selection = MediaStore.MediaColumns.RELATIVE_PATH + " like ? ";
            String[] selectionArgs = new String[]{DIRECTORY_NAME};
            ContentResolver contentResolver = this.getContentResolver();
            Cursor cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, selection, selectionArgs, null);

            while(cursor.moveToNext()){
                long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
                Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);

                uriArrayList.add(contentUri);

            }
            cursor.close();
        }
        catch (Exception e){
            e.printStackTrace();
        }

For Deleting Image Files: (This code is in my Adapter class)

    try {
    ContentResolver contentResolver = context.getContentResolver();
    cachedUri = f; //f is Uri 
    contentResolver.delete(f, null, null);
}catch (SecurityException securityException) {

    RecoverableSecurityException recoverableSecurityException;
    if (securityException instanceof RecoverableSecurityException) {
        recoverableSecurityException =
                (RecoverableSecurityException) securityException;
    } else {
        throw new RuntimeException(
                securityException.getMessage(), securityException);
    }
  
    IntentSender intentSender = recoverableSecurityException.getUserAction()
            .getActionIntent().getIntentSender();
    try {
        resultInterface.onResultTaken(intentSender, cachedUri); //Giving a call to the activity that implements the interface

    } catch (Exception e) {
        e.printStackTrace();
    }
}

Prompting the user (Inside My Activity to get onActivityResult):

public class SomeActivity extends AppCompatActivity implements ResultTakenListener{

protected void onCreate(){ 
...}

@Override
public void onResultTaken(IntentSender intentSender, Uri uri) throws IntentSender.SendIntentException {
    cacheduri = uri;
    startIntentSenderForResult(intentSender, 12, null, 0, 0, 0, null);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode == 12){
    Log.d("Result Code is-->", String.valueOf(resultCode)); //This gives RESULT_OK
        if(resultCode == RESULT_OK){
            ContentResolver contentResolver = this.getContentResolver();
            contentResolver.delete(cacheduri, null, null);
    
        }
    }
}}
Countrybred answered 28/6, 2021 at 13:47 Comment(0)
S
2

Well. There are 2 methods to do it. One of them will work on android 11 as well. I am not talking about the requestLegacyExternalStorage permission here. You can also do it that way, btw.

In Android 10, You have to take delete files permission for each file if you're not using requestLegacyExternalStorage permission or the Storage Access Framework (DocumentsProvider way).

To do that, check this method.

For Android 10 :

public static void deleteImageAPI29(Context context, Uri uri) {
    ContentResolver resolver = context.getContentResolver();
    try {
        resolver.delete(uri, null, null);
    } catch (SecurityException securityException) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            RecoverableSecurityException recoverableSecurityException = (RecoverableSecurityException) securityException;
            IntentSenderRequest senderRequest = new IntentSenderRequest.Builder(recoverableSecurityException.getUserAction()
        .getActionIntent().getIntentSender()).build();
            deleteResultLauncher.launch(senderRequest);
        }
    }
}

Now, the deleteResultLauncher stands here as an alternative of the onActivityResult() API replacement as it's deprecated from android 11.

Here is how you declare it :

ActivityResultLauncher<IntentSenderRequest> deleteResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartIntentSenderForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == RESULT_OK){
                    Toast.makeText(context, "Image deleted.", Toast.LENGTH_SHORT).show();
                }
            }
        }
);

It will prompt a dialog to delete the image. If user allows it, then it will delete that image without any problem.

For Android 11 :

public void deleteImageAPI30(Context context, ArrayList<Media> arrayList) {
    ContentResolver contentResolver = context.getContentResolver();
    ArrayList<Uri> arrayList2 = new ArrayList();
    for (int i = 0; i < arrayList.size(); i++) {
        arrayList2.add(arrayList.get(i).getUri()); // You need to use the Uri you got using ContentUris.withAppendedId() method
    }
    Collections.addAll(arrayList2);
    IntentSender intentSender = MediaStore.createDeleteRequest(contentResolver, arrayList2).getIntentSender();
    IntentSenderRequest senderRequest = new IntentSenderRequest.Builder(intentSender)
            .setFillInIntent(null)
            .setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 0)
            .build();
    deleteResultLauncher.launch(senderRequest);
}

The declaration of the deleteResultLauncher is the same here as shown above.

Second method is more useful here, as it can be used to delete multiple images at a time. But unfortunately, it starts from Android 11. It's not available in android 10. So if you want to delete multiple images in android 10, then you can go with requestLegacyExternalStorage option, or 'DocumentsProvider' API.

Apart from that, these two methods are perfect for the cause.

And yes, If you want to save the images in Android 10 & above versions with Mediastore API, then have a look at this article, it will help you a lot.

Scorper answered 5/12, 2021 at 17:28 Comment(0)
T
1

I have not tested it but I think you need to integrate custom behaviour for when you are dealing with build lower than Q.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                
         //your existing code that seems to work above Q
            } else {
                String docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
                File file = new File(docsDir, "fileName.jpg");
                if(file.exists());
                file.delete();
            }
Transparent answered 27/10, 2021 at 19:47 Comment(1)
It won't work as storage API changes from android 10 & above. It will work on android 10 if you decalre requestLegacyExternalStorage in Manifest. But for android 11, it's not possible.Scorper
A
0

Add android:requestLegacyExternalStorage="false" in your android manifest. That allows scoped storage.

For versions below android 10, set android:requestLegacyExternalStorage to false.

Alsace answered 3/11, 2021 at 12:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.