I've been trying to get my app to be able to delete an audio file. However, after trying many possible solutions, I couldn't really find one that works.
Here is my solution so far:
public static void deleteFiles(List<Track> tracks, Context context,
final MutableLiveData<IntentSender> deletionIntentSenderLD){
final Uri AUDIO_URI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
for(Track t : tracks){
try {
context.getContentResolver().delete(ContentUris
.withAppendedId(AUDIO_URI, t.getUriId()), null, null);
}catch (SecurityException securityException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (securityException instanceof RecoverableSecurityException) {
deletionIntentSenderLD
.postValue(((RecoverableSecurityException) securityException)
.getUserAction().getActionIntent().getIntentSender());
} else
throw securityException;
} else
throw securityException;
}
}
}
When the try block fails a SecurityException is catch then the IntentSender is passed to the live data that is observed in a fragment:
audioViewModel.getDeletionIntentSenderLD().observe(getViewLifecycleOwner(),
intentSender -> {
try {
startIntentSenderForResult(intentSender, DELETE_PERMISSION_REQUEST,
null, 0 ,0, 0,
null);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
});
I've tried implementing the onRequestPermissionResult() method but that doesn't do anything. I've also tried deleting the files using File file = new File(), however, due to the changes made to Android 10, I didn't expect it to work.
So after many Google searches, I've come to the conclusion that the best approach (to my knowledge) is to simply turn off scoped storage for Android Q (10).
Here, I'll provide two solutions. The first is the one where I turn it off and the second is the one where scope storage is still enable. However, a thing you should note is that the second solution is a little buggy, at times it actually does delete both the actual media file and updates the Media Store, but most times it simply deletes from the Media Store only. Obviously, this isn't a very good solution as on reboot your application would then load those files back in because the Media Store would scan for them.
Solution 1 - Turn off Scoped Storage
For this solution you can still target Android 11. All you have to do is go to the build.gradle file at the Module Level and set the compileSdkVersion and targetSdkVersion to 30.
After that, you go into the AndroidManifest.xml and have the uses-permission and application tag set up like this:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:ignore="ScopedStorage"/>
<application
android:requestLegacyExternalStorage="true"
...
After having done that, you could use the Content Resolver to delete the media file (and update the Media Store) and you do not have to worry about catching a security exception like its said in the Android docs. Your implementation for Android 11s delete operation should not be affected.
Solution-ish 2 - Turn on Scoped Storage
Firstly, in your manifest ensure that the WRITE_EXTERNAL_STORAGE permissions maxSdkVersion is set to 28. Also ensure that requestLegacyExternalStorage is set to false (don't think this is required). Then simply copy the code in my original post. You do not require a Live Data if you are doing the delete operation from your activity/fragment. But you should note that startIntentSenderForResult() requires an activity.
But as I mentioned before, I did experience some bugs with this. The most frustrating thing about this solution though is that it does not delete the actual file but instead deletes the entry from the Media Store. Maybe this has something to do with the fact that #blackapps mentioned, which is that you cannot bulk delete and I might have implemented it slightly wrong. Nevertheless, this is horrible for user experience if bulk deletion is impossible in Android 10.
The tutorials I followed for this are:
https://developer.android.com/training/data-storage/shared/media#remove-item
https://www.raywenderlich.com/9577211-scoped-storage-in-android-10-getting-started#toc-anchor-007
https://www.solutionanalysts.com/blog/scoped-storage-in-android-10/
Side Note - Delete on Android 11
To delete on Android 11 you just need to call createDeleteRequest() which should return a PendingIntent. From this PendingIntent you could get the IntentSender by using getIntentSender. Pass this intent sender to the activity/fragment then call startIntentSenderForResult() in your activity/fragment. This pops up a dialog to the user asking them if the application can delete a file. If the user gives permission the system goes ahead and deletes the file and updates the Media Store.
Side Side Note - Scoped Storage, Android 10 and Future
From everything I've seen, it seems to suggest that scoped storage is only enforced in Android 11 but I'm not entirely sure if the legacy option would still be available in Android 10 indefinitely. But I would have to do more research on this...
I am getting
open failed: EACCES (Permission denied)
on the line OutputStream myOutput = new FileOutputStream(outFileName);
I checked the root, and I tried android.permission.WRITE_EXTERNAL_STORAGE.
How can I fix this problem?
try {
InputStream myInput;
myInput = getAssets().open("XXX.db");
// Path to the just created empty db
String outFileName = "/data/data/XX/databases/"
+ "XXX.db";
// Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
// Transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
// Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
buffer = null;
outFileName = null;
}
catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Google has a new feature on Android Q: filtered view for external storage. A quick fix for that is to add this code in the AndroidManifest.xml file:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android Q. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
You can read more about it here: https://developer.android.com/training/data-storage/use-cases
Edit: I am starting to get downvotes because this answer is out of date for Android 11. So whoever sees this answer please go to the link above and read the instructions.
For API 23+ you need to request the read/write permissions even if they are already in your manifest.
// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
/**
* Checks if the app has permission to write to device storage
*
* If the app does not has permission then the user will be prompted to grant permissions
*
* #param activity
*/
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
For official documentation about requesting permissions for API 23+, check https://developer.android.com/training/permissions/requesting.html
I had the same problem... The <uses-permission was in the wrong place. This is right:
<manifest>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
<application>
...
<activity>
...
</activity>
</application>
</manifest>
The uses-permission tag needs to be outside the application tag.
Add android:requestLegacyExternalStorage="true" to the Android Manifest
It's worked with Android 10 (Q) at SDK 29+
or After migrating Android X.
<application
android:name=".MyApplication"
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon=""
android:label=""
android:largeHeap="true"
android:supportsRtl=""
android:theme=""
android:requestLegacyExternalStorage="true">
I have observed this once when running the application inside the emulator. In the emulator settings, you need to specify the size of external storage ("SD Card") properly. By default, the "external storage" field is empty, and that probably means there is no such device and EACCES is thrown even if permissions are granted in the manifest.
In addition to all the answers, make sure you're not using your phone as a USB storage.
I was having the same problem on HTC Sensation on USB storage mode enabled. I can still debug/run the app, but I can't save to external storage.
My issue was with "TargetApi(23)" which is needed if your minSdkVersion is bellow 23.
So, I have request permission with the following snippet
protected boolean shouldAskPermissions() {
return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
}
#TargetApi(23)
protected void askPermissions() {
String[] permissions = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE"
};
int requestCode = 200;
requestPermissions(permissions, requestCode);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
if (shouldAskPermissions()) {
askPermissions();
}
}
Be aware that the solution:
<application ...
android:requestLegacyExternalStorage="true" ... >
Is temporary, sooner or later your app should be migrated to use Scoped Storage.
In Android 10, you can use the suggested solution to bypass the system restrictions, but in Android 11 (R) it is mandatory to use scoped storage, and your app might break if you kept using the old logic!
This video might be a good help.
Android 10 (API 29) introduces Scoped Storage. Changing your manifest to request legacy storage is not a long-term solution.
I fixed the issue when I replaced my previous instances of Environment.getExternalStorageDirectory() (which is deprecated with API 29) with context.getExternalFilesDir(null).
Note that context.getExternalFilesDir(type) can return null if the storage location isn't available, so be sure to check that whenever you're checking if you have external permissions.
Read more here.
I'm experiencing the same. What I found is that if you go to Settings -> Application Manager -> Your App -> Permissions -> Enable Storage, it solves the issue.
It turned out, it was a stupid mistake since I had my phone still connected to the desktop PC and didn't realize this.
So I had to turn off the USB connection and everything worked fine.
I had the same problem on Samsung Galaxy Note 3, running CM 12.1. The issue for me was that i had
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18"/>
and had to use it to take and store user photos. When I tried to load those same photos in ImageLoader i got the (Permission denied) error. The solution was to explicitly add
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
since the above permission only limits the write permission up to API version 18, and with it the read permission.
In addition to all answers, if the clients are using Android 6.0, Android added new permission model for (Marshmallow).
Trick: If you are targeting version 22 or below, your application will request all permissions at install time just as it would on any device running an OS below Marshmallow. If you are trying on the emulator then from android 6.0 onwards you need to explicitly go the settings->apps-> YOURAPP -> permissions and change the permission if you have given any.
Strangely after putting a slash "/" before my newFile my problem was solved. I changed this:
File myFile= new File(Environment.getExternalStorageDirectory() + "newFile");
to this:
File myFile= new File(Environment.getExternalStorageDirectory() + "/newFile");
UPDATE:
as mentioned in the comments, the right way to do this is:
File myFile= new File(Environment.getExternalStorageDirectory(), "newFile");
I had the same problem and none of suggestions helped. But I found an interesting reason for that, on a physical device, Galaxy Tab.
When USB storage is on, external storage read and write permissions don't have any effect.
Just turn off USB storage, and with the correct permissions, you'll have the problem solved.
I would expect everything below /data to belong to "internal storage". You should, however, be able to write to /sdcard.
Change a permission property in your /system/etc/permission/platform.xml
and group need to mentioned as like below.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
<group android:gid="sdcard_rw" />
<group android:gid="media_rw" />
</uses-permission>
I had the same error when was trying to write an image in DCIM/camera folder on Galaxy S5 (android 6.0.1) and I figured out that only this folder is restricted. I simply could write into DCIM/any folder but not in camera.
This should be brand based restriction/customization.
Maybe the answer is this:
on the API >= 23 devices, if you install app (the app is not system app), you should check the storage permission in "Setting - applications", there is permission list for every app, you should check it on! try
To store a file in a directory which is foreign to the app's directory is restricted above API 29+. So to generate a new file or to create a new file use your application directory like this :-
So the correct approach is :-
val file = File(appContext.applicationInfo.dataDir + File.separator + "anyRandomFileName/")
You can write any data into this generated file !
The above file is accessible and would not throw any exception because it resides in your own developed app's directory.
The other option is android:requestLegacyExternalStorage="true" in manifest application tag as suggested by Uriel but its not a permanent solution !
When your application belongs to the system application, it can't access the SD card.
keep in mind that even if you set all the correct permissions in the manifest:
The only place 3rd party apps are allowed to write on your external card are "their own directories"
(i.e. /sdcard/Android/data/)
trying to write to anywhere else: you will get exception:
EACCES (Permission denied)
Environment.getExternalStoragePublicDirectory();
When using this deprecated method from Android 29 onwards you will receive the same error:
java.io.FileNotFoundException: open failed: EACCES (Permission denied)
Resolution here:
getExternalStoragePublicDirectory deprecated in Android Q
In my case I was using a file picker library which returned the path to external storage but it started from /root/. And even with the WRITE_EXTERNAL_STORAGE permission granted at runtime I still got error EACCES (Permission denied).
So use Environment.getExternalStorageDirectory() to get the correct path to external storage.
Example:
Cannot write: /root/storage/emulated/0/newfile.txt
Can write: /storage/emulated/0/newfile.txt
boolean externalStorageWritable = isExternalStorageWritable();
File file = new File(filePath);
boolean canWrite = file.canWrite();
boolean isFile = file.isFile();
long usableSpace = file.getUsableSpace();
Log.d(TAG, "externalStorageWritable: " + externalStorageWritable);
Log.d(TAG, "filePath: " + filePath);
Log.d(TAG, "canWrite: " + canWrite);
Log.d(TAG, "isFile: " + isFile);
Log.d(TAG, "usableSpace: " + usableSpace);
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
Output 1:
externalStorageWritable: true
filePath: /root/storage/emulated/0/newfile.txt
isFile: false
usableSpace: 0
Output 2:
externalStorageWritable: true
filePath: /storage/emulated/0/newfile.txt
isFile: true
usableSpace: 1331007488
I am creating a folder under /data/ in my init.rc (mucking around with the aosp on Nexus 7) and had exactly this problem.
It turned out that giving the folder rw (666) permission was not sufficient and it had to be rwx (777) then it all worked!
The post 6.0 enforcement of storage permissions can be bypassed if you have a rooted device via these adb commands:
root#msm8996:/ # getenforce
getenforce
Enforcing
root#msm8996:/ # setenforce 0
setenforce 0
root#msm8996:/ # getenforce
getenforce
Permissive
i faced the same error on xiaomi devices (android 10 ). The following code fixed my problem.
Libraries: Dexter(https://github.com/Karumi/Dexter) and Image picker(https://github.com/Dhaval2404/ImagePicker)
Add manifest ( android:requestLegacyExternalStorage="true")
public void showPickImageSheet(AddImageModel model) {
BottomSheetHelper.showPickImageSheet(this, new BottomSheetHelper.PickImageDialogListener() {
#Override
public void onChooseFromGalleryClicked(Dialog dialog) {
selectedImagePickerPosition = model.getPosition();
Dexter.withContext(OrderReviewActivity.this) .withPermissions(Manifest.permission.READ_EXTERNAL_STORAGE)
.withListener(new MultiplePermissionsListener() {
#Override
public void onPermissionsChecked(MultiplePermissionsReport report) {
if (report.areAllPermissionsGranted()) {
ImagePicker.with(OrderReviewActivity.this)
.galleryOnly()
.compress(512)
.maxResultSize(852,480)
.start();
}
}
#Override
public void onPermissionRationaleShouldBeShown(List<PermissionRequest> list, PermissionToken permissionToken) {
permissionToken.continuePermissionRequest();
}
}).check();
dialog.dismiss();
}
#Override
public void onTakePhotoClicked(Dialog dialog) {
selectedImagePickerPosition = model.getPosition();
ImagePicker.with(OrderReviewActivity.this)
.cameraOnly()
.compress(512)
.maxResultSize(852,480)
.start();
dialog.dismiss();
}
#Override
public void onCancelButtonClicked(Dialog dialog) {
dialog.dismiss();
}
});
}
In my case the error was appearing on the line
target.createNewFile();
since I could not create a new file on the sd card,so I had to use the DocumentFile approach.
documentFile.createFile(mime, target.getName());
For the above question the problem may be solved with this approach,
fos=context.getContentResolver().openOutputStream(documentFile.getUri());
See this thread too,
How to use the new SD card access API presented for Android 5.0 (Lollipop)?
I Use the below process to handle the case with android 11 and targetapi30
As pre-created file dir as per scoped storage in my case in root dir files//<Image/Video... as per requirement>
Copy picked file and copy the file in cache directory at the time of picking from my external storage
Then at a time to upload ( on my send/upload button click) copy the file from cache dir to my scoped storage dir and then do my upload process
use this solution due to at time upload app in play store it generates warning for MANAGE_EXTERNAL_STORAGE permission and sometimes rejected from play store in my case.
Also as we used target API 30 so we can't share or forward file from our internal storage to app
2022 Kotlin way to ask permission:
private val writeStoragePermissionResult =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->}
private fun askForStoragePermission(): Boolean =
if (hasPermissions(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
) {
true
} else {
writeStoragePermissionResult.launch(
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
)
false
}
fun hasPermissions(context: Context, vararg permissions: String): Boolean = permissions.all {
ActivityCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
I am new to Parse. I wanted to create a test object, I have used the following code for initializing it:
Parse.initialize(this, "W10AcPJEV80uy3ZXtRfLi06VNzvy680rtPm9N", "yIkBHQWdWdZljTpJENeDkfCtsvrCTGwgb5oYW");
ParseObject testObject = new ParseObject("TestObject");
testObject.put("foo", "bar");
testObject.saveInBackground();
But when I check the Data browser in the Parse dashboard, I see nothing. At the same time, I can see a request was made from my app. I have added the necessary permissions on the manifest as well.
Check this. Hopefully It will help u a lot.
Parse Setup:
In the onCreate method of your Activity, add the following (remember to replace APP_ID and CLIENT_ID with the keys you got from Parse earlier):
Parse.initialize(this, "APP_ID", "CLIENT_ID");
ParseAnalytics.trackAppOpened(getIntent());
In addition to that initializer code, we’ll need to add get some permissions for our app. Add the following two permissions to your
AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
initialization code:
ParseObject.registerSubclass(Task.class)
creating object user as:
ParseUser user = new ParseUser();
user.setUsername(mUsernameField.getText().toString());
user.setPassword(mPasswordField.getText().toString());
user.signUpInBackground(new SignUpCallback(){
.....
....
}
I'm building an app for universal access, my app works in fullscreen with a custom dialer. So if users press home or back during a phone call I need to give them the opportunity to return the call somehow. (In fact I'm thinking about reopening the call in progress automatically if they leave).
I know how to start a call with a number but I don't know how to open the incall screen during a call, I tried doing an Intent.ACTION_CALL without a number but it initiates a second phone call on top of the other:
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"));
startActivity(callIntent);
I think this should be doen with an intent or by simply bringing it to front. But I don't know how to do it. How can I reopen a call in progress programatically?
if it's still relevant or for other people interested in this:
ACTION_CALL_BUTTON do exactly that.
if the call is in progress, it would bring it to the front, and if it's not, it would
bring the call log.
http://developer.android.com/reference/android/content/Intent.html#ACTION_CALL_BUTTON
I found a different solution that seems to work well. I many combinations of Intents and also tried the ACTION_CALL_BUTTON as suggested above but in 4.3 it opens a new dialer and does not take you to the current call in progress. Not sure how this behaves in older sdks. After much research and tries I found this to work well, although it could probably use some optimizing...
telephonyManager = (TelephonyManager) getSystemService( Context.TELEPHONY_SERVICE );
if( telephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK )
{
ActivityManager m = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = m.getRunningTasks( 10 );
for( int i = 0; i < tasks.size(); ++i )
{
RunningTaskInfo task = tasks.get( i );
ComponentName component = task.baseActivity;
String packageName = component.getPackageName();
if( packageName.contains( "com.android.phone" ) )
{
m.moveTaskToFront( task.id, 0 );
return;
}
}
}
You will also need to add the permissions to your manifest file:
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />