Accessing external storage in Android API 29
On Android 10 Environment.getExternalStorageDirectory()
and Environment.getExternalStoragePublicDirectory()
will return storage paths but paths are not readable or writable.
For Android 10 you can continue to use paths provided by Environment.getExternalStorageDirectory() and Environment.getExternalStoragePublicDirectory() if you add
android:requestLegacyExternalStorage="true"
to application tag in manifest file. At runtime your app can callEnvironment.isExternalStorageLegacy()
to check if the request has been done.Another (not known) possibility (only for Android 10) is to add
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
to manifest file.
The user has to go to the advanced settings of the app and enable fromAdvanced settings Install unknown apps | Allow from this source
.
The nice thing with this is that the user can switch the access rights. You can make it easier for the user if you implement an intent forSettings.ACTION_APPLICATION_DETAILS_SETTINGS
where he can change the settings.
A funny thing is that Environment.isExternalStorageLegacy() returns true then too.Compiling for Android 11 both options do not work on an Android 11 device. (But they continue to work for Android 10 devices). The paths of Environment.getExternalStorageDirectory() and Environment.getExternalStoragePublicDirectory() are
usable again in read mode and very often in write mode too
. And this is great as one can simply list the contents of directories like Download or Pictures or DCIM/Camera again using the File class.
But adding<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
to manifest file and implementing an intent forSettings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
will give your app read/write access for all files even on removable micro sd card.
(Finally you can remove the google ban not being able to read/write your own micro sd card on your own Android device using your own app).Environment.isExternalStorageManager()
can be used to check if the permission is on/off.
As long as you do not try to upload your app to the play store you are fine.
Android SDK 30, write to the root of external storage
You can target SDK 30 and add MANAGE_EXTERNAL_STORAGE permission to the manifest:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
Do note it's a dangerous permission so you'll need to request it differently, like this:
if (!Environment.isExternalStorageManager()) {
requestManageAllPermission();
return;
}
private void requestManageAllPermission() {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(String.format("package:%s", getApplicationContext().getPackageName())));
startActivityForResult(intent, REQ_MANAGE_EXTERNAL);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivityForResult(intent, REQ_MANAGE_EXTERNAL);
}
}
And you need to handle the results in:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQ_MANAGE_EXTERNAL) {
if (!Environment.isExternalStorageManager())
finish();
}
}
REQ_MANAGE_EXTERNAL is a int constant, can be any number you want, in my case its 2296
WRITE_EXTERNAL_STORAGE when targeting Android 10
A workaround is to actually ignore the warning, as it is just informational and therefore harmless. By setting maxSdkVersion to 28 no need to worry anymore.
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
tools:ignore="ScopedStorage" />
Note that using the android:requestLegacyExternalStorage flag as stated in other answers is not a solution, is just a temporary patch which will no longer work at all in Android 11 (API 30), and future versions
UPDATE, to clarify the doubts and confusions shown by some developers in the comments:
If using the requestLegacyExternalStorage flag in Android 10 (API 29) then request the WRITE_EXTERNAL_STORAGE permission as usual.
The flag requestLegacyExternalStorage does nothing in Android 11 (API 30), it is completely ignored, and there is not workaround for it.
WRITE_EXTERNAL_STORAGE does not give any privileges in Android 11 (API 30), it does nothing at all, therefore in API 11 you need to set the maxSdkVersion to 29.
If in Android 10 (API 29) you are also not using requestLegacyExternalStorage then set maxSdkVersion to 28 instead of 29.
Starting in Android 11 (API 30), the older File API can again be used but "only" when accessing the public "shared storage" folders (DCIM, Music, etc.), or your app "private" directory. For other locations the DocumentFile API is required.
Consider that the File API is now much slower in Android 11 (API 30), because has been refactored becoming essentially a wrapper. This is to enforce its usage just to the allowed locations. So, is no longer a fast system file API, is just a wrapper that internally delegates the work to the MediaStore. When using the File API in Android 11 or above you should consider the performance penalty hit, as according to the Android team it will be 2 to 3 times slower than if accessing directly the MediaStore.
How to get Access to External Storage in Android 10 (Android Q)?
If you target Android 10 or higher, set the value of requestLegacyExternalStorage to true in your app's manifest file:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting
Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
Access files
To load media files, call one of the following methods from ContentResolver:
Uri contentUri = ContentUris.withAppendedId(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
cursor.getLong(Integer.parseInt(BaseColumns._ID)));
String fileOpenMode = "r";
ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(uri, fileOpenMode);
if (parcelFd != null) {
int fd = parcelFd.detachFd();
// Pass the integer value "fd" into your native code. Remember to call
// close(2) on the file descriptor when you're done using it.
}
On devices running Android 10 (API level 29) and higher, your app can get exclusive access to a media file as it's written to disk by using the IS_PENDING flag.
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "IMG1024.JPG");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.IS_PENDING, 1);
ContentResolver resolver = context.getContentResolver();
Uri collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
Uri item = resolver.insert(collection, values);
try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(item, "w", null)) {
// Write data into the pending image.
} catch (IOException e) {
e.printStackTrace();
}
// Now that we're finished, release the "pending" status, and allow other apps
// to view the image.
values.clear();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
resolver.update(item, values, null, null);
Related Topics
Can Not Find a View With Findviewbyid()
How to Find Out Which Keystore Was Used to Sign an App
View Contents of Database File in Android Studio
How to Read Mms Data in Android
Httpclient Won't Import in Android Studio
What's the Best Way to Limit Text Length of Edittext in Android
How to Find Serial Number of Android Device
How to Send a File in Android from a Mobile Device to Server Using Http
How to Use Arrayadapter≪Myclass≫
How to Get Uri from an Asset File
How to Check Grants Permissions At Run-Time
How to Install an Apk File in the Android Emulator
Need to Handle Uncaught Exception and Send Log File
How to Add a Button Dynamically in Android
Viewpager.Setoffscreenpagelimit(0) Doesn't Work as Expected
How to Change Spinner Text Size and Text Color
How to Parse the Androidmanifest.Xml File Inside an .Apk Package
How to Dismiss the Dialog with Click on Outside of the Dialog