Storage updates in Android 11

Feature Request
Support the android 11 new file system based policy.
Motivation Behind Feature
Support the changes that are planned for android 11 (API level 30)
Feature Description
I'm not sure how to fully describe this, but basically support what's documented here:
https://developer.android.com/about/versions/11/privacy/storage
Alternatives or Workarounds
Not targeting API level 30 until this is supported?

Seems like the fix that was merge #417 in order to solve #408 will no longer work for android 11 (API level 30).
I'm not entirely sure I understand what needs to be done in android in order to get access to the file system, something like opening a file picker in order to get write permissions to a folder?
I'd love to help progress this, maybe send a PR if someone can guide me to the relevant place in the code and define the appropriate solution...

Seems like the fix that was merge #417 in order to solve #408 will no longer work for android 11 (API level 30).

Yes, you're correct. In API 29, there is a new file system policy. #417 utilised an available flag to request the old file system system, but API 30 will ignore that flag, forcing app developers to implement the new policy model.
To learn about the new android model, you should probably read up on Scoped Storage. I briefly read up on this last weekend and it appears that some apps may require to migrate their data locations depending on where they store data. Scoped storage means just that, you're access to the filesystem is limited to a certain scope. Parts of the file system that you might have had access before may be impossible to access in API 30. This means app developers should take this time to determine if they need to migrate data. Read the android notice for more information on migration. Given the usual pattern, users have probably until August 2021 before Google force API 30 for new apps, and November 2021 before users are forced to use API 30 on existing apps.
The new MANAGE_EXTERNAL_STORAGE permission I believe will essentially make the app behave pre API 29 without the legacy storage flag, but will likely be highly scrutinised by Google if the app uses it without a justified reason. This could be an opt in feature by documenting how one can add this permission using    config-file or edit-config. The users must be targeting API 30 in order to use this permission, it isn't available in API 29.
Lastly, getting familiar with the MediaStore APIs sounds like is quite important, because apps can access some external filesystem storages, for accessing pictures, videos, and other media-related resources.
What needs to be changed exactly in the Apache codebase I'm not so certain myself, I'm not that familiar with the code base.

Adding ' android:requestLegacyExternalStorage="true"' to AndroidManifest.xml solves the problem.
Read more at https://developer.android.com/training/data-storage/use-cases.

@olsbg this won't work for android 11, it does and will work for android 10.
I've migrated my entire code that saves file to the external file system to share files that are created in memory to avoid any future limitations...

@olsbg this won't work for android 11, it does and will work for android 10.
I've migrated my entire code that saves file to the external file system to share files that are created in memory to avoid any future limitations...

Can you share how you achieved this?

Instead of saving files to the file system I use the social sharing plugin. This way a user is prompted with options on what he wants to do with the file I just created in memory:
https://github.com/IsraelHikingMap/Site/blob/4e479a5a92051ae5ec84136ca8c20547a4d11f26/IsraelHiking.Web/sources/application/services/file.service.ts#L152

Thanks, @HarelM ,
Unfortunately this won't work in my case because I'm trying to read a file

@Vatsov Are you reading a file a user selects? If so I don't think there's an issue that is related to Android 11, or at least I hope not as you open the file picker and this should be OK even in Android 11 as far as I know.
The other way I solved the file open is by registering my app to open certain file types and allowing the user to share these files with the app.
https://github.com/IsraelHikingMap/Site/blob/4e479a5a92051ae5ec84136ca8c20547a4d11f26/IsraelHiking.Web/config.xml#L45
And:
https://github.com/IsraelHikingMap/Site/blob/4e479a5a92051ae5ec84136ca8c20547a4d11f26/IsraelHiking.Web/sources/application/services/open-with.service.ts#L85
Hope that helps... :-)

@HarelM  Yes, I’m trying to read the file which the user selects with this.fileChooser.open()
The interesting thing is that this.file.checkFile returns true, but when I try with this.file.readAsText it returns FileError {code: 1, message: "NOT_FOUND_ERR"}

FYI
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory + "/Download" used to work but now fails with Code 1 on Android 10 both without specifying  android:requestLegacyExternalStorage="true" and with. I can't test on Android 11.
It would be really useful to know if there's a plan for the maintainers to create a new release which makes the plugin work with Android 10 and 11 without the need to manually tweak files like the manifest. Hope there is. This is a really useful plugin.
Thanks

FYI
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory + "/Download" used to work but now fails with Code 1 on Android 10 both without specifying  android:requestLegacyExternalStorage="true" and with. I can't test on Android 11.
It would be really useful to know if there's a plan for the maintainers to create a new release which makes the plugin work with Android 10 and 11 without the need to manually tweak files like the manifest. Hope there is. This is a really useful plugin.
Thanks

What should work

window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory + "/Download" used to work

This should work as is when targeting API 28 (now forbidden by Google Play)
This should work when targeting API 29 with the dev version of the plugin (which has the android:requestLegacyExternalStorage="true"), alternatively you can use the  to add this flag.
This won't work when targeting API 30, as API 30 ignores the android:requestLegacyExternalStorage attribute.

It's important to read the Android Notes before you target API 30. You may need to migrate your files to another folder to maintain access to them when targeting API 30 using the new APIs.
Who this will affect
This will affect any app that reads or writes to the external file system (aka the cordova.file.external*). If you don't use external file paths, then you probably don't need any changes in your app.
I'll be adding a help wanted label. We don't have a planned solution yet. If a volunteer would take the lead and finding a solution to prepare a PR, I encourage to use our Dev Mailing List so that we can discuss requirements and potential solutions.
Useful Links: https://developer.android.com/about/versions/11/privacy/storage

What should work

window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory + "/Download" used to work

This should work as is when targeting API 28 (now forbidden by Google Play)
This should work when targeting API 29 with the dev version of the plugin (which has the android:requestLegacyExternalStorage="true"), alternatively you can use the  to add this flag.
This won't work when targeting API 30, as API 30 ignores the android:requestLegacyExternalStorage attribute.

It's important to read the Android Notes before you target API 30. You may need to migrate your files to another folder to maintain access to them when targeting API 30 using the new APIs.
Who this will affect
This will affect any app that reads or writes to the external file system (aka the cordova.file.external*). If you don't use external file paths, then you probably don't need any changes in your app.
I'll be adding a help wanted label. We don't have a planned solution yet. If a volunteer would take the lead and finding a solution to prepare a PR, I encourage to use our Dev Mailing List so that we can discuss requirements and potential solutions.
Useful Links: https://developer.android.com/about/versions/11/privacy/storage

@breautek Thanks for the quick response and useful summary. I'll have to think about this :-)

@breautek Thanks for the quick response and useful summary. I'll have to think about this :-)

I've recently implanted a small android app which had to save images to a folder/media storage so I might publish my conclusions here once I'm done...

I've recently implanted a small android app which had to save images to a folder/media storage so I might publish my conclusions here once I'm done...

window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory + "/Download" used to work

This should work when targeting API 29 with the dev version of the plugin (which has the android:requestLegacyExternalStorage="true"),

Did you mean running cordova plugin add https://github.com/apache/cordova-plugin-file.git
Will be this backward compatible for API <= 28 also?

window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory + "/Download" used to work

This should work when targeting API 29 with the dev version of the plugin (which has the android:requestLegacyExternalStorage="true"),

Did you mean running cordova plugin add https://github.com/apache/cordova-plugin-file.git
Will be this backward compatible for API <= 28 also?

Will be this backward compatible for API <= 28 also?

Yes, but you still need to use build tools version 29, otherwise the build will fail to recognize android:requestLegacyExternalStorage.

Will be this backward compatible for API <= 28 also?

Yes, but you still need to use build tools version 29, otherwise the build will fail to recognize android:requestLegacyExternalStorage.

Great, thanks, indeed I made
cordova plugin rm cordova-plugin-file
cordova plugin add https://github.com/apache/cordova-plugin-file.git

I tested it now and it allows me to use cordova.file.externalRootDirectory + "/Download" in Android 10 (API 29)

Yes, but you still need to use build tools version 29, otherwise the build will fail to recognize android:requestLegacyExternalStorage.

Did you mean this in config.xml?
<preference name="android-targetSdkVersion" value="29"/>

Thanks in advance

Great, thanks, indeed I made
cordova plugin rm cordova-plugin-file
cordova plugin add https://github.com/apache/cordova-plugin-file.git

I tested it now and it allows me to use cordova.file.externalRootDirectory + "/Download" in Android 10 (API 29)

Yes, but you still need to use build tools version 29, otherwise the build will fail to recognize android:requestLegacyExternalStorage.

Did you mean this in config.xml?
<preference name="android-targetSdkVersion" value="29"/>

Thanks in advance

I tested it now and it allows me to use cordova.file.externalRootDirectory + "/Download" in Android 10 (API 29)
Yes, but you still need to use build tools version 29, otherwise the build will fail to recognize android:requestLegacyExternalStorage.
Did you mean this in config.xml?

