I am having trouble gaining write access to device SD card root directory. I can write to device internal storage and app package folder in an SD card without problems. I found that gaining access to an SD card is possible using Storage Access Framework, so I looked at other apps to see how they gain access to an SD card, and found Total Commander with its neat permission dialog.
How to show such a dialog because I cannot find any examples? My goal is to store large files on the SD card that stay after uninstalling the app.
Similar questions:
Android (Write on external SD-Card): java.io.IOException: Permission denied
Android M write to SD Card - Permission Denied
Android SD Card Write Permission using SAF (Storage Access Framework)
requestPermissions(thisActivity, new String[]{Manifest. permission. READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); Android system creates a popup dialog to request permission.
Starting in Android 11, whenever your app requests a permission related to location, microphone, or camera, the user-facing permissions dialog contains an option called Only this time. If the user selects this option in the dialog, your app is granted a temporary one-time permission.
I found open source file manager. Looked at the code and finally found the solution.
Only works on Android N (7.0.0) (api 24) and above.
First get root path of SD card and show user permission dialog.
public void takeCardUriPermission(String sdCardRootPath) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
File sdCard = new File(sdCardRootPath);
StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
StorageVolume storageVolume = storageManager.getStorageVolume(sdCard);
Intent intent = storageVolume.createAccessIntent(null);
try {
startActivityForResult(intent, 4010);
} catch (ActivityNotFoundException e) {
}
}
}
When user accepts the request, onActivityResult() gets triggered and we can save uri from intent data
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 4010) {
Uri uri = data.getData();
grantUriPermission(getPackageName(), uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uri, takeFlags);
}
}
Now we can retrieve uri of SD card root and use it with Storage Access Framework
public Uri getUri() {
List<UriPermission> persistedUriPermissions = getContentResolver().getPersistedUriPermissions();
if (persistedUriPermissions.size() > 0) {
UriPermission uriPermission = persistedUriPermissions.get(0);
return uriPermission.getUri();
}
return null;
}
Unfortunately, using createAccessIntent is now deprecated in Android Q, and the documentation states that if you use it in Android Q, it will immediately return RESULT_CANCELED.
They recommend you use an intent with ACTION_OPEN_DOCUMENT_TREE.You can specify the directory it opens initially (which is only supported on Android O and above), making it easier for the user, but this still is not a very user friendly approach.
I'm wondering if there are any other user friendly solutions that anyone knows of? I don't understand why Android removed createAccessIntent, as it seems to be the most user friendly way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With