Minimum API 24 (Android 7.0)
Note:
- Beginning with Android 4.4 (API level 19) it's no longer necessary for your app to request the WRITE_EXTERNAL_STORAGE permission to write to its own application-specific directories on external storage, which are provided by getExternalFilesDir(). However, the permission is required for API level 18 and lower. Example:
File extDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); - If the permission is a runtime permission or special permission, and if your app is installed on a device that runs Android 6.0 (API level 23) or higher, you must request the runtime permission or special permission yourself.
- On devices that run Android 4.4 (API level 19) and higher, your app can interact with a documents provider, including external storage volumes and cloud-based storage, using the Storage Access Framework. This framework allows users to interact with a system picker to choose a documents provider and select specific documents and other files for your app to create, open, or modify.
- To support media file access on devices that run Android 9 (API level 28) or lower, declare the READ_EXTERNAL_STORAGE permission and set the maxSdkVersion to 28. See reference 1.
- If your app targets Android 10 (API level 29) or lower, you can temporarily opt out of scoped storage in your production app. If you target Android 10, however, you need to set the value of requestLegacyExternalStorage to true in your app's manifest file.
AndroidManifest.xml
...
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
/>
...
<!-- This attribute is "false" by default on apps targeting Android 10. -->
<!-- android:requestLegacyExternalStorage="true" -->
<application
android:requestLegacyExternalStorage="true"
...
MainActivity.java
...
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
// android:maxSdkVersion="28" = Build.VERSION_CODES.P
if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
checkMyPermission();
}
... int MY_CODE_REQUEST = 123;
/*
* 1. check permission
*/
private void checkMyPermission() {
// Build.VERSION.SDK_INT >= 23
// https://riptutorial.com/android/example/23932/multiple-runtime-permissions-from-same-permission-groups
ArrayList<String> sPermissionRequest = new ArrayList<>();
int myRead = ContextCompat.checkSelfPermission(getApplicationContext()
, READ_EXTERNAL_STORAGE);
int myWrite = ContextCompat.checkSelfPermission(getApplicationContext()
, WRITE_EXTERNAL_STORAGE);
if (myRead != PackageManager.PERMISSION_GRANTED) {
sPermissionRequest.add(getString(R.string.s_permission_read_storage));
}
if (myWrite != PackageManager.PERMISSION_GRANTED) {
sPermissionRequest.add(getString(R.string.s_permission_write_storage));
}
if (sPermissionRequest.size()>0) {
// NOT Granted
String[] sPermissions = new String[sPermissionRequest.size()];
for (int i=0;i<sPermissionRequest.size();i++) {
sPermissions[i]=sPermissionRequest.get(i);
}
//sLog.e("dedetok", "sPermissionRequest.size()>0 size "+sPermissions.length); // debug
requestPermissions(sPermissions, MY_CODE_REQUEST);
}
}
/*
* 2. receive permission status
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode==MY_CODE_REQUEST) {
if (permissions.length>0) {
ArrayList<String> myPermission = new ArrayList<>();
for (int i=0;i<permissions.length;i++) {
//Log.e("dedetok", permissions[i]+" "+grantResults[i]); // debug
if (permissions[i].equals(getString(R.string.s_permission_read_storage)) &&
grantResults[i]!=PackageManager.PERMISSION_GRANTED) {
myPermission.add(READ_EXTERNAL_STORAGE);
}
if (permissions[i].equals(getString(R.string.s_permission_write_storage)) &&
grantResults[i]!=PackageManager.PERMISSION_GRANTED) {
myPermission.add(WRITE_EXTERNAL_STORAGE);
}
}
// Not granted, Request permissions on runtime
String[] sRequestPermission = new String[myPermission.size()];
for (int i=0;i<myPermission.size();i++) {
sRequestPermission[i] = myPermission.get(i);
}
//Log.e("dedetok", "requestCode==MY_CODE_REQUEST "+sRequestPermission.length); // debug
requestPermissionLauncher.launch(sRequestPermission);
}
}
}
/*
* 3. callback user permission
* Register the permissions callback, which handles the user's response to the
* system permissions dialog. Save the return value, an instance of
* ActivityResultLauncher, as an instance variable.
*/
private final ActivityResultLauncher<String[] > requestPermissionLauncher =
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions()
, new ActivityResultCallback() {
@Override
public void onActivityResult(Object objMap) {
//Log.e("dedetok", "onActivityResult "+o.toString()); // debug
if (objMap instanceof Map) {
StringBuilder mySB = new StringBuilder();
Map<String,Boolean> myMapPermission = (Map<String,Boolean>) objMap;
myMapPermission.forEach((permissionKey, permissionBoolean)->{
//Log.e("dedetok", permissionKey+" "+READ_EXTERNAL_STORAGE+" "+permissionBoolean); // debug
if (permissionKey.equals(READ_EXTERNAL_STORAGE) && !permissionBoolean) {
mySB.append(READ_EXTERNAL_STORAGE+" ");
}
if (permissionKey.equals(WRITE_EXTERNAL_STORAGE) && !permissionBoolean) {
mySB.append(WRITE_EXTERNAL_STORAGE+" ");
}
});
//Log.e("dedetok", "mySB "+mySB.toString()); // debug
if (mySB.length()>0) {
updateStatus(mySB.toString());
}
}
}
});
References:
- https://developer.android.com/training/data-storage/shared/documents-files
- https://developer.android.com/training/permissions/declaring
- https://developer.android.com/guide/topics/manifest/uses-permission-element
- https://stackoverflow.com/questions/3093365/how-can-i-check-the-system-version-of-android
- https://developer.android.com/training/data-storage/use-cases