"Build Tools" is part of the Android SDK, you can download it using the Android Studio's SDK Manager tool.

I tested it now and it allows me to use cordova.file.externalRootDirectory + "/Download" in Android 10 (API 29)
Yes, but you still need to use build tools version 29, otherwise the build will fail to recognize android:requestLegacyExternalStorage.
Did you mean this in config.xml?

"Build Tools" is part of the Android SDK, you can download it using the Android Studio's SDK Manager tool.

@breautek thanks. I also used your answer, citing the source, to reply on stackoverflow the open issue.

@breautek thanks. I also used your answer, citing the source, to reply on stackoverflow the open issue.

Hi,
I am getting com.android.builder.internal.aapt.v2.Aapt2Exception exception after adding android:requestLegacyExternalStorage="true" using edit config. Any help is appreciated.

Hi,
I am getting com.android.builder.internal.aapt.v2.Aapt2Exception exception after adding android:requestLegacyExternalStorage="true" using edit config. Any help is appreciated.

Adding android:requestLegacyExternalStorage="true" is throwing AAPT: error: attribute android:requestLegacyExternalStorage not found. Because cordova 9.x.x is building with SDK-28 even i mention targetSdkVersion as 29. Is there any workaround for that?

Adding android:requestLegacyExternalStorage="true" is throwing AAPT: error: attribute android:requestLegacyExternalStorage not found. Because cordova 9.x.x is building with SDK-28 even i mention targetSdkVersion as 29. Is there any workaround for that?

You need to be using build tools 29.
https://cordova.apache.org/docs/en/dev/guide/platforms/android/index.html#adding-sdk-packages

You need to be using build tools 29.
https://cordova.apache.org/docs/en/dev/guide/platforms/android/index.html#adding-sdk-packages

Seems like opening a file in android 11 doesn't work. It does work in android 10. I'm not using any code but simply input with type=file to pick a file.
If anyone has a solution please let me know, I'll see if I can send a PR once I get to trying and fixing it...

@HarelM do you have any updates on this issue?
Is the social sharing plugin the only option yet to save a file in Android 11?

Not yet, but we are talking about two different issues:

Save to file
Open a file

To summarize my experience:

I have "solved" save to file by using social sharing - both due to security and mainly due to better user experience (IMO of course)
I will be looking into the issue of file open on android 11 soon - see issue https://github.com/IsraelHikingMap/Site/issues/1451 in my repo.

Also I've just seen recently that starting from August/November this year Google is changing the policy to target API level 30 for all new/updated applications so this has to be solved soon from my point of view...

Thank you @HarelM , regarding point 1. could you kindly tell me exactly which social sharing plugin do you use?

cordova-plugin-x-socialsharing
https://github.com/IsraelHikingMap/Site/blob/6b658ae41f5a21d582ac15ee18385af4f128c6ac/IsraelHiking.Web/package.json#L116

received yesterday, Google is pressing on time limits

Same here @jfoclpf. I will paste the text here so that people can find it easier.
Starting May 5th, you must let us know why your app requires broad storage access
We've detected that your app contains the requestLegacyExternalStorage flag in the manifest file of 1 or more of your app bundles or APKs.
Developers with apps on devices running Android 11+ must use Scoped Storage to give users better access control over their device storage. To release your app on Android 11 or newer after May 5th, you must either:
Update your app to use more privacy friendly best practices, such as the Storage Access Framework or Media Store API
Update your app to declare the All files access (MANAGE_EXTERNAL_STORAGE) permission in the manifest file, and complete the All files access permission declaration in Play Console from May 5th
Remove the All files access permission from your app entirely
For apps targeting Android 11, the requestLegacyExternalStorage flag will be ignored. You must use the All files access permission to retain broad access.
Apps requesting access to the All files access permission without a permitted use will be removed from Google Play, and you won't be able to publish updates.
There is a learn more

@breautek does this plugin request MANAGE_EXTERNAL_STORAGE, that is, does it request "Use of All files access  permission"? This last sentence is very alarming: "Apps requesting access to the All files access permission without a permitted use will be removed from Google Play, and you won't be able to publish updates."
Do you know how can we request use of just one single public directory, for example, the Download directory?
Is there any plugin or means to interact with the Android Storage Access Framework?

@breautek does this plugin request MANAGE_EXTERNAL_STORAGE, that is, does it request "Use of All files access permission"? This last sentence is very alarming: "Apps requesting access to the All files access permission without a permitted use will be removed from Google Play, and you won't be able to publish updates."
Do you know how can we request use of just one single public directory, for example, the Download directory?
Is there any plugin or means to interact with the Android Storage Access Framework?

This plugin does not use MANAGE_EXTERNAL_STORAGE. It was something I suggested people to use as a workaround in the past before Google made that announcement. Obviously unless if you're building a file manager type of app, it will be hard justifying the usage of MANAGE_EXTERNAL_STORAGE permission and therefore won't be recommended for most apps.
I think the single largest challenge with this plugin is how generic access the file plugin is suppose to be, and how do we translate file paths to a specific container type (such as Audio, Video, Pictures, Documents, etc), using the appropriate MediaStore APIs. Based on my experience, relying on mime type information inferred by file extension is not really reliable.
Someone will need to do the research, but it might not be possible to use this plugin for external storage anymore, or it may be more efficient to have a new plugin that uses the MediaStore APIs with a cleaner API. like getAudioList(), getDownloadList(), etc...

Thank you so much @breautek
but I really don't know either how come Play Store is complaining about MANAGE_EXTERNAL_STORAGE in my cordova app, because my config.xml does not make any reference to MANAGE_EXTERNAL_STORAGE. If my cordova APP has set this MANAGE_EXTERNAL_STORAGE where should it be?

Thank you so much @breautek
but I really don't know either how come Play Store is complaining about MANAGE_EXTERNAL_STORAGE in my cordova app, because my config.xml does not make any reference to MANAGE_EXTERNAL_STORAGE. If my cordova APP has set this MANAGE_EXTERNAL_STORAGE where should it be?

Probably in your <cordova-root>/platforms/android/app/src/main/AndroidManifest.xml
If you see it there, I'd suggest try removing the platform and re-adding it. Cordova does a good job making sure things gets added to files, but has a hard time cleaning up if configurations gets removed.

Thank you @breautek , I will check it out
If I understood correctly, it seems that a way to overcome the objections raised by new policy on Android 11+ is to use the Storage Access Framework. This Framework opens a system file picker and we can use it to open or save files with the intents ACTION_OPEN_DOCUMENT or ACTION_CREATE_DOCUMENT. This plugin could have a method that would trigger these intents with a file picker
// Request code for creating a PDF document.
private static final int CREATE_FILE = 1;

private void createFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");
    intent.putExtra(Intent.EXTRA_TITLE, "invoice.pdf");

    // Optionally, specify a URI for the directory that should be opened in
    // the system file picker when your app creates the document.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);

    startActivityForResult(intent, CREATE_FILE);
}

You can then access external storage

On devices that run Android 4.4 (API level 19) and higher, your app can interact with a documents provider, including external storage volumes and cloud-based storage, using the Storage Access Framework. This framework allows users to interact with a system picker to choose a documents provider and select specific documents and other files for your app to create, open, or modify.

in https://developer.android.com/training/data-storage/shared/documents-files#java
Maybe I'm missing or misunderstanding something, please correct me if I am.

This plugin could have a method that would trigger these intents with a file picker,

The goal of this plugin is to be able to programmatically control the file system files, so if the Storage Access Framework requires the use of UI to pick files, I don't think that will be acceptable in this plugin. And if that's the only way to access external files, then it sounds like we'll need a new plugin for accessing the external storage. Although I don't think that's the case, the MediaStore has Direct File starting in API 30, which I think could be used, but it is documented that this is an inefficient way (performance wise) of accessing MediaStore/Scoped storage files. Would be great if someone could experiment with this.

Here is my case :
I have an app in production that actually :

target API29, with android:requestLegacyExternalStorage="true"
and use :

cordova.file.externalRootDirectory
file path to external card, if the external card exits, via cordova.plugins.diagnostic.getExternalSdCardDetails

According to my tests, with a device using Android 11 (API 30) and android:requestLegacyExternalStorage="false" in AndroidManifest.xml

I can read an existing file
but can't create a new one

To support the changes requested by Google, I'm planning to :

check if access to external is actually granted via cordova.plugins.diagnostic.requestExternalStorageAuthorization
and then :
copy user existing files (as far as i can read them) to cordova.file.dataDirectory
delete existing files (if possible, not tested)
put a variable in localstorage, so that the app use cordova.file.dataDirectory for the next starts.

If it is a fresh install, I would use only cordova.file.dataDirectory for storing the new files.
But i'm stuck for the access to external card.
Is there a way to get a path to a folder in external card, even if that path is acceded only by my application ?
What is strange is that the path returned by getExternalSdCardDetails seems to use a sandboxed folder :
file:///storage/0123-4567/Android/data/com.mycompany.myapp/files/

