I am trying to use "Legacy Storage" for my app so that it can run on API29, as a temporary measure until I can understand the new storage model. I have set
requestLegacyExternalStorage="true"
in the manifest, but it still doesn't work. The Android docs say:
Test scoped storage
To enable scoped storage in your app, regardless
of your app's target SDK version and manifest flag values, enable the
following app compatibility flags:
DEFAULT_SCOPED_STORAGE (enabled for all apps by default)
FORCE_ENABLE_SCOPED_STORAGE (disabled for all apps by default)
To disable scoped storage and use the legacy storage model instead, unset both flags.
How do I unset the flags? Any other advice?
Back to the main question:
How do I unset the flags? Any other advice?
The flags you mention (DEFAULT_SCOPED_STORAGE and FORCE_ENABLE_SCOED_STORAGE) are for development purpouse only. You can set them in App Compatibility Changes panel on settings. To find this pannel, you have to unlock Developer options in settings and you can find there App Compatibility Changes.
For more information, see the documentation: How to identify which changes are enabled
At last (with more research on stackoverflow!) I have found the way around my problem, so I'll describe it here in case it is of help to anyone else.
THE APP WORKS OK UP TO API28:
My app downloads a file called myfile.txt and saves it in external memory, using DownloadManager. The directory where DownloadManager saves it is determined by
request.setDestinationInExternalPublicDir("","myfile.txt");
which returns the directory path/storage/emulated/0
When I read the file, I get the directory to read from using
String myPath = Environment.getExternalStorageDirectory();
which returns the same directory path. This works OK up to API28.
THE PROBLEM:
For API29, I put
android:requestLegacyExternalStorage="true"
in the <application....../> section of the manifest, but even then request.setDestinationInExternalPublicDir doesn't work - it crashes with a runtime error.
THE ANSWER THAT WORKED FOR ME:
Instead of request.setDestinationInExternalPublicDir I used
request.setDestinationInExternalFilesDir(this,"","myfile,txt");
which works OK in API29 and also the earlier versions. However, it returns a different directory path: /storage/emulated/0/Android/data/com.barney.aboutmyjourney/files (com.barney/aboutmyjourney is my app).
To get the directory path when reading the file, I therefore used
File myFile=this.getExternalFilesDir("myfile.txt");
String myPath=myFile.getParent();
which returns the path I want (/storage/emulated/0/Android/data/com.barney.aboutmyjourney/files).
This now works for me. Now just lots of slog modifying all my file IO. Perhaps I should just understand and used Scoped Storage instead!
Related
I am using Samsung A30s phone for accessing phone storage files. My files location in device is /storage/emulated/0/MY_FILES/. I kept some files in MY_FIlES directory but my below code does
not show any containing files under this directory.
So how can I get all files belong to this MY_FIlES directory in phone storage?
File Directory = new File("/storage/emulated/0/MY_FIlES/");
File[] files = Directory.listFiles();
But files return null;
Note: I have a permission(READ_EXTERNAL_STORAGE) to access file.
One important thing, I updated my phone in latest API. But before update, I used this path, " /sdcard/MY_FIlES/ and it worked fine.
First, never hardcode paths for apps that you plan to distribute. For those, please use methods on Context, Environment, or StorageVolume (Android 11 only) to find the base directory to use.
For Android 10 and 11, you need to add android:requestLegacyExternalStorage="true" to your <application> element in the manifest. This opts you into the legacy storage model, and your existing external storage code will work.
Note that in Android 11+, you will not be able to write to that directory, regardless of whether you have requested WRITE_EXTERNAL_STORAGE or not. Google would vastly prefer that you use the Storage Access Framework (e.g., ACTION_OPEN_DOCUMENT), so that users have more control over where files get placed on their devices or in their chosen cloud storage providers.
I would like to add something more to the #CommonsWare answer. As per the Android's storage update, they enforce scoped storage in the Android 11
version.
But to give developers additional time for testing, 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.
If your app opts out of scoped storage when running on Android 10 devices, it's recommended that you continue to set requestLegacyExternalStorage to true in your app's manifest file. That way, your app can continue to behave as expected on devices that run Android 10.
For more info on this, please check documentation
I've been going over the Android file access documentation lately, but I seem to be unable to figure out how to actually open a file given as a string containing the path to the file I'd like to open.
What I (eventually) want to accomplish is something like this:
The user selects a specific kind of text file using Intents, receiving a URI to the file. From this I derive the path (getPath()) and pass this string to the native C++ code.
The native C++ opens the file from the string, parsing the content.
Perform some actual work with the above.
From what I've found so far, it seems like it is no longer possible to open files this way (as of SDK version 26 at least):
A hard-coded path to a file I know exists gives me permission denied.
The path itself received from getPath() triggers a No such file or directory error.
One workaround called for opening the file on the Java side using the ContentResolver, and then passing the file descriptor to the native side. This works, but it's problematic: the files can contain references to other files to be opened ("include files") making such a solution of limited use.
Just to make things clear, these files reside locally on the "USB" partition of the Android system, unrelated to the app itself. Not as resources/assets to the APK or anything similar which other questions of this kind seem to require.
In summary, I guess the question is this: Is it possible to open a file, and possibly any other files it refers to, given a path from the Java side of the application? Is there any requirements for doing this, such as requesting the correct permissions for folders or something similar?
As of Android 6.0 (API level 23) you need to request permissions every time your app starts for "dangerous actions" such as accessing the filesystem or reading contacts. The linked pages already have a snippet of code you can copy.
On older phones requesting permissions in the manifest was sufficient, but the target SDK version was recenly upped to 8.0 (=26). If you want to support devices pre-6.0, the Android compatibility library will allow you to call the same API.
I fully read the documentation of Cordova plugin filesystem. Furthermoe I've been googling without though conclusive explanations.
Particularly, how do the paths stored in the variables in cordova.file.* map the real folders in Android filesystem that you can see on any File Explorer? For example, I cannot save a file into the Downloads folder. I tried file:///android_asset/Download without success.
What exactly is the protocol file:/// and the path file:///android_asset/? What is the "application's sandbox"?
For example, I save a file into cordova.file.cacheDirectory because I need to deal with a temporary file, but I tried to find such file within a file explorer, and I can't find such file. Is it hidden somehow? I can't find it neither in /data/data/<app-id>/cache nor in "file:///data/user/0/com.form.parking.violation/cache/" (real value of that string).
I know, it's too many questions, but I will plan to make this a canonical question, since clear and pedagogical information is very scarce.
Using a 'File Manager' app on device won't give you access to items listed as 'Private' in the documentation you've listed. 'Private' means no other app should be able to see the contents which is sort-of described by "application sandbox". Normally a sandbox is for describing an environment which something can't get out of. If you aren't familiar with multi-user environments it can also mean that others without the right level of permission can't see in.
And unless the device you are testing on is rooted, you won't be able to see those 'private' files like the SQLite database and other files you are interested in unless you use adb from the Android SDK with the adb shell command run-as as described here:
Android ADB access to application databases without root
Note that to grab the files you need to have your Cordova app debuggable by Android Studio (debug mode).
For what is file:///android_asset/ I'll refer to this SO question:
What does file:///android_asset/www/index.html mean?
On Android 4.4.2 Environment.getExternalStorageDirectory().getPath() returns /storage/emulated/0 but this path does not exist on my Nexus5 Android 4.4.2. Environment.getExternalStorageDirectory().getPath() worked up until Android 4.4.2.
How can I get the /sdcard path on Android 4.4.2?
This path does not exist on my Nexus5 Android 4.4.2.
Yes, it does, for your process at runtime.
For example, this sample project downloads a file to Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS). If you log the location at runtime, when running it on a Nexus 5, it is reported as /storage/emulated/0/Download. And the download succeeds.
If you are looking for /storage/emulated/0 via DDMS or adb shell, you will not find it. For those tools, default external storage is /mnt/shell/emulated/0. Hence, the downloaded file from the above sample appears in the /mnt/shell/emulated/0/Download directory.
AFAIK, the difference is tied to providing separate external storage to secondary accounts.
The Storage Options documentation says to use Environment.getExternalStorageDirectory() (as you are already correctly using). This function is available on all versions of Android.
Are you seeing it return a path that isn't actually available on a 4.2 device?
Please note (from Environment.getExternalStorageDirectory()):
Applications should not directly use this top-level directory, in order to avoid polluting the user's root namespace. Any files that are private to the application should be placed in a directory returned by Context.getExternalFilesDir, which the system will take care of deleting if the application is uninstalled. Other shared files should be placed in one of the directories returned by getExternalStoragePublicDirectory(String).
Writing to this path requires the WRITE_EXTERNAL_STORAGE permission, and starting in read access requires the READ_EXTERNAL_STORAGE permission, which is automatically granted if you hold the write permission.
Starting in KITKAT, if your application only needs to store internal data, consider using getExternalFilesDir(String) or getExternalCacheDir(), which require no permissions to read or write.
Sometimes /storage/emulated/0 can be written to, but reads fail... so tests for "writability" are not sufficient. This is such an annoying problem, I have come up with an equally annoying but effective solution.
Hardcode "/mnt/sdcard" Yea, I said it.
Looks like someone else said it first ... storing android application data on SD Card
More joy... http://forums.bignerdranch.com/viewtopic.php?f=414&t=7407
I'm writing an Android app which uses wi-fi, so I can't easily debug to emulator (no wi-fi support... ;-), so I go with my real device (a Samsung Galaxy S).
I would like to be able to read data files my app writes, to debug and test.
If I use, say:
new File(getFilesDir(), "myfile.xml");
I get my data file written to /data/data/MYPACKAGE/files/, but that directory is non accessible via adb (nor via Eclipse's DDMS).
My device is not rooted (and I'd prefer to avoid rooting it, if possible... ;-)
Where should I write my data file to?
It probably makes sense to put the files on the sdcard during development, formally you should call getExternalStorageDirectory() to find it and of course will need external storage permission.
Alternatively, you could give public access to your private files in the debug version; just don't forget to turn that off before you ship (as a certain Internet telephony company reportedly did). However, this will not make the private files browsable as the intervening directories are not, you would only be able to adb pull them via their exact path name.
A third choice would be to leave the data internal and private, but have a debug function to copy it over to the sdcard for analysis. You could even do this in a separate .apk establishing a shared user id with the first, meaning no changes at all to your application.
Simply use external storage!
You can write to your SDcard. You should use getExternalStorageDirectory() to get your SDcard's path. You will have to include the <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> in your Manifest to do that.
The answer differs depending on your API level. Review the section in the documentation on external storage to get the answer for this.
http://developer.android.com/guide/topics/data/data-storage.html#filesExternal
For a somewhat generic answer, the sdcard directory that you should be storing files in is the directory returned from getExternalStorageDirectory() (which should be the root of your sdcard or possibly internal expanded storage as with my Captivate), with subdirectories of /Android/data/your.package.name/files
Oh yes, and as another poster mentioned, don't forget the WRITE_EXTERNAL_STORAGE permission in your manifest.