In case of making directory in external storage in android app I use this code:
File mDirectory = new File(Environment.getExternalStorageDirectory() + "/myDir");
if (!mDirectory.exists()) {
mDirectory.mkdirs();
if (!mDirectory.mkdirs()) {
Log.e("App", "failed to create directory");
}
}
but the directory didn't created and all the time the error message shown up in logcat:
App: failed to create directory
I also use both mkdir() and mkdirs(), but the result is same. where is the mistake?
updated:
AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
What Android Version are you running this app on ? This quote from the Android Developer Website says
If the device is running Android 6.0 (API level 23) or higher, and the app's targetSdkVersion is 23 or higher, the app requests permissions from the user at run-time. The user can revoke the permissions at any time, so the app needs to check whether it has the permissions every time it runs.
If the device is running Android 5.1 (API level 22) or lower, or the app's targetSdkVersion is 22 or lower, the system asks the user to grant the permissions when the user installs the app.
Now there is a notion of Dangerous permissions. That are permissions the user needs to grant at runtime and WRITE_EXTERNAL_STORAGE is one of those
This tutorial on the Android Dev website will teach you how to request permission at runtime
You are calling mkdirs twice. The first call creates the directory. The second call returns false, because the directory already exists.
Related
I need to do permission request change for my Gallery/Photo permission because since Android 13 (SDK 33) you cant request android.permission.READ_EXTERNAL_STORAGE to allow gallery browsing in case of photo upload. You need to use android:name="android.permission.READ_MEDIA_IMAGES" instead. Question is if I can simply put it like this to manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
If it will affect older SDKs because this permission is new one added in SDK 33.
Or if I have to somehow if this in Manifest so this permission gonna be listed only for SDK >= 33
I tried this here but it seems like incorrect command (warning that its not allowed there)
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission-sdk-33 android:name="android.permission.READ_MEDIA_IMAGES"/>
android.permission.READ_MEDIA_IMAGES is included in SDK >= 33, so older SKDs does not have this definition, hence it cannot be listed. I checked in the app settings of a device older than 33, and it is not listed, only android.permission.READ_EXTERNAL_STORAGE is listed. Then you need to programmatically ask for the correct permission depending on the SDK version of the device.
I would like to make a file manager app targeting API level 31 that requires access to all files on the device. To do this, I have followed the guidance here: https://developer.android.com/training/data-storage/manage-all-files.
I have added the MANAGE_EXTERNAL_STORAGE permission and allowed it in the device settings. Here is the Kotlin code I'm using to list the root directories on the device:
val file = Environment.getStorageDirectory()
// ensure we have file access permission
if (Environment.isExternalStorageManager() && Environment.isExternalStorageManager(file)) {
Log.d("FileManager", "Exists: ${file.exists()}")
Log.d("FileManager", "Is directory: ${file.isDirectory}")
Log.d("FileManager", "List files: ${file.listFiles()}")
}
Here is the output:
D/FileManager: Exists: true
D/FileManager: Is directory: true
D/FileManager: List files: null
As you can see, the directory exists, but listFiles() unexpectedly returns null. Given this, how can I navigate all the files on the device, starting from the root directory?
I have seen similar questions, but their answers all seem outdated or unusable. Here are some of the suggestions I've found:
Use Environment.getExternalStorageDirectory()
This works, but is deprecated in API level 31.
Use android:requestLegacyExternalStorage="true"
This is outdated.
Use ActivityResultContracts
This does not apply to a custom file manager app.
Given this, how can I navigate all the files on the device, starting from the root directory?
You can't, at least on unrooted devices. Even with MANAGE_EXTERNAL_STORAGE, all that you have access to is external storage, not the entire device filesystem.
Use Environment.getExternalStorageDirectory() — This works, but is deprecated in API level 31.
It is now undeprecated, as of Android 12L and Android 13 DP2. Hopefully the public documentation will reflect this when Android 13 ships later this year.
Use android:requestLegacyExternalStorage="true" — This is outdated.
You still want it for Android 10 support.
Adding a uses permission MANAGE_EXTERNAL_STORAGE to manifest file is not enough to get 'all files access'.
You also at runtime have to start an intent for Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION.
I'm returning the external files directory for API 23 and below.
I want to check that the media is mounted, however this test is always passed, although the WRITE_EXTERNAL_STORAGE permission is not yet declared in the manifest.
I'm not sure, but I thought the test should fail for API 18 and below if the WRITE_EXTERNAL_STORAGE is not declared/enabled?
A permission denied exception is then thrown when trying to access this directory.
if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
{
return getExternalFilesDir(null).getAbsolutePath() + "/update_files";
}
We have an app that uses external storage to store some temporary files: images, binary data. The code for that has been working for a few years without big changes until recently. On Android Q it doesn't work:
File f = new File(Environment.getExternalStorageDirectory().toString() + File.separator + MainActivity.APP_DIR)
f.mkdirs();
// do sth with f
The mkdirs now returns just false.
Required permission is provided in the manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
The code works fine on previous versions of Android. Is there some system level change to this type of access? If so, what is the workaround?
There was huge privacy change in android Q by introducing Scoped Storage.
Since Q beta 4 it's possible to opt-out of that feature by:
targeting API 28 (or lower)
using requestLegacyExternalStorage manifest attribute (while targetting API 29):
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android Q. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
edit: as mentioned in other answer this does not work if app is targeting API 30 - Android 11 devices will ignore legacy storage flag.
edit 2: heads up for anyone planning to publish on play store - soon usage of this flag will be restricted (new and updated apps won't be accepted) unless its required for core functionality (e.g. file manager)
UPDATE: Since Android 11 scoped storage is enforced. Apps that target Android 10 (API level 29) can still request the requestLegacyExternalStorage attribute. This flag allows apps to temporarily opt-out of the changes associated with scoped storage, such as granting access to different directories and different types of media files. After you update your app to target Android 11, the system ignores the requestLegacyExternalStorage flag.
In API level 29 direct access to shared/external storage devices is deprecated. When an app targets Build.VERSION_CODES.Q, the path returned from getExternalStorageDirectory() 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.
It's a best practice to use scoped storage unless your app needs access to a file that doesn't reside in the app-specific directory.
Those who face the issue with managing files in Android-Q may read through this article to know further.
In the new Android update API 30 you can only write in your app-specific files
File directory = new File(context.getFilesDir(), "YOUR_DIR");
directory.mkdirs();
or in the external storage of your app Android/data
File directory = new File(myContext.getExternalFilesDir("FolderName"),"YOUR_DIR");
UPDATE
this answer provided another solution https://stackoverflow.com/a/65744517/8195076
UPDATE
another way is to grant this permission in manifest
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
like this answer https://stackoverflow.com/a/66968986/8195076
In my manifest I only ask for these two permissions:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
But when I install the app on my phone I also get this notice:
Low-risk permissions
Phone ID
Get your phone ID, including IMEI, IMSI, etc.
From what I gather from this SO answer, I should need to use TelephonyManager and call
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
to require this permission. But I'm not using TelephonyManager or calling that permission. I don't want to ask users to give this permission. I've looked through my manifest and gradle files. Could it be that some code I used triggered this permission being called without me specifically asking for it? I know I'm not giving you a lot to go on, by I don't know where else to look.
Update 1
I created a completely new project in Android Studio and generated a signed APK from it. I installed it but no permissions were asked. (That at least confirmed for me that it wasn't some "new feature" in Android Studio that automatically asked for this permission.)
Update 2
As of #antonio's answer I found app/build/outputs/logs/manifest-merger-release-report.txt However, I didn't find any implied permissions being requested.
Update 3
Here are the dependencies my app is using (from gradle.build):
dependencies {
compile 'com.android.support:support-v4:21.0.3'
compile 'com.readystatesoftware.sqliteasset:sqliteassethelper:+'
}
I tested a new application with both of these dependencies and no permissions were requested.
My next step is to add every activity again from scratch and see if I can find where this permission starts getting called. To be continued...
Update 4
I played around with copying everything to a new project and refactoring the project name and removing pieces, but it turned out to be quite complex. I wasn't able to isolate a reason.
Update 5
I set the targetSdkVersion to 1. This gives a new message when announcing permissions before installing the app:
read phone status and identity
Allows the app to access the phone features of the device. This
permission allows the app to determine the phone number and device
IDs, whether the call is active, and the remote number connected by a
call.
The old Phone ID permission notice that I wrote about at the beginning is still there (showing up after the app is installed). This makes me wonder if it is related to the OS (I'm using MIUI Android on a Xiaomi phone). There is still something about the app that causes this to display in this app but not in other apps. I need to test this out on other devices next.
This happens because you are importing a library with a targetSdkVersion lower than your application's targetSdkVersion
From the Manifest Merger documentation:
When importing a library with a targetSdkVersion lower than the
importing application targetSdkVersion, some permissions maybe
automatically added to the resulting merged manifest file.
This is necessary since such libraries targeted runtimes where such permissions were implicitly granted. Therefore declaring such permission was not necessary. However in more recent Android releases, these permissions are not automatically granted. Therefore, a library targeting such old runtime without the permissions would not work properly once merged in the application targeting a more recent runtime.
The permissions that can be added are:
WRITE_EXTERNAL_STORAGE Added when importing a library with a targetSdkVersion < 4
READ_EXTERNAL_STORAGE Added when importing a library that declared WRITE_EXTERNAL_STORAGE
READ_PHONE_STATE Added when importing a library with a targetSdkVersion < 4
READ_CALL_LOG Added when importing a library with a targetSdkVersion < 16 and using READ_CONTACTS permission
WRITE_CALL_LOG Added when importing a library with a targetSdkVersion < 16 and using WRITE_CONTACTS permission
You can inspect the report generated by Manifest Merger (In \app\build\outputs\logs\manifest-merger-XXX-report) to see what library caused the adding of the READ_PHONE_STATE permission. You will see something like:
android:uses-permission#android.permission.READ_PHONE_STATE
IMPLIED from AndroidManifest.xml:2:1 reason: the.library has a targetSdkVersion < 4
In a multi-module gradle-project, one has to apply an matching configuration, like defaultConfig{ ... }, where the targetSdkVersion should be specified to resolve these IMPLIED permissions.
Add the following code to your AndroidManifest.xml to remove the unnecesary permission(s):
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
Source:
https://stackoverflow.com/a/27542669/406295