Please update plugin to use MediaStore APIs too, and remove flag "requestLegacyExternalStorage" or make this flag optional

Please update plugin to use MediaStore APIs too, and remove flag "requestLegacyExternalStorage" or make this flag optional

If you want to opt out that flag simply install this plugin from npm and not github
cordova plugin add cordova-plugin-file

But then it it won't work an Android 10+

i wanna save a file, not share a file

Please update plugin to use MediaStore APIs too, and remove flag "requestLegacyExternalStorage" or make this flag optional

If you want to opt out that flag simply install this plugin from npm and not github
cordova plugin add cordova-plugin-file

But then it it won't work an Android 10+. If you just want to save a file, a temporary workaround is to use the cordova-plugin-x-socialsharing and then the user saves the file wherever they want. I got this tip from @HarelM and it works nicely on Android 11.

i wanna save a file, not share a file

You can save a file but you have to use the application directories, which are not accessible to the user, you can use cordova.file.applicationDirectory or cordova.file.applicationStorageDirectory
If you want to save a file for the user to later open (for example a pdf), you have to use cordova.file.externalRootDirectory. And right now, on Android 11+ you can't, unless you use the workaround I mentioned.

@Davparis just a question, for what purpose do you need to save and open the files? For the user later to open them or just for the internal storage of the app? If it's the latter you can use applicationStorageDirectory which works on every Android version.

Me

i wanna save a file, not share a file

You can save a file but you have to use the application directories, which are not accessible to the user, you can use cordova.file.applicationStorageDirectory which is read/write.
If you want to save a file for the user to later open (for example a pdf), you have to use cordova.file.externalRootDirectory. And right now, on Android 11+ you can't, unless you use the workaround I mentioned.

I need save to a shared folder. (Create/delete)

Right now on Android 11 you can't

Right now on Android 11 you can't
…

I know that. This a big problem

@victorvhpg Google will not remove apps with that flag to true, it will simply ignore that flag on Android 11+. Google will remove indeed apps with MANAGE_EXTERNAL_STORAGE permission with no valid reason therefor.

@victorvhpg Google will not remove apps with that flag to true, it will simply ignore that flag on Android 11+. Google will remove indeed apps with MANAGE_EXTERNAL_STORAGE permission with no valid reason therefor.

Sorry, you is right, the flag will ignored.
I dont wanna use
MANAGE_EXTERNAL_STORAGE  ( all device files)
Only a public/shared folder like "download" its good for me.

@breautek do you think a PR with only removing the requestLegacyExternalStorage=true here would move this issue a bit forward?
Here's what I have in my inbox in play store console right now (I'm using a version from this repo and not a official version):

New apps must target API 30 by August, updates must target it by November
You are using the above flag, don't, we'll remove your app by may 5th

So just removing this flag would allow using the latest version of this plugin from this repository which is not/will not be possible in a few weeks...
As I said earlier, I have stopped using external storage almost completely after the release of Android 10 since I kinda figured this would be the direction google is going for... I still have issues opening files though which is annoying but I also solved this by associating files with my app and in general avoid using files but rather upload content to a server and store user data in a database...
I'm beginning to think that using files is what we are used to from the old windows environment where folder and files were important, but as time progresses I see that more and more services are cloud based, and the usage of files is becoming annoying and inconvenient (compare the UX of word to google docs - the fact that in google docs everything is there, available, allows multiple users to edit, share, see and what not seems like the future to me... I might be completely wrong though :-))

@HarelM check the differences between the npm latest version 6.0.2 and this github dev version.
The differences in the core code, if I could fully understand, are negligible
The npm version does not have the flag requestLegacyExternalStorage to true, so my advice is to use the npm version, that is what I plan to do
cordova plugin rm https://github.com/apache/cordova-plugin-file.git
cordova plugin add cordova-plugin-file

That said, I also agree with you that that flag should be set to false. You can find it here.

API 29 enforces the scoped storage rules and that's what the legacy flag is for. To opt out of the new storage constraints. It was always meant to be a temporary flag with API 30 completely ignoring it and enforcing scoped storage constraints.
While removing the flag will have to be done eventually, it alone is not going to solve API 30 changes.
If you're app have no real reason to use external storage, then yes, it's probably best to migrate to internal storage. Internal storage is completely sandboxed and your app can read/write to this sandbox with no special permissions.
If time permits, I'd like to start experimenting with solutions next weekend.

If you're app have no real reason to use external storage, then yes, it's probably best to migrate to internal storage. Internal storage is completely sandboxed and your app can read/write to this sandbox with no special permissions.

Does that mean we need to amend this line in config.xml ?
<preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,cache,cache-external,assets"/>

In the case we just use internal storage, how this line should be? Is this ok then?
<preference name="AndroidExtraFilesystems" value="files,cache,assets"/>

But then it it won't work an Android 10+. If you just want to save a file, a temporary workaround is to use the cordova-plugin-x-socialsharing and then the user saves the file wherever they want. I got this tip from @HarelM and it works nicely on Android 11.

@jfoclpf I am not sure that I understood how the workaround works.
Using the cordova-plugin-x-socialsharing you will ask the user to share the document (on drive, by mail...) so he can get the document.
Or is there a way to use this plugin on the 'Files' android app so the user can save the file on his device ?

Users can install xiaomi file manager for example and share a file with that app or simply share with mail, messaging, Facebook, drive etc. The idea is that they can choose what to do with the file instead of the app deciding on an arbitrary location they should "guess".

Or is there a way to use this plugin on the 'Files' android app so the user can save the file on his device ?

I recommend my users to simply install this add-on, I'd say it's not even an app, it simply allows you to share to file system

@jfoclpf Will this work on Android 11 sdk 30+ ?

@jfoclpf Will this work on Android 11 sdk 30+ ?

Yes, so far the feedback from the users is that it works

