Scoped Storage in Android — Writing & Deleting Media Files

Vishrut Goyani
4 min readOct 24, 2021

--

On September 3, 2019, Google launched android 10 and it has introduced a major change in the file access system, or rather call it file handling on Android — Scoped Storage.

There are lots of articles about what is scoped storage and how it changes the way developers access the files with traditional methods. So without getting into many details, let’s go straight to the solution of the problem what the scoped storage created for writing and deleting media files.

Well, It’s not a kind of problem first, It’s just some unknown methods and ways of creating, writing, and deleting media files on devices with android 10 and above that some developers or say beginners don’t know. So it’s likely an alternative method as well as some new API implementations.

Let’s get into it.

Writing files with the Mediastore API

When it comes to writing files on Android 10, you might have thought about using traditional methods with the requestLegacyExternalStorage flag in your app’s Manifest. But it’s not the case when you are developing apps for android 11 (or with the targetSdkVersion 30) as google just stated that the flag will be removed in the future and it’s just a temporary solution.

There is also a point to be noted that the android.permission.WRITE_EXTERNAL_STORAGE won’t work even if you’ve declared the requestLegacyExternalStorage flag in your manifest as apps can’t write directly to other app directories as well as modify the content of other app directories as per the scoped storage changes on Android 11.

The app can still write files on its relative directories like Pictures for images, DCIM for documents and other media files, Movies for videos. There are other ways to go around that but we will talk about writing an image in DCIM for now as it’s probably one of the most asked questions out there. Here is the image to understand it better.

If you want to write images in DCIM directory, then you don’t have to create the folder by yourself like using the traditional file.mkdirs() or other methods like that. You just need to pass the file path and bitmap to the mediastore & it will create the directory automatically by itself if it doesn’t exist!

Here is how you do it:

public static Uri saveBitmap(Context context,
Bitmap bitmap,
Bitmap.CompressFormat compressFormat,
String mime_type,
String display_name,
String path) {
OutputStream openOutputStream;
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, display_name);
contentValues.put(MediaStore.Images.ImageColumns.MIME_TYPE, mime_type);
contentValues.put(MediaStore.Images.ImageColumns.RELATIVE_PATH, path);
ContentResolver contentResolver = context.getContentResolver();
Uri insert = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
if (insert != null) {
try {
openOutputStream = contentResolver.openOutputStream(insert);
if (openOutputStream == null) {
throw new IOException("Failed to open output stream.");
} else if (bitmap.compress(compressFormat, 90, openOutputStream)) {
openOutputStream.close();
return insert;
} else {
throw new IOException("Failed to save bitmap.");
}
} catch (IOException unused) {
contentResolver.delete(insert, null, null);
return insert;
} catch (Throwable th) {
th.addSuppressed(th);
}
}
return null;
}

You can change some attributes like compress format, mimetype, display name as per your needs here (Same for other media as well). The implementation of this method looks like this:

String path = pathtoyourDirinDCIM; //It's the path where you want to save your image or mediaUri uri = DisplayUtils.saveBitmap(context,
bitmap,
Bitmap.CompressFormat.JPEG,
"image/jpeg",
"IMG100.jpg",
path);
Log.d(TAG, "saved image uri: " + uri);

That’s it! You’ve successfully saved your image to your desired location without sweating a bit!

Now let’s take a look at how to delete images on devices with android 10 & above.

Deleting Images using new delete APIs

There are several ways when it comes to deleting images on android 10 and above versions - Deleting images using DocumentsProvider API, Using new Mediastore APIs, and some other ways.

We will focus on deleting images using the new mediastore delete API for android 10 and android 11 because there are many things to know before using DocumentsProvider.

I am giving this example with the latest ActivityResultLauncher API which is the replacement for the deprecated onActivityResult API.

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); //Use of ActivityResultLauncher
}
}
}

ActivityResultLauncher declaration:

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();
}
}
}
);

For android 11, the ActivityResultLauncher declaration stays the same, there are changes in the delete method only.

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);
}

And that’s it! You are good to go to delete the images on android 10 and above with these methods and new APIs.

NOTE: We are not using the MANAGE_EXTERNAL_STORAGE permission here because it’s only usable for file-operations-related apps like File managers & it’s tough to get the approval of your app while publishing it on the play store with that permission included in your manifest.

So this was the best way to write and delete images on android 10 and above, say in scoped storage. You can try it with different types of media like videos, documents, PDF, and others.

Thank you for reading this article! Any questions & suggestions are greatly appreciated.

Have a great productive day!

Vishrut Goyani
Android Developer

--

--