I have an Android 7.0 test device and my APK targets = \"targetSdkVersion 22\", with:
If you target SDK 29 and you still get a "permission denied" error after successfully requesting the WRITE_EXTERNAL_STORAGE permission you should add
android:requestLegacyExternalStorage="true"
to the application definition in AndroidManifest.xml.
<manifest ... >
<!-- This attribute is "false" by default on apps targeting
Android 10 or higher. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
see https://developer.android.com/training/data-storage/compatibility
If you're running your app on API level 23 or greater you have to request permission at runtime.
Request permission:
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, WRITE_REQUEST_CODE);
Then handle the result:
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case WRITE_REQUEST_CODE:
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
//Granted.
}
else{
//Denied.
}
break;
}
}
For more information visit Requesting Permissions at Run Time - Android Doc
UPDATE: See Volker Voecking's answer for a better solution
EDIT: This is simply a workaround for those who don't have time to look for a solution. *
If you get permission denied error even when the permissions are granted and you already implemented permission checks,
Change targetSdkVersion and compilesdkversion from 29 to 28 or any other lower level.
Related to the answer "make sure you're not targetting api level 29...", I found this.
https://developer.android.com/reference/android/os/Environment#getExternalStorageDirectory
。。。。。。。。。。
"getExternalStoragePublicDirectory"
This method was deprecated in API level 29.
To improve user privacy, direct access to shared/external storage devices is deprecated. When an app targets Build.VERSION_CODES.Q, the path returned from this method is no longer directly accessible to apps. Apps can continue to access content stored on shared/external storage by migrating to alternatives such as Context#getExternalFilesDir(String), MediaStore, or Intent#ACTION_OPEN_DOCUMENT.
。。。。。。。。。。
so, in that case you have to replace "getExternalStoragePublicDirectory" with those other methods.
Here is what I did in a similar situation:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
void checkForPermissions()
{
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
// This part I didn't implement,because for my case it isn't needed
Log.i(TAG,"Unexpected flow");
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE);
// MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
// Permission is already granted, call the function that does what you need
onFileWritePermissionGranted();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay!
// call the function that does what you need
onFileWritePermissionGranted();
} else {
Log.e(TAG, "Write permissions has to be granted tp ATMS, otherwise it cannot operate properly.\n Exiting the program...\n");
}
return;
}
// other 'case' lines to check for other
// permissions this app might request.
}
}
void onFileWritePermissionGranted()
{
// Write to some file
}
Android N introduces a new model of permissions which only asks for permissions when the app really needs it rather than during installation like it previously did.
Use the following code to ask for permissions
Note - Also add the required permissions in the Manifest file
If you aren't asking permissions from Main Activity pass the reference to the context/activity
The following example shows example for asking permissions to Write to external storage, multiple permisisons can be requiested at the same time (Not recommended by google).
public class MainActivity extends AppCompatActivity {
/**
* Variables for requiesting permissions, API 25+
*/
private int requestCode;
private int grantResults[];
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {if(ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED ){
//if you dont have required permissions ask for it (only required for API 23+)
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},requestCode);
onRequestPermissionsResult(requestCode,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},grantResults);
}
@Override // android recommended class to handle permissions
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case 1: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("permission","granted");
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.uujm
Toast.makeText(MainActivity.this, "Permission denied to read your External storage", Toast.LENGTH_SHORT).show();
//app cannot function without this permission for now so close it...
onDestroy();
}
return;
}
// other 'case' line to check fosr other
// permissions this app might request
}
}