I am using ionic 4 and I faced the same issue so I tried the ionic 4 version of resolveDirectoryUrl(cordova.file.externalRootDirectory, function (dirEntry) {)) and on success function I used dirEntry.getDirectory ('Download'... and I can create folders and save files to the Download folders on android 11 without adding android:requestLegacyExternalStorage="true". Here is the code for ionic 4. I have also tried to convert it to the normal web version.
createDirectory(rootDir) {
    return new Promise((res, rej) => {
      this.file.resolveDirectoryUrl(rootDir).then((rootDirEntry)=>{

        rootDirEntry.getDirectory('Download', { create: false, exclusive:false},  (dirEntry) =>{
          dirEntry.getDirectory('updates', { create: true, exclusive:false}, (subDirEntry) =>{
            this.updateDir = subDirEntry;
            this.appDir = dirEntry;
            res(this.updateDir)
          }, (err)=> {
            this.onErrorGetDir(rootDir+'Download/','updates');
            rej(err)
          })
        }, (err)=> {
          this.onErrorGetDir(rootDir,'Download')
          rej(err)
        })
      })

    })
  }

and the normal web version. Note that I have only tested the ionic 4 version of this code.
function createDirectory(rootDir /*cordova.file.externalRootDirectory*/) {
    window.resolveDirectoryURL(rootDir, function (rootDirEntry) {
      dirEntry.getDirectory('Download', { create: false, exclusive:false}, function (dirEntry) {
        dirEntry.getDirectory('updates', { create: true, exclusive:false}, function (subDirEntry) {
            /* delete a file or donwload a file using cordova-plugin-advanced-http.
                I tested mine using cordova-plugin-advanced-http to download a file to this folder */
            createFile(subDirEntry, "fileInNewSubDir.txt");

        }, onErrorGetDir);
      }, onErrorGetDir);
    })
  }

This doesn't allow you to write or read from the root directory but works with the download directory and probably other sub directories in the root directory.
You will need READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions.

@joshuaokpako that's strange, normally you shouldn't be able to write to cordova.file.externalRootDirectory + 'Download/' without the android:requestLegacyExternalStorage to true (check the answer from @breautek ) after API 29. Are you using the dev version of this file plugin (i.e. fetched from github)?

@jfoclpf I am using ionic 4 and the ionic cordova plugin is built on this file plugin. I have attached the link to the ionic 4 website that references this plugin https://ionicframework.com/docs/native/file

@joshuaokpako ah, now I see, you are using the npm version (npm install cordova-plugin-file) and not the dev version and in the npm version android:requestLegacyExternalStorage is not set to true. Thus it works.

I would like to invite people to try out my fork and give it a test: https://github.com/breautek/cordova-plugin-file/tree/feat/api30
My fork removes the requestLegacyExternalFlag which means it should work on both API 29 and API 30+. It ensures that it prompts for the proper permission in read/write external storage.
Note: It's possible that you've already granted or denied external storage permissions, in which case it may just work or it may not work. If you're getting a file not found kind of errors, or file listings are simply empty when you expect there to be content, that probably means you're lacking permissions. Android does not tell us that a file exists but you lack permissions to view; it just gives a file not found error. I assume this is for privacy reasons.
You can use adb shell pm reset-permissions to reset your permissions on all apps back to a state of "not requested", so that you can test as if the user is running your app for the first time.
I used https://github.com/breautek/cordova-file-api30-test-app as a test app. If something is not working right for you on my fork, feel free to make a pull request and add a test case.
Another Note: I've seen Google is cracking down, requiring external use via READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE; in order to use these permissions (and this plugin for accessing external storage) you will need to give justification. You'll going to have to evaluate if your app needs external storage access, and if not, then start migrating to internal storage. As a result of this, I'm planning on making this plugin add the external storage permissions as a configurable option.
Last Note: This plugin is currently not compatible with cordova-android@10/nightly. So please only test using<EMAIL_ADDRESS>

@breautek  thanks for the proposed solution.
Does this also fix Writing to external Files like ( /Download ) on SDK 30 ?

@breautek thanks for the proposed solution.
Does this also fix Writing to external Files like ( /Download ) on SDK 30 ?

It should. I didn't test writing to the Downloads directory explicitly, but feel free to augment my test app https://github.com/breautek/cordova-file-api30-test-app/blob/86b1c9135f1e68e62ddc2d874fd82cd933cb9392/www/js/index.js#L173

@breautek thanks for the proposed solution.
Does this also fix Writing to external Files like ( /Download ) on SDK 30 ?

It should. I didn't test writing to the Downloads directory explicitly, but feel free to augment my test app https://github.com/breautek/cordova-file-api30-test-app/blob/86b1c9135f1e68e62ddc2d874fd82cd933cb9392/www/js/index.js#L173

thanks for the proposed solution.

My fork is not intended to be a solution, I just want to make sure that my changes are covering API 30 properly in wide range of use cases before I prepare a PR.

@breautek  In your test you write to file to externalApplicationStorageDirectory , I tested it . That works fine.
I intend to save the file to Download folder inside externalRootDirectory.

externalRootDirectory is readable, but not writable. You cannot create directories or files inside this directory anymore.

I read your previous comment , but Is there any workaround or other plugin/method to save files to the public android Download directory ?

@interstellerS I know the thread is long, but we have already discussed that previously.
The current workaround is to use the npm version of this plugin and not the dev version, just run to be sure you're using the npm version
cordova plugin rm cordova-plugin-file
cordova plugin add cordova-plugin-file

The npm version does not use requestLegacyExternalFlag on which Google complains, on and after Android 11.
Then install the plugin cordova-plugin-x-socialsharing and use the options.files Array property. This let the users save the file wherever they want, instead of you trying to find the correct path. If you want to be sure your users are able to share to the file system, you can suggest them to install this simple APP, which is indeed just an addon (it will not create any new icon and it's not openable, it simply allows users to share a file to the file system).

I would like to invite people to try out my fork and give it a test: https://github.com/breautek/cordova-plugin-file/tree/feat/api30
My fork removes the requestLegacyExternalFlag which means it should work on both API 29 and API 30+. It ensures that it prompts for the proper permission in read/write external storage.
Here are some highlight changes:

It's possible that you've already granted or denied external storage permissions, in which case it may just work or it may not work. If you're getting a file not found kind of errors, or file listings are simply empty when you expect there to be content, that probably means you're lacking permissions. Android does not tell us that a file exists but you lack permissions to view; it just gives a file not found error. I assume this is for privacy reasons.
You can use adb shell pm reset-permissions to reset your permissions on all apps back to a state of "not requested", so that you can test as if the user is running your app for the first time.
externalRootDirectory is readable, but not writable. You cannot create directories or files inside this directory anymore.
I used https://github.com/breautek/cordova-file-api30-test-app as a test app. If something is not working right for you on my fork, feel free to make a pull request and add a test case.
I've seen Google is cracking down, requiring external use via READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE; in order to use these permissions (and this plugin for accessing external storage) you will need to give justification. You'll going to have to evaluate if your app needs external storage access, and if not, then start migrating to internal storage. As a result of this, I'm planning on making this plugin add the external storage permissions as a configurable option.
This plugin is currently not compatible with cordova-android@10/nightly. So please only test using<EMAIL_ADDRESS>

Im having a use case of reading the files from shared storage which is failing in android 11 currently. I had tried your solution. It asks for the permissions required and requestLegacyExternalFlag is removed. But still unable to read the files.
As the new apps would need to target SDK 30, any workaround for this issue? Any way to read files from shared storage(Downloads/images..).
Thanks

Hello, I confirm that it works with sdk 29.0.3.
I am writing an application that saves files that come either from a web server (downloaded) or created by my application.
I'm using sdk 30 and it works for downloaded files with "cordova-plugin-file-transfer" but not locally with "cordova-plugin-file"
Can someone explain me the difference between the two?
Thanks

I store a created file inside files folder in the internal storage and i want the user to want to send it via email using cordova-plugin-x-socialshearing. But when the Gmail app or Google Drive opens it says that the file couldn't be attached. I don't understand where the problem is, where this file must be stored...

Check the file size. File larger than 25 mb won't be attached. I have no issues with the plugin and gmail, I use it for reporting an issue in my app to attach log files.

Chooser

@Vatsov file chooser also returns base64 data, you can convert that into blob, then use the this.file.writeFile() method to save the file into your app directory

Hello, I was facing a similar problem, when I needed to read a file from the storage of an Android device, the plugin returns null in the contents of the file.
The reason for this is the "partial" permission implemented in Android 11, although the user gives the storage permission this always stays in "only multimedia content".
So what I did was send the user to the settings section of the app and tell them to change the storage permission to "allow access to all files".
And with that change I was already able to read the files in any directory on the device on Android 11.
Note:
You need to implement this in your config.xml for the storage permission to work well on Android 11

I hope and this will help you.

Tell the user to allow the app to access all files is not a good practice,
it poses potential security problems and it is exactly that which Google
was trying to avoid with the new policies on Android 11.
A sábado, 5/06/2021, 16:41, Joshua Eduardo González Ruíz <
@.***> escreveu:

Hello, I was facing a similar problem, when I needed to read a file from
the storage of an Android device, the plugin returns null in the contents
of the file.
The reason for this is the "partial" permission implemented in Android 11,
although the user gives the storage permission this always stays in "only
multimedia content".
So what I did was send the user to the settings section of the app and
tell them to change the storage permission to "allow access to all files".
And with that change I was already able to read the files in any directory
on the device on Android 11.
Note:
You need to implement this in your config.xml for the storage permission
to work well on Android 11
I hope and this will help you.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/apache/cordova-plugin-file/issues/426#issuecomment-855249720,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AA6M4DLMZZOLJK7STKAVVOLTRIZR7ANCNFSM4SAAWWGQ
.

Tell the user to allow the app to access all files is not a good practice, it poses potential security problems and it is exactly that which Google was trying to avoid with the new policies on Android 11. A sábado, 5/06/2021, 16:41, Joshua Eduardo González Ruíz < @.***> escreveu:
…

I know, but that was the only way I could get the process going. If I find any other way, I will gladly share it around here until there is an official form.

Hello, i found plugin for save image base64 in public directory, using api Media Store. Work in android 10 and 11.
I hope and this will help someone.
https://github.com/Heartade/cordova-plugin-android-mediastore

Hello, I was facing a similar problem, when I needed to read a file from the storage of an Android device, the plugin returns null in the contents of the file.
The reason for this is the "partial" permission implemented in Android 11, although the user gives the storage permission this always stays in "only multimedia content".
So what I did was send the user to the settings section of the app and tell them to change the storage permission to "allow access to all files".
And with that change I was already able to read the files in any directory on the device on Android 11.
Note:
You need to implement this in your config.xml for the storage permission to work well on Android 11
 <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
I hope and this will help you.

Unfortunately this is not working for me!
I try to write  a file from a html input tag. This is working with eg files from the Downloads-Folder. But it is not working with files from google drive. Setting the permission to accal all files  ("android.permission.MANAGE_EXTERNAL_STORAGE") does not change this behaviour. I get this error message:
DOMException: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.

Any update??

Hello, i found plugin for save image base64 in public directory, using api Media Store. Work in android 10 and 11.
I hope and this will help someone.
https://github.com/Heartade/cordova-plugin-android-mediastore
@HarelM @breautek
Maybe the solution is mediaStore

For android 10 and 11 that's probably true, but if you need to support 9 in one plugin it's hard...

This plugin is currently not compatible with cordova-android@10/nightly. So please only test using<EMAIL_ADDRESS>

Is it compatible with cordova-android@10 now? I tested it and it works for me so far.

@breautek please highlight your comment about the test fork you created, because people are not reading it since the thread is becoming too long.
Newcomers: test the following fork
https://github.com/apache/cordova-plugin-file/issues/426#issuecomment-830841544

@breautek any update?

@breautek please highlight your comment about the test fork you created, because people are not reading it since the thread is becoming too long.
Newcomers: test the following fork because it was made to overcome all the problems related with storage on Android 11 and beyond
cordova plugin add https://github.com/breautek/cordova-plugin-file/tree/feat/api30

and downgrade to cordova 9.1 if necessary (latest is 10)
npm i -g<EMAIL_ADDRESS>
More info here: #426 (comment)

but google not allowed android api 29 anymore we must have to update api level 30 which is only possible by cordova 10.0.0
so we must solved this issue to upload new apk in playstore.

I've read through this entire thread (trying to follow as much as I can), the Google docs on scoped storage, and other issues and stackoverflow posts and I'm still not 100% sure that this issue applies to me, so if anyone can tell me I'd really appreciate it...
For a feature I'm working on (involving opening a video, overlaying text on it with canvas, and capturing the output) I need the video and page to be same origin. So I've tried to use API30 and<EMAIL_ADDRESS>for the WebViewAssetLoader functionality to serve my app from https://localhost.
The problem I have is that after updating (from API29 / cordova-android@9.1), when I select a video from the photo library, using previously working code like this:
          navigator.camera.getPicture(
            file => {
              file = file.indexOf('file://') === 0 ? file : 'file://' + file
              window.resolveLocalFileSystemURL(file, (fileEntry) => {
                this.video = fileEntry.toInternalURL() // Vue.js data property that populates video src
              })
            },

            () => {},

            {
              destinationType: Camera.DestinationType.FILE_URI,
              sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
              mediaType: Camera.MediaType.VIDEO
            }
          )
        }

... I get an error like this in the console:
GET cdvfile://localhost/sdcard/DCIM/Camera/VID_20210802_200106.mp4 net::ERR_UNKNOWN_URL_SCHEME
If I go back to API29 /<EMAIL_ADDRESS>it works fine again.
Where I'm not sure if this is the same issue is from comments like this one by @HarelM https://github.com/apache/cordova-plugin-file/issues/426#issuecomment-751977160 :

@Vatsov Are you reading a file a user selects? If so I don't think there's an issue that is related to Android 11, or at least I hope not as you open the file picker and this should be OK even in Android 11 as far as I know.

...and things that I read in google's docs that seem to suggest that reading the users photo/video library is treated differently to accessing other types of file.
Regardless, I've still tried adding the MANAGE_EXTERNAL_STORAGE permission, but that hasn't seemed to have any effect.
So can anyone tell me if this is the same issue? Or is it a different one caused by the upgrade to API30 and/or<EMAIL_ADDRESS>?
To be honest I'm feeling a little out of my depth here and would very much like to get back to my cosy javascript safe place! :)
Thanks in advance for any help or suggestions!

@skmbr on my side the latest working versions of android and camera plugins are:
"cordova-android": "^9.1.0", "cordova-plugin-camera": "^5.0.2",

@skmbr on my side the latest working versions of android and camera plugins are:
"cordova-android": "^9.1.0", "cordova-plugin-camera": "^5.0.2",
I dont think that your issue is related with this. Its look like native Java code error from incompatible versions of cordova-android and cordova-plugin-camera. Try to check Java stacktrace in Android Studio logging.

Thanks @sytolk
The console error I posted is from the javascript console in chrome devtools after building my app in debug mode.
I assumed it wasn't anything to do with the camera plugin as that's returning the correct internal file path.  I thought cordova-plugin-file was responsible for resolveLocalFileSystemURL and cdvfile:// links?
Will I need to wait for cordova-android@10 support in cordova-plugin-camera ?

I've read through this entire thread (trying to follow as much as I can), the Google docs on scoped storage, and other issues and stackoverflow posts and I'm still not 100% sure that this issue applies to me, so if anyone can tell me I'd really appreciate it...
For a feature I'm working on (involving opening a video, overlaying text on it with canvas, and capturing the output) I need the video and page to be same origin. So I've tried to use API30 and<EMAIL_ADDRESS>for the WebViewAssetLoader functionality to serve my app from https://localhost.
The problem I have is that after updating (from API29 / cordova-android@9.1), when I select a video from the photo library, using previously working code like this:
          navigator.camera.getPicture(
            file => {
              file = file.indexOf('file://') === 0 ? file : 'file://' + file
              window.resolveLocalFileSystemURL(file, (fileEntry) => {
                this.video = fileEntry.toInternalURL() // Vue.js data property that populates video src
              })
            },

            () => {},

            {
              destinationType: Camera.DestinationType.FILE_URI,
              sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
              mediaType: Camera.MediaType.VIDEO
            }
          )
        }

