-
Notifications
You must be signed in to change notification settings - Fork 761
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cordova 12 can not save file to Android API 33 file system #613
Comments
Can you show the complete code? The code snippet shown only the |
@breautek sure, my initial post has been updated with the whole demo code for proof of concept to write and save to file. |
Thanks, now we can see what you're attempting to do and at a glance everything looks right. There are caveats with using the external storage, but as far as I understand that doesn't apply if you're attempting to write to application-specific external storage which I'm not sure when I'll find the time but I'll try copying this code/concept in my own environment to see what I can find, if I can reproduce the issue. Normally I do that kind of stuff over the weekend and the GF might be pulling me away this weekend however. EDIT:
Actually... I don't think the file explorer will show |
You mean to use this plugin instead - https://www.npmjs.com/search?q=ecosystem%3Acordova%20storage%20access%20framework ? @breautek maybe you can give me example code which I can try as these plugins github pages are not very informative? |
By the way if I will use cordova.file.externalRootDirectory I get error code 9 |
I've never used any of these plugins myself in my own work so I don't have first-hand experience. Some community members was talking about this plugin over at apache/cordova#424 maybe it has some code snippets that you can get an idea how it works.
Under scoped storage rules creating new files in the root directory is forbidden. The android docs says to use the existing sub-directories for shared content, or your application data directory. File APIs which this plugin interfaces with are not very usable for external storage management anymore since API 29 which is when scoped storage came into effect. In fact, when scoped storage was first introduced on API 29 they completely disabled file system access across the board to the external filesystem. So this plugin will not work at all on API 29 devices. Android implemented something in API 30 they called Fuse which binds the Filesystem APIs to the scoped storage model, which allows limited access to the external storage via filesystem APIs. They primarily did this to allow NDK libraries to read media content. So effectively this file plugin is best used for internal storage use only.
Unfortunately the media store API is a drastically different API and it will be quite a feat if possible to facade the media store API behind a filesystem-like API. Which is also in part the reason they implemented the Fuse system for API 30+ devices.
References: |
Okay, I will try with saf-mediastore plugin to see will it work. |
I have the same issue. The file save does not throw any error and it doesn't get saved in some Android 13 devices. Did you find a solution to this @Geshaa ? What worked for you in the end? |
Well unfortunatelly I still have no solution @Mr-Anonymous |
I know this is still a problem with cordova-plugin-file. After 4 days digging into the problem, the only solution that works stable so far is using another plugin: https://github.com/customautosys/cordova-plugin-saf-mediastore here is an example on how to read file:
|
Yep @tmishutin , I was just able to make use of this plugin as well a couple of days ago.
|
We had the same problem with writing to our own directory in Android root dir ( As far as I understood from the Android documentation, MANAGE_EXTERNAL_STORAGE is enough to write to that directory. This permission was implemented in one of previous Android versions. However, this permission has to be granted exclusively by user, it is not enough to be mentioned in config.xml. But, starting with Android 13 we were not able to write. I realized that the cordova-file-plugin is refusing get dir request because we had have not granted all READ_MEDIA_* permissions and FileUtils.hasReadPermission() returned false. If I added READ_MEDIA_* permissions to config.xml everything worked as before. Anyway, I don't understand why filePlugin is testing if all READ_MEDIA_* permissions are granted to allow read access and not at least one of. IMHO, this is bypassing the Android granularity. I propose to test in hasReadPermission and also in hasWritePermission if all files access is granted also. If it is granted, everything works without READ_MEDIA_* permissions granted. In case, I disabled All files access in Android Special access settings, read/write was refused with Unknown error from filePlugin private boolean hasReadPermission() {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return Environment.isExternalStorageManager() // Test all files access, MANAGE_EXTERNAL_STORAGE permission
|| PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
|| PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_VIDEO)
|| PermissionHelper.hasPermission(this, Manifest.permission.READ_MEDIA_AUDIO);
} else {
return PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
}
}
private boolean hasWritePermission() {
// Starting with API 33, requesting WRITE_EXTERNAL_STORAGE is an auto permission rejection
return android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
? Environment.isExternalStorageManager() // Test all file access, MANAGE_EXTERNAL_STORAGE permission
: PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
} |
You're right. It's done this way because A) filesystem doesn't know what kind of file you're working with and B) this represents the old behaviour the best. Android's scoped storage model does not fit well in a "Filesystem" concept and in fact there are a lot of limitations in using Filesystem APIs when interacting with the scoped storage framework. Using a media store plugin to interface with media store APIs should allow you to not request the READ_* permissions at all.
While this permission will allow you to access the external filesystem as a traditional filesystem, the permission is a protected permission. Which means you need to provide justification to Google make use of the permission. Google reserves the right to reject any app that they feel do not need the MANAGE_EXTERNAL_STORAGE permission. In otherwords, unless if your app's main purpose is to manage files, you likely cannot make use of that permission and deploy to the Google Play store. This is why it's not used by the file plugin. Google has also announced they are going to be more strict on the usage of the READ_* permissions, which puts a greater emphasize on moving towards the media store API. READ_MEDIA_IMAGES, and so on requires you to be implementing an app that requires broad access to a media type (such as a gallery app) In otherwords, I believe the file plugin is no longer going to be useful for interacting with the external storage partition and usage of the file plugin is probably going to be best reserved for interacting with internal app private storage only. I'd strongly consider looking into media store plugin instead. |
Finally, Google can accept the need of this permission, or so it's our case, the app is built for internal business usage and it is not provided by Play store. Other words, application can have this permission allowed, so FilePlugin should consider allowed We are storing logs, zip backups and images to specific directory in android root to be accessible for admin externally, not only from app. So it is not only media file type, but any type. Is there a way to use media store to acces any type of file? Why can't be FilePlugin used as facade to media store, so any implementation of FilePlugin do not need to be rewritten. Thank you for your response. It saves me time. |
Might be possible but will be very difficult and/or error prone. Media Store interfaces exposes no file structure. It's just containers so to speak that you query against. So the concept of directories, file paths, etc doesn't really exists. So it will be hard to facade that concept using a traditional filesystem API.
I'm pretty sure there is a misunderstanding there, but I don't interact with the external filesystem in any of my projects so I don't have first hand experience either. What I do know is filesystem access to external storage is limited to images, videos and audio files, or app's own private data (in the app's external data directory). That's why the READ_MEDIA_* permissions only includes video, audio and images, but not documents for example. If you're using the app's private data directory, like If you need to access other kinds of document files like zips or txt, etc, then the MediaStore API is the only path to access non-media files. Or if you're not deploying your app through app stores then the An example is something like:
It's untested but presumably if the permission is declared and user has the setting enabled in the app settings, then the filesystem APIs should just work... (I think). The plugin may need to be forked to remove READ_MEDIA/READ_EXTERNAL_* checks however. |
As I had mentioned in my first comment, we did manage this to be working. We use What we had realized with Android 13 is that it was not working, because file plugin did not consider this And there was my comment targeted, file plugin requires This was maybe the root cause of problems also of other implementations that realized problems with file plugin on Android 13. That the app did not request FYI, we store files in |
ok, I read the previous discussions here and I am still unsure on how do I resolve this for this plugin in Android 13? So what is the solution for this please? In my app, I allow users to download photos and PDFs that they can store in their device in any of these urls which is available:
But nothing is triggered and there are no errors showing either. |
Might be worth a test but I think |
Hi, After reviewing the previous discussions on this topic, I find myself in agreement with Mr. Anonymous. It appears that the solution remains unclear. Despite adding READ_MEDIA_* permissions to my config.xml, my app fails to execute the desired action, specifically downloading a PDF file. In my code, I use cordova.file.externalRootDirectory + Download (file:///storage/emulated/0/Download). While this approach functions as intended on devices running Android 11, it encounters issues on those running Android 13. Consequently, I'm left wondering whether it's feasible to download files to the specified folder. I attempted using cordova.file.externalDataDirectory + Download, which yielded partial success. This method opens the file with a PDF viewer, facilitating the download to the Download folder. However, I observed that when using this directory, the file is located in a folder structure resembling file:///data/user/0/app/files/Download. This behavior deviates from my expectations and could potentially complicate the file retrieval process for end-users. |
Hi Sonja @sorojas-technisys , If you want to use externalRootDirectory, as I wrote before, if As was mentioned by breautek, Google can block app with this MANAGE_EXTERNAL_STORAGE permissions requirement if to be provided in Play store. For me this is not problem as our app is used in enterprise internally. |
A PDF is a document file and android lacks a permission system for document files. They only have _MEDIA, _VIDEO, and _AUDIO for reads. Additionally there is no permissions for writes. More details on this is now documented but if you're working with document files and require to use the external filesystem, then migrating to a MediaStore plugin will probably become necessary. |
Hi @breautek , In my case I'm not getting callback from FileSystem's getFile funtion where it is pointing to cache directory. |
Bug Report
Problem
I am using Cordova 12.0 and I am creating cordova android 12 project API 33 on which I am trying to create text file and save it on phone memory so it could be then used vie phone File Explorer.
I use simple code like code below that and createFile method itselfs simple uses fileWriter write to write Blob object to file.
`
However I have no error but file has never been created in the device/emulator.
What is expected to happen?
File to be created somewhere in the device.
Maybe I have to use different key than
externalDataDirectory
?What does actually happen?
Nothing
Environment, Platform, Device
Windows 10 running Android Studio
Version information
Cordova 12
Cordova android 12
Cordova-plugin-file 8.0.1
Android Emulator API 33
Checklist
The text was updated successfully, but these errors were encountered: