My `requestLegacyExternalStorage` is not working in Android 11 - API 29 - android

I understand why requestLegacyExternalStorage is not working on Android 11 with API 30. However, it doesn't work for me on Android 11 when I am targeting to API 29.
Here are the code level differences I observed:
Method StorageManager::getStorageVolumes was able to list all the volumes on Android 10 (both internal storage and external storage), but it only lists internal storage on Android 11.
We also tried File("/storage").listFiles(), it returns null which means this path doesn't exist.
My app is designed to browse files on external storages, now it stops working on Android 11 as we're not able to see the external storage for some reason.
Here is my AndroidManifest.xml
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:requestLegacyExternalStorage="true" >
...
Here is my build.gradle
compileSdkVersion 30
buildToolsVersion '30.0.1'
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
...
I am wondering if there is anything I missed in order to use the legacy way to access external storages on Android 11.
Updated: #blackapps
File("/storage").listFiles() returns a File object of the external storage with path like /storage/981B-469E, and we're allowed to list files in it.
Context.getExternalFilesDir() returns a File object with path like /storage/emulated/0/Android/data/com.xx.xx/files
Environment.getExternalStorageDirectory() returns a File object with path like /storage/emulated/0
I am not able to list files inside paths like /storage/emulated/0.

Android 11 has removed scoped storage so, legacyExternalStorage is only seen in Android 10, and ignored in Android 11, if you migrate back to Android 10 then it will work.
or use in AndroidManifest.xml
Manage_External_Storage= true
and ask for Access all file permission from user.
To overcome this situation, Google had given options to use other options, and nothing more is in their documentations.

Related

How to define permission in Manifest specifically for newer Android SDK?

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.

Android mkdirs() return false on Android 11 with Environment.getExternalStorageDirectory()

Mkdirs() function is not working on Android 11. every thing is working fine on Android 10 and lower.
Code:
***String path =Environment.getExternalStorageDirectory().getAbsolutePath() + "/My_directory/";
File temp_file = new File(path);
if (!temp_file.exists()){
Boolean can_create= temp_file.mkdir();
}***
the above code returns true in case of Android 10 or lower. but returns false in case of Android 11.
Manifest permissions:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Noting that runtime permission is considered for same (READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE).
Manifest application:
<application
android:requestLegacyExternalStorage="true"
The only way I am able to write in external storage is using getExternalFilesDir() but this is not the root directory.
according to this developer website, we can't create folders any more in the root directory!
Questions so far after checking this:
1- Is it confirmed that in Android 11 we can't create any folder on the root directory? any work around?
2-If yes, what is the way forward to save data in external storage, excluding getExternalFilesDir() ?
3- Why android:requestLegacyExternalStorage="true" is not working?
***String path =Environment.getExternalStorageDirectory().getAbsolutePath() + "/My_directory/";
You cannot create directories in root of external storage on Android 11 devices.
Instead create your own directories in one of the public directories of external storage.
File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "My_directory");
Why android:requestLegacyExternalStorage="true" is not working?
android:requestLegacyExternalStorage is no longer works on Android 11+. It is just a helper on Android 10 to give developers more time before migrating to scoped storage. On scoped storage, you need to use URI for creating, renaming, moving files, etc. Thus java.io.File is almost useless now.
Is it confirmed that in Android 11 we can't create any folder on the root directory? any workaround?
No workaround.
BTW, to reduce scoped storage complexity, I've created a library named Simple Storage. It works across API levels.

React-Native 0.63 not able to access Device Storage images in Android 10

I am using React Native 0.63. I wanted to display images in a FlatList from my phone's device storage and was using <Image/> for the renderItem prop of FlatList.
Using the source prop, I added a uri key but I am still not able to see the images. The image tiles are being generated in the FlatList and I am able to see the thin scrollbar on the right when I try to scroll over the list.
This is what I am doing to display the image. (The uri used here is of an image which I got from the CameraRoll.getPhotos() API)
<Image
source={{ uri: 'file:///storage/emulated/0/Pictures/Twitter/20201019_160855.jpg'}}
style={{ height: 100, width: 100 }}
/>
I have made sure about the following:
Allowed READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions for the application.
Added file:// prefix to the file.
Made sure source={{ uri: ... }} in <Image/> works for external links, tried using Google logo, random jpegs from the internet.
Another thing which I noticed is, I am using Samsung phone with Android 10 on my device where I am facing this issue. I tried running this on the Google Nexus 5 Android Emulator which also runs on Android 10 and works perfectly fine (By this, I mean I am able to access the emulator's device storage and not using the exact same uri specified above).
UPDATE: Tried compiling the application on Android 7, the above thing is working fine. Any issue with the the SDK Versions?
Are you getting a permission denied error?
This might be helpful:
https://github.com/react-native-image-picker/react-native-image-picker/issues/1259
Something to try adding:
android:requestLegacyExternalStorage="true"
... to the tag in your AndroidManifest.xml.
Warning:
From May 5 2021 onwards:
Play Store will not accept this implementation and remove all apps using it except if the app functionally really requires it and it needs to be requested.
The implementation will need to use Scoped Storage.
Are the images appearing on the emulator correctly?
If the issue is only on your device, you will want to check you've added
android.permission.READ_EXTERNAL_STORAGE to your AndroidManifest.xml and requested perission to use file storage
See: Usage - react-native CameraRoll
This can be helpful: https://github.com/react-native-image-picker/react-native-image-picker/issues/1259
In manifest
android:requestLegacyExternalStorage="true"
<application
android:name=".MainApplication"
android:label="#string/app_name"
android:icon="#mipmap/ic_launcher"
android:allowBackup="true"
android:theme="#style/AppTheme"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">
build.gradle
// camera
compileSdkVersion = 29
I Found Storage Permission wasn't working in the Samsung Phone
so i added following in the
android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
More Importantly This fixed my Issue :
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
then check for the Permission as regular and it will work
My Config for the React-native APP
"react": "18.1.0",
"react-native": "0.70.6",
buildToolsVersion = "31.0.0"
minSdkVersion = 22
compileSdkVersion = 33
targetSdkVersion = 33
This Worked in My case 👍👍
Happy Hacking 😂

Android listFiles returns empty array

I uploaded several files to folder "/sdcard/Books/LadySusan" on my android emulator. When I check if file in folder exist, expression
new File("/sdcard/Books/LadySusan/ladysusan_1_austen_64kb.mp3").exists()
returns true, file exists in folder, but when I use
new File("/sdcard/Books/LadySusan").listFiles()
it returns empty array, not null but File[0]. I have permissions to read files from SD card
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
and code to request them. Any idea what can be wrong?
Finally I found. It seems to me there is a bug in Android SDK (10.0+). When I started emulator with Android 9.0, it returns files as I expected.
It is not a bug. As of Android 10, external storage access is scoped to app files and media.
This means that your application can only access the files in the app-specific directory (which can be retrieved in code with getExternalFilesDir()) or media in the media store.
As long as you target an Android version lower than API level 30 (Android 11) you can get away with accessing the external storage via the following permission:
<application android:requestLegacyExternalStorage="true" ... >

Android Q: file.mkdirs() returns false

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

Categories

Resources