... I get an error like this in the console:
GET cdvfile://localhost/sdcard/DCIM/Camera/VID_20210802_200106.mp4 net::ERR_UNKNOWN_URL_SCHEME
If I go back to API29 /<EMAIL_ADDRESS>it works fine again.
Where I'm not sure if this is the same issue is from comments like this one by @HarelM #426 (comment) :

@Vatsov Are you reading a file a user selects? If so I don't think there's an issue that is related to Android 11, or at least I hope not as you open the file picker and this should be OK even in Android 11 as far as I know.

...and things that I read in google's docs that seem to suggest that reading the users photo/video library is treated differently to accessing other types of file.
Regardless, I've still tried adding the MANAGE_EXTERNAL_STORAGE permission, but that hasn't seemed to have any effect.
So can anyone tell me if this is the same issue? Or is it a different one caused by the upgrade to API30 and/or<EMAIL_ADDRESS>?
To be honest I'm feeling a little out of my depth here and would very much like to get back to my cosy javascript safe place! :)
Thanks in advance for any help or suggestions!
I dont try this but i think you can solve your issue using this as suggest here
<preference name="AndroidInsecureFileModeEnabled" value="true" />

@krunalsk007

I dont try this but i think you can solve your issue using this as suggest here
<preference name="AndroidInsecureFileModeEnabled" value="true" />

No, that puts everything back to using file://
I need https://localhost and WebViewAssetLoader to solve my origin issues.

This is quite a thread. I was looking at it trying to figure out why I couldn't read a file after the user selects it from a system file picker. My issue is specifically for targeting API level 30, and my application was attempting to resolve a given URL to a path on the phone, then use a FileInputStream to read the file and write it into a my applications data. The solution to this problem is to call something like context.getContentResolver().openInputStream(uri) to read the file instead. I'm still working out how to connect everything, because right now cordova-android@10, cordova-plugin-filechooser, cordova-plugin-filepath, and cordova-plugin-file are all in use. Just putting this out there, even though it won't help anyone trying to figure out why they can't write files with scoped storage.

Hello, i found plugin for save image base64 in public directory, using api Media Store. Work in android 10 and 11.
I hope and this will help someone.
https://github.com/Heartade/cordova-plugin-android-mediastore

Thanks!
This seems to work for us.
In our ionic cordova app we want users to be able to download photos and videos stored in our protected app space to their media directory.
We have extended the repo downloading video files:
https://github.com/konnectappdev/cordova-plugin-android-mediastore

Hello,
I just want to share a solution I found to my problem as it's related to this thread.
Problem with SDK 30 :
Out team used the path externalApplicationStorageDirectory to store some assets of the application, wich are downloaded the first time the user connect to the application.
But we weren't able to access those files, and got an ACCESS_DENIED error.
Solution :
I made a little modification in CordovaActivity.java in init() function
`protected void init() {
appView = makeWebView();
createViews();
if (!appView.isInitialized()) {
appView.init(cordovaInterface, pluginEntries, preferences);
}
/********** MODIFICATION SDK 30 **********/
    WebView webView = (SystemWebView)(appView.getEngine().getView());
    WebSettings webSettings = webView.getSettings();
    webSettings.setAllowFileAccess(true);
/**********************************/

cordovaInterface.onCordovaInit(appView.getPluginManager());

// Wire the hardware volume controls to control media if desired.
String volumePref = preferences.getString("DefaultVolumeStream", "");
if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
    setVolumeControlStream(AudioManager.STREAM_MUSIC);
}

}`
you also need to add these import :
import android.webkit.WebView;
import android.webkit.WebSettings;
import org.apache.cordova.engine.SystemWebView;
Hope this can help some of you :)

Hello,
I just want to share a solution I found to my problem as it's related to this thread.
Problem with SDK 30 :
Out team used the path externalApplicationStorageDirectory to store some assets of the application, wich are downloaded the first time the user connect to the application.
But we weren't able to access those files, and got an ACCESS_DENIED error.
Solution :
I made a little modification in CordovaActivity.java in init() function
protected void init() {
    appView = makeWebView();
    createViews();
    if (!appView.isInitialized()) {
        appView.init(cordovaInterface, pluginEntries, preferences);
    }

    /********** MODIFICATION SDK 30 **********/
        WebView webView = (SystemWebView)(appView.getEngine().getView());
        WebSettings webSettings = webView.getSettings();
        webSettings.setAllowFileAccess(true);
    /**********************************/

    cordovaInterface.onCordovaInit(appView.getPluginManager());

    // Wire the hardware volume controls to control media if desired.
    String volumePref = preferences.getString("DefaultVolumeStream", "");
    if ("media".equals(volumePref.toLowerCase(Locale.ENGLISH))) {
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
    }
 }

