Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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);
    
        }
    }
}}
like image 431
Devang Shah Avatar asked Nov 06 '22 01:11

Devang Shah


1 Answers

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.

like image 152
Dev4Life Avatar answered Nov 14 '22 07:11

Dev4Life