you also need to add these import :
import android.webkit.WebView;
import android.webkit.WebSettings;
import org.apache.cordova.engine.SystemWebView;

Hope this can help some of you :)

I also want to include what Google says about setting setAllowFileAccess to true, for informational purposes:

setAllowFileAccess
Enables or disables file access within WebView. Note that this enables or disables file system access only. Assets and resources are still accessible using file:///android_asset and file:///android_res.
Note: Apps should not open file:// URLs from any external source in WebView, don't enable this if your app accepts arbitrary URLs from external sources. It's recommended to always use androidx.webkit.WebViewAssetLoader to access files including assets and resources over http(s):// schemes, instead of file:// URLs. To prevent possible security issues targeting Build.VERSION_CODES.Q and earlier, you should explicitly set this value to false.

I changed the android version in which the application is focused adding this tag in the config.xml:
<preference name="android-targetSdkVersion" value="28" />
And this change allows to save files into the device using on Android 8, Android 9, Android 10 and Android 11.
I did tests on Android emulator and physical devices working correctly.
The Android documentation recommends to change the API Level version to avoid the scoped storage feature on Android 10.
You get more information about scoped storage feature here:
https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage

I changed the android version in which the application is focused adding this tag in the config.xml:
<preference name="android-targetSdkVersion" value="28" />
And this change allows to save files into the device using on Android 8, Android 9, Android 10 and Android 11.
I did tests on Android emulator and physical devices working correctly.
The Android documentation recommends to change the API Level version to avoid the scoped storage feature on Android 10.
You get more information about scoped storage feature here:
https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage
I hope this information will be helpful.

This isn't a solution to anybody who wants to publish to the Google Play store as they require the target SDK to be set to >= 30.

I changed the android version in which the application is focused adding this tag in the config.xml:
<preference name="android-targetSdkVersion" value="28" />
And this change allows to save files into the device using on Android 8, Android 9, Android 10 and Android 11.
I did tests on Android emulator and physical devices working correctly.
The Android documentation recommends to change the API Level version to avoid the scoped storage feature on Android 10.
You get more information about scoped storage feature here:
https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage
I hope this information will be helpful.

This isn't a solution to anybody who wants to publish to the Google Play store as they require the target SDK to be set to >= 30.

You're right, it is necessary set API Level to 30 for Play Store but I forced the API Level to 30 testing again in Android 8, 9, 10 and 11 and everything worked fine.
This is the preference tag:
<preference name="android-targetSdkVersion" value="30" />
Also, I tested that an application with this tag could be uploaded to Play Store Console and the file was accepted.

You're right, it is necessary set API Level to 30 for Play Store but I forced the API Level to 30 testing again in Android 8, 9, 10 and 11 and everything worked fine.

When running your tests, did you uninstall the app and its data from your device first?
Should test the clean install use case, where a user has never had the app installed before.

You're right, it is necessary set API Level to 30 for Play Store but I forced the API Level to 30 testing again in Android 8, 9, 10 and 11 and everything worked fine.

When running your tests, did you uninstall the app and its data from your device first?
Should test the clean install use case, where a user has never had the app installed before.

Sure, I cleaned the data and uninstall the application before test in each Android version.

Hey guys any update on this I am not able to upload files from the download folder due to this storage change
I am getting file input stream in file transfer plugin to show progress other solutions like Media store API and SAF also not working for me

I am not sure which part of the cordova stack is responsible for this but setting an img src attribute to the file:// address of a stored image retrieved using this plugin and FileEntry.toURL() used to work fine. However now:
<img src="FileEntry.toURL()"> results in:

Not allowed to load local resource: file:///storage/emulated/0/Android/data/...

<img src="FileEntry.toInternalURL()"> results in:

Failed to load resource: net::ERR_UNKNOWN_URL_SCHEME

I think this is the same as the issue over on https://github.com/apache/cordova-plugin-camera/issues/761

Starting November 1, 2021, updates to apps and games on Google Play will be required to target Android 11 (API level 30) or higher.
I have tried to apply a couple of solutions from this thread but it didn't work.
Also, I've tried manually adding the needed permission but 'android.permission.MANAGE_EXTERNAL_STORAGE' don't appear in AndroidManifest.xml after I add to config.xml next part:
    <config-file file="app/src/main/AndroidManifest.xml" mode="merge" parent="/manifest" xmlns:android="http://schemas.android.com/apk/res/android">
      <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
    </config-file>

package.json
    "cordova-android": "^9.1.0",
    "cordova-js": "^5.0.0",

I don't think anything will work until this plugin uses the new scoped storage APIs. This issue has the label "Help wanted" so if anyone feels like getting their hands dirty with Java that would be great. My time would be better spent migrating to Capacitor.

Does anyone has a solution for the problem with SDKVersion 30?

Does anyone has a solution for the problem with SDKVersion 30?

I don't but I can confirm that Capacitor's Filesystem plugin works great and uses all the new Android APIs under the hood i.e. you get a content:// URL that you can actually use when storing your files.
The ground seems to be shrinking beneath Cordova's feet at an accelerating rate and there's no better time to migrate to Capacitor than now. It's really stable, is a pleasure to use and the migration should be fairly painless.

Anyone else who had a solution for that without capacitor?

I use externalDataDirectory instead of  externalRootDirectory.

externalDataDirectory

can you be more specific? can you show me your code?

@hanslbua
import { File } from '@ionic-native/file/ngx';
import { FileTransfer, FileTransferObject } from '@ionic-native/file-transfer/ngx';
.....
  constructor(
    private file: File,
    private transfer: FileTransfer,
  ) {
  }
....
  async download(url: string, fileName: string): Promise<string> {
    const directoryPath: string = this.isIOS ? this.file.documentsDirectory : `${this.file.externalDataDirectory}Download/`;
    const fileTransfer: FileTransferObject = this.transfer.create();
    const entry = await fileTransfer.download(url, `${directoryPath}${fileName}`);
    return entry.toURL();
  }
....

package.json
...
    "@ionic-native/file": "^5.15.0",
    "@ionic-native/file-transfer": "^5.30.0",
    "cordova-plugin-file": "^6.0.2",
    "cordova-plugin-file-transfer": "git+https://github.com/apache/cordova-plugin-file-transfer.git",
...

I addressed the issue where the application could not access a file shared into the application by the user by instead forking cordova-plugin-filepath: https://github.com/VIAVI-Solutions/cordova-plugin-filepath/commit/a4714209cfbd2bd8eebad902b8f5ef3e34fb8baa

Hey i have it like this:
window.resolveLocalFileSystemURL(file, resolveOnSuccess, function () { console.log("resolveLocalFileSystemURL Error"); });
function resolveOnSuccess(entry){
    //new file name

    let fileName = "test.jpg"
    let myFolderApp = "app_media";

    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSys) {
    //The folder is created if doesn't exist
    fileSys.root.getDirectory( myFolderApp,
                    {create:true, exclusive: false},
                    function(directory) {
                        entry.copyTo(directory, fileName,  successCopy, function () { console.log("copyTo Error"); });
                    }
                    , function () { console.log("getDirectory Error"); });
                    },
    function () { console.log("requestFileSystem Error"); });
}

Output is: getDirectory Error
Can anyone help me?

Starting November 1, 2021, updates to apps and games on Google Play will be required to target Android 11 (API level 30) or higher.
I have tried to apply a couple of solutions from this thread but it didn't work.
Also, I've tried manually adding the needed permission but 'android.permission.MANAGE_EXTERNAL_STORAGE' don't appear in AndroidManifest.xml after I add to config.xml next part:
    <config-file file="app/src/main/AndroidManifest.xml" mode="merge" parent="/manifest" xmlns:android="http://schemas.android.com/apk/res/android">
      <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
    </config-file>

package.json
    "cordova-android": "^9.1.0",
    "cordova-js": "^5.0.0",

Is it generating Android target: android-30 ?
because When I try to add "cordova-android": "^9.1.0" it shows Android target: android-29 not 30

Starting November 1, 2021, updates to apps and games on Google Play will be required to target Android 11 (API level 30) or higher.
I have tried to apply a couple of solutions from this thread but it didn't work.
Also, I've tried manually adding the needed permission but 'android.permission.MANAGE_EXTERNAL_STORAGE' don't appear in AndroidManifest.xml after I add to config.xml next part:
    <config-file file="app/src/main/AndroidManifest.xml" mode="merge" parent="/manifest" xmlns:android="http://schemas.android.com/apk/res/android">
      <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
    </config-file>

package.json
    "cordova-android": "^9.1.0",
    "cordova-js": "^5.0.0",

@Yura13  Is it generating Android target: android-30 ?
because When I try to add "cordova-android": "^9.1.0" it shows Android target: android-29 not 30

I believe that until this plugin code adapts to the new android 30 requirements the only clean option is to store only locally and transfer data from and to other plugins that have adapted(if possible). For example, get the images from the camera plugin in base64 and store locally(cordova.file.dataDirectory).

@Topiya
Yes
        <preference name="android-minSdkVersion" value="22" />
        <preference name="android-targetSdkVersion" value="30" />

@Topiya Yes. You probably need to change the next lines in config.xml:
        <preference name="android-minSdkVersion" value="22" />
        <preference name="android-targetSdkVersion" value="30" />

@Yura13  I have already added above lines in config.xml. But still getting Android target: android-29

Verify installed sdk version on your computer via Android SDK

Also, try to run the next commands in terminal:
ionic cordova platform rm android
ionic cordova platform add android@9

I have already added sdk as above.

 Has someone else the same problem? How can i fix this?

If I add cordova-android 10.1.0 then only it shows Android target: android-30 while add platform.
PS D:\Project> ionic cordova platform add<EMAIL_ADDRESS>
cordova.cmd platform add<EMAIL_ADDRESS>Using cordova-fetch for<EMAIL_ADDRESS>Adding android project...
Creating Cordova project for the Android platform:
Path: platforms\android
Package: test.test
Name: test
Activity: MainActivity
Android target: android-30

If I add cordova-android 10.1.0 then only it shows Android target: android-30 while add platform.
PS D:\Project> ionic cordova platform add<EMAIL_ADDRESS>
cordova.cmd platform add<EMAIL_ADDRESS>Using cordova-fetch for<EMAIL_ADDRESS>Adding android project...
Creating Cordova project for the Android platform:
Path: platforms\android
Package: test.test
Name: test
Activity: MainActivity
Android target: android-30

cant acess the file :/ in sdk 30

I addressed the issue where the application could not access a file shared into the application by the user by instead forking cordova-plugin-filepath: VIAVI-Solutions/cordova-plugin-filepath@a471420

@chrisjdev Thank you. This is a great workaround for the time being.

i still cant acess my file , im using 👍
window.FilePath.resolveNativePath('file:///data/user/0/events.staff.booking.app/cache/FB_IMG_1637505177709.jpg?', successCallback, errorCallback); function successCallback(s){  console.log("sucess:")+s;	 } function errorCallback(e){ console.log("error:")+e;
but the the file path isent acessible

i still cant acess my file , im using +1 window.FilePath.resolveNativePath('file:///data/user/0/events.staff.booking.app/cache/FB_IMG_1637505177709.jpg', successCallback, errorCallback); function successCallback(s){ console.log("sucess:")+s; } function errorCallback(e){ console.log("error:")+e;
but the the file path isent acessible , i get no acessible path only console log "sucess:"

After making the change to the FilePath.java in plugins folder, have you tried removing and adding the Android platform?

i did, but i just realise that if i run window.open on my path : "file:///data/user/0/events.staff.booking.app/cache/FB_IMG_1637505177709.jpg" .. the image opens ... i just cant get it to upload to my server dont know why i get upload code:1 ... using file-tranfere plugin ...

Addressing a few comments over the past few days:

but the the file path isent acessible , i get no acessible path only console log "sucess:"

You're only getting "success: because that's the only thing you're logging out.
Instead of doing: console.log("sucess:")+s; I think you mean:
console.log("sucess:", s);

cant acess the file :/ in sdk 30

The root / folder is protected. You can only access files under your application internal directory (See the File system directory table. Anything that isn't explicitly marked as external in this table are internal directories.

Not allowed to load local resource: file:///storage/emulated/0/app_media/1637263763703.jpg

Starting with cordova-android@10, cordova-android uses WebViewAssetLoader by default and disables local file system access as recommended by the android docs. This is because this option is deemed insecure, but was a critical component that Cordova that depended on with on other alternative until Android introduced the WebViewAssetLoader. Using the WebViewAssetLoader also overcomes some other same-origin issues as requests appear to come from a true origin rather than the filesystem which has a null origin. More information on this change in our blog release notes.
This also means you can't use use file system urls directly like before. I think I might have to make a blog post about this but you can still read the file (assuming you have access to that file) as a blob and use Object URLs.
Alternatively, if you want the previous cordova-android behaviour, you can enable the AndroidInsecureFileModeEnabled preference in your config.xml file.
This is not related to the cordova-plugin-file, so I would respectfully ask to refrain from posting more on this. If you have further questions on this, you can reach reach out to our Slack community. (Link is a temporary link since the main link is down at the time of writing...)

MANAGE_EXTERNAL_STORAGE

Note that adding the MANAGE_EXTERNAL_STORAGE permission may work for you but it also may limit your ability to deploy to the Google Play store. This permission will require justification and Google reserves the right to block your app from the play store if they feel like your app don't need this permission.

I have added AndroidInsecureFileModeEnabled in config.xml but it is not woking for me.

@breautek I have added AndroidInsecureFileModeEnabled in config.xml but it is not woking for me.

Thank you for the explanation @breautek. My issue was that I wasn't using WebViewAssetLoader. Simply replacing file:// with https://localhost in my JavaScript allowed me to access my files from the webview in Android 11.
@brunoalex are you able to access https://localhost/data/user/0/events.staff.booking.app/cache/FB_IMG_1637505177709.jpg in your JavaScript?

please let me know I'm also facing same issue for android 11 device (Cordova android@10 ).
code :
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory, function(dir) {
dir.getFile("test.pdf", {create:true}, function(file) {
file.createWriter(function(fileWriter) {
fileWriter.write("byteArrays data");
alert('Message', 'File Downloaded Successfully');
}, function(){
alert('Error!', "Unable to save the file");
});
},function(e){
alert('Error!', e);
});
},function(e){
console.log(e);
});
I tried with below code in Androidmainfest file

Config.xml file

I tried with below code in Androidmainfest file

Config.xml file

@alex-steinberg  i can acess the file, but the funtions i used to upload the file after selecting from the galery to the server arent working in sdk30..  "ft.upload(imageURI, server, function(r)" ...

@alex-steinberg i can acess the file, but the funtions i used to upload the file after selecting from the galery to the server arent working in sdk30.. "ft.upload(imageURI, server, function(r)" ...

I have a similar problem.. my app downloads and unzips images in ///storage/emulated/0/Android/data/myappid/files/ but the app cannot access the images by javascript or by code ex. <img src="///storage/emulated/0/Android/data/myappid/files/image.png">
looks very confused

we are facing a similar issue where existing cordova implementation to read a file from downloads folder is working but not in android 11. Have tried all possible changes suggested to use fileentry object to get reference and then copy to a location where application has access to but read itself is failing. Cordova version used is 9.x and cordova-file-plugin has been used to read file. Required feature is to allow user to select a file from device and upload it. Upload to server is in base64 format. Kindly advise

we are facing a similar issue where existing cordova implementation to read a file from downloads folder is working but not in android 11. Have tried all possible changes suggested to use fileentry object to get reference and then copy to a location where application has access to but read itself is failing. Cordova version used is 9.x and cordova-file-plugin has been used to read file. Required feature is to allow user to select a file from device and upload it. Upload to server is in base64 format. Kindly advise
@alex-steinberg - can you please share your code snippet and clarify whether you are using cordova 9 or cordova 10?

@alex-steinberg - We have set targetSdkVersion to 30 in config.xml file and created build using platform 9.1.0.
Below code works fine in lower android version (till android 10). In android 11 while reading a pdf file from download loaction, we get error as GET file:///storage/emulated/0/Download/Test.pdf net::ERR_ACCESS_DENIED
this.fileChooser.open()
.then(uri => {
let fileName = null;
let fileExtension = null;
let fileType = null;
let isvalid = true;
this.filePath.resolveNativePath(uri)
.then(filePath => {
return this.base64EncodeFile(filePath);
}).then((base64Data: any)=>{
console.log(base64Data);
})
.catch(err => console.log(err));
})
.catch(e => console.log(e));
}
base64EncodeFile(src){
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.onload = function () {
let reader = new FileReader();
reader.onloadend = function () {
let result = {
base64: reader.result,
size: xhr.response.size,
type: xhr.response.type
}
resolve(result);
}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', src);
xhr.responseType = 'blob';
xhr.send();
});
}
We have also tried below code
To copy from download folder to internal storage. It gives {"code":1,"message":"NOT_FOUND_ERR"} error
pathsrc : file:///storage/emulated/0/Download/Test.pdf
fileName : Test.pdf
this.file.copyFile(pathsrc, fileName, this.file.dataDirectory, fileName).
then((result) => { console.log('file copied :' + result);
}).catch(err => console.log("copied err "+ JSON.stringify(err)));;
Below code is using copyTo method, it gives error as FileError {code: 1}
window.resolveLocalFileSystemURL(pathsrc, function (fileSystem) {
fileSystem.getFile(fileName, {
create: false, // try true first to make sure that there are no other problems around
exclusive: false
}, function (fileEntry) {
window.resolveLocalFileSystemURL(window.cordova.file.externalDataDirectory , function (newFileEntry) {
fileEntry.copyTo(newFileEntry, fileName, function (result) {
console.log("save successfully:", result);
}, function (err) {
console.log("err-fileEntry.copyTo: ",err);
});
}, function (err) {
console.log("err-window.resolveLocalFileSystemURL: ",err);
});
}, function (err) {
console.log("err-fileSystem.getFile: ",err);
});
}, function (err) {
console.log("err-resolveLocalFileSystemURL: ",err);
});

#please let me know I'm also facing same issue for android 11 device (Cordova android@10 ). code :
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory, function(dir) { dir.getFile("test.pdf", {create:true}, function(file) { file.createWriter(function(fileWriter) { fileWriter.write("byteArrays data"); alert('Message', 'File Downloaded Successfully'); }, function(){ alert('Error!', "Unable to save the file"); }); },function(e){ alert('Error!', e); }); },function(e){ console.log(e); });
https://stackoverflow.com/questions/70110433/cordova-file-externalrootdirectory-not-working-in-android-11-devices

Have you got a solution to this @Maheshvy

I am trying to write file using following code:
this.file.checkDir(this.directorypath, 'AppFolder').then((dirExists) => {
        this.file.writeFile(this.downloadDirectory, update_file + '.'+saveExtention, fileData, { replace: true }).then((fileEntry) => {
          let mimeTestType = mimeType;
          let path = fileEntry.nativeURL
          this.openingFile(path, mimeTestType)
          resolve(true)
        }).catch((ex) => {
          resolve(true)
            console.error('Error')
        })
      }, (response) => {
        this.file.createDir(this.directorypath, 'AppFolder', true).then((response) => {
          this.file.writeFile(this.downloadDirectory, update_file + '.'+saveExtention, fileData, { replace: true }).then((fileEntry) => {
            let mimeTestType = mimeType;
            let path = fileEntry.nativeURL
            this.openingFile(path, mimeTestType)
            resolve(true)
          }).catch((ex) => {
            resolve(true)
            console.error('Error')
          })
        })
      })
      });

This works till android 10 but not in android 11, it throws error.

This simple example app should shed some light on some of the issues raised here. At the very least, it would make a good place to continue the conversation around using files on Android 11 Cordova apps since, as has been mentioned, the problems experienced here aren't caused by the cordova-plugin-file plugin. cc @siddharthaPatil @danicarla @brunoalex

With<EMAIL_ADDRESS>and<EMAIL_ADDRESS>tested on android version from 12 to 8 on emulator, all version working.
config.xml
<widget ..... xmlns:android="http://schemas.android.com/apk/res/android">

...

        <edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
            <application android:usesCleartextTraffic="true" android:requestLegacyExternalStorage="true"/>
        </edit-config>
...

window.resolveLocalFileSystemURL, DirectoryEntry.getFile(....) together with fileEntry.createWriter can write to xx/Download/yyy successfully on every android version.
Without android:requestLegacyExternalStorage="true", API 29 ( android 10 ) would fail to write, other version still work.

Add code below to AndroidManifest.xml, and you can write file to storage root directory like cordova.file.externalRootDirectory:
<manifest xmlns:tools="http://schemas.android.com/tools">
     ...
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
     <application android:requestLegacyExternalStorage="true">
     ...
</manifest>

Tested on Android 11

Add code below to AndroidManifest.xml, and you can write file to storage root directory like cordova.file.externalRootDirectory:
<manifest xmlns:tools="http://schemas.android.com/tools">
     ...
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
     <application android:requestLegacyExternalStorage="true">
     ...
</manifest>

Tested on Android 11

This isn't standard behaviour or it works because the build isn't targeting API 30 or later as the Android docs explicitly states WRITE_EXTERNAL_STORAGE when targeting API 30 / Android 11 or later. Additionally when targeting API 30, scoped storage is enforced and requestLegacyExternalStorage is ignored.

@breautek I was reading now your post upwards, too many characters to go through :) and reading also your blog post
Ok, file:// is supposed to be insecure, but my previous question remains: is not possible to adapt the keys inside cordova.file to https://localhost? What am I missing?

@jfoclpf You're using a jquery API that presumably uses an XHR request to fetch data from a file:// url. This API is not related to the file plugin at all.
The webview doesn't have direct access to file:// unless if AndroidInsecureFileModeEnabled preference is enabled. Enabling this will make Cordova uses the same filesystem based strategy for the webview, which is the same behaviour in cordova-android@9 and older. If AndroidInsecureFileModeEnabled is off (the default value), then the webview is handled through a WebViewAssetLoader, which is a way of treating your web app as if it was being loaded from a secure https site, enabling features that requires a secure context. But this also means that the webview and standard browser features don't have direct access to the file system.
While the browser itself doesn't have direct access to the FS, native plugins still does, so if you used the actual cordova plugin API, cordova.file.applicationDirectory + 'www/json/anomalies.json will be readable.

mentions replacing file:// by http://localhost. Does this work? If yes, shouldn't cordova.file.applicationDirectory be updated accordingly?

http://localhost points to the asset directory, which is effectively the equivalent of file://android_asset/www. The http:// version of the path will work in the webview and other browser APIs. But your real issue is that you're mixing APIs together. No, cordova.file.applicationDirectory shouldn't be updated because that's part of the File plugin API, which still uses the file:// protocol, as that is what used on the native side. The cordova.file.applicationDirectory + 'www/json/anomalies.json' path will resolve into a proper path that the file plugin can read.
Here's an untested example (may have syntax errors or a function name might be wrong, going off memory...)
resolveLocalFilesystemURL(cordova.file.applicationDirectory + 'www/json/anomalies.json', (fileEntry) => {
    fileEntry.file((file) => {
        let reader = new FileReader();
        reader.onloadend = () => {
            console.log('JSON String', reader.result);
        };

        reader.readAsText(file);
    });
}, (error) => {
    console.error('resolve error', error);
});

If you're using the WebViewAssetLoader, the latest version of this plugin as a fileEntry.toURL() API that also produces a http(s):// friendly URLs for standard browser APIs to consume.

Thank you @breautek for the clarification. Indeed for now I set AndroidInsecureFileModeEnabled on and it works just fine and I uploaded the AAB file to Google Play Console (with SDK 30-33) and it didn't complain (no errors nor warnings). But I guess it just a question of time.
I will later remove AndroidInsecureFileModeEnabled (off) and I will test it considering http://localhost is the equivalent of file://android_asset/www and using your piece of code.
Thanks again

Came to this. issue repeatedly when searching for a way to fix opening files on Android that broke since skd 29.
Updated my utillto make it work for devices running Android 11 with sdk 33.
export async function openFile(remoteUrl: string, fileName: string, mimeType: string) {
  if (!isCordova(window)) {
    window.open(remoteUrl, '_blank');
    return;
  }

  const transfer = FileTransfer.create();
  const extension = mime.extension(mimeType);
  const devicePath = `${File.dataDirectory}${fileName}.${extension}`;

  await transfer.download(remoteUrl, devicePath);
  const fileEntry = await File.resolveLocalFilesystemUrl(devicePath);
  await FileOpener.open(fileEntry.nativeURL, mimeType);
}


print(shape.auto_shape_type) gives KeyError: 'line'

Hello!
I have the following code:
from pptx import Presentation
from pptx.enum.shapes import MSO_SHAPE_TYPE

prs = Presentation("mcve.pptx")
for slide in prs.slides:
    for shape in slide.shapes:
        if shape.shape_type == MSO_SHAPE_TYPE.AUTO_SHAPE:
            print("Is this the problem {}".format(shape.auto_shape_type))

Running on mcve.pptx (1 slide, 2 shapes)
I would expect it to print out a string. However it fails with keyerror:
bash-3.2$ python3 mcve.py
Traceback (most recent call last):
  File "mcve.py", line 9, in <module>
    print("Is this the problem {}".format(shape.auto_shape_type))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pptx/shapes/autoshape.py", line 302, in auto_shape_type
    return self._sp.prst
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pptx/oxml/shapes/autoshape.py", line 303, in prst
    return prstGeom.prst
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pptx/oxml/xmlchemy.py", line 244, in get_attr_value
    return self._simple_type.from_xml(attr_str_value)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pptx/enum/base.py", line 198, in from_xml
    return cls._xml_to_member[xml_val]
KeyError: 'line'
bash-3.2$

Is this the expected behaviour? It occurs in 0.6.18 and 0.6.21.

Do you mean 0.6.21 rather than 0.6.12? Even 0.6.18 is over two years old.

Do you mean 0.6.21 rather than 0.6.12? Even 0.6.18 is over two years old. 0.6.21 is the latest.

Thank you. Fixed :)

Ah. I see why the noseless smiley. :-)
But that doesn't mean your problem's gone away. (And I can't solve it.)

@joereddington "line" is not a valid auto-shape type. It is however a valid connector type.
Next step would be to understand the provenance of that particular shape and perhaps have a look at its XML with print(shape._sp.xml).