apache / cordova-plugin-file

Apache Cordova File Plugin
https://cordova.apache.org/
Apache License 2.0
742 stars 762 forks source link

Storage updates in Android 11 #426

Closed HarelM closed 2 years ago

HarelM commented 4 years ago

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?

pawan-logiciel commented 3 years ago

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

ChrisHSandN commented 3 years ago

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

Yura13 commented 3 years ago

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",
ivanolarocca commented 3 years ago

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",
  1. Set "android-targetSdkVersion" to "30"
  2. Update "cordova-version" from 9 to 10
  3. Set the preserveLegacyExternalStorage flag to true to preserve the legacy storage model so that your app can migrate a user's data when they upgrade to the new version of your app that targets Android 11. `
    </edit-config>` [(source)](https://developer.android.com/training/data-storage/use-cases#if_your_app_targets)
  4. Check latest cordova plugins update and fixs to support new android sdk target and platform (cordova-android 10 release highlights)
alex-steinberg commented 3 years ago

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.

hanslbua commented 3 years ago

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

alex-steinberg commented 3 years ago

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 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.

hanslbua commented 3 years ago

Anyone else who had a solution for that without capacitor?

Yura13 commented 3 years ago

I use externalDataDirectory instead of externalRootDirectory.

hanslbua commented 3 years ago

I use externalDataDirectory instead of externalRootDirectory.

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

Yura13 commented 3 years ago

@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",
...
chrisjdev commented 3 years ago

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

hanslbua commented 3 years ago

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?

Topiya commented 3 years ago

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

harisrnd commented 3 years ago

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).

Yura13 commented 3 years ago

@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" />
Topiya commented 3 years ago

@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

Yura13 commented 3 years ago

Verify installed sdk version on your computer via Android SDK image

Also, try to run the next commands in terminal:

ionic cordova platform rm android
ionic cordova platform add android@9
Topiya commented 3 years ago

Inkedandroid-studio_LI

I have already added sdk as above.

hanslbua commented 3 years ago

With Android 10 save to persistant has worked but i get this error:

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

Not allowed sounds for me like a permission problem. Has someone else the same problem? How can i fix this?

Topiya commented 3 years ago

If I add cordova-android 10.1.0 then only it shows Android target: android-30 while ionic cordova platform add android.

PS D:\Project> ionic cordova platform add android@10.1.0

cordova.cmd platform add android@10.1.0 Using cordova-fetch for cordova-android@10.1.0 Adding android project... Creating Cordova project for the Android platform: Path: platforms\android Package: test.test Name: test Activity: MainActivity Android target: android-30

brunoalex commented 3 years ago

cant acess the file :/ in sdk 30

sachithd commented 3 years ago

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.

brunoalex commented 3 years ago

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 get no acessible path only console log "sucess:"

sachithd commented 3 years ago

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?

brunoalex commented 3 years ago

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 ...

breautek commented 3 years ago

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.

Topiya commented 3 years ago

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

alex-steinberg commented 3 years ago

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?

Maheshvy commented 3 years ago

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

brunoalex commented 3 years ago

@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)" ...

danicarla commented 3 years ago

@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

siddharthaPatil commented 2 years ago

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); });

jsBiztech commented 2 years ago

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.

alex-steinberg commented 2 years ago

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

JaosnHsieh commented 2 years ago

With cordova@10.0.0 and cordova-plugin-file@6.0.2, 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.

nailnafir commented 2 years ago

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>

You can use this method:

var folderpath = cordova.file.externalRootDirectory + "Download";
  window.resolveLocalFileSystemURL(folderpath, function (dir) {
    dir.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
      fileEntry.createWriter(function (writer) {
        writer.onwrite = function (writing) {
          console.log("WRITING...");
        };
        writer.write(fileData);
        console.log("SUCCESS");
      }, function () {
        console.log("FAIL");
      });
    });
  });

Tested on Android 11

breautek commented 2 years ago

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.

jfoclpf commented 1 year ago

This is becoming a bit messy :)

From updating from SDK v30 upwards the APP breaks exactly by merely reading the file system (cordova v11 and cordova-android v10.1.2)

jquery.min.js:2 Not allowed to load local resource: file:///android_asset/www/json/anomalies.json
$.getJSON(cordova.file.applicationDirectory + 'www/json/anomalies.json', function (data) {
   console.log(data)
})

1 - @breautek just confirm me please, if I remember correctly the issue regarding the transition from SDK 29 upwards was merely related with writing content to the device file system, right?

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

My package.json:

```json { "name": "in-my-district", "displayName": "In my District!", "version": "1.2.25", "description": "Report to your municipality certain irregularities in your neighborhood or district", "homepage": "https://nomeubairro.app/", "android-version-code": 10225, "scripts": { "test": "standard && node test/convertHbsToHtml.js && html-validate test/www/index.html && printf '\\n\\nTest OK\\n\\n'", "version": "cordova-set-version --version ${npm_package_version} && git add config.xml && node scripts/setAndroidVersionCode.js -v ${npm_package_version} && git add package.json", "push-version": "git push && git push origin v${npm_package_version}", "build-release-apk": "npm test && scripts/buildReleaseAPK.sh", "build-release-aab": "npm test && scripts/buildReleaseAAB.sh", "run-release-on-device": "scripts/runReleaseOnDevice.sh", "run-debug-on-device": "adb get-state 1>/dev/null 2>&1 && echo 'DEVICE ATTACHED' || { echo 'No device attached'; exit 1; } && cordova run android --device --debug", "regenerate-png-files": "./res/icon/generateIcons.sh && ./res/screen/generateScreens.sh && cp res/icon/android/512.png www/img/logo.png && cp res/icon/android/512.png ../fastlane/metadata/android/en-US/images/icon.png && git add res/* www/img/logo.png ../fastlane/metadata/android/en-US/images/icon.png && git commit -m 'PNGs regenerated'" }, "repository": { "type": "git", "url": "git://github.com/jfoclpf/in-my-district.git" }, "author": "João Pimentel Ferreira", "license": "GPL-3.0", "dependencies": { "@fortawesome/fontawesome-free": "6.2.1", "async": "^3.2.4", "bootstrap": "^5.2.3", "cordova": "^11.1.0", "cordova-browser": "^6.0.0", "cordova-import-npm": "^1.0.35", "cordova-pdf-generator": "^2.1.1", "cordova-plugin-cache-clear": "^1.3.8", "cordova-plugin-device": "^2.1.0", "cordova-plugin-geolocation": "^4.1.0", "cordova-plugin-inappbrowser": "^5.0.0", "cordova-plugin-is-debug": "^1.0.0", "cordova-plugin-screen-orientation": "^3.0.2", "cordova-plugin-simple-image-resizer": "0.2.0", "cordova-plugin-splashscreen": "^6.0.2", "cordova-plugin-statusbar": "^3.0.0", "cordova-plugin-whitelist": "^1.3.4", "cordova-set-version": "^13.0.1", "crypto-js": "^4.1.1", "es6-promise-plugin": "^4.2.2", "exif-js": "^2.3.0", "express-handlebars": "^6.0.6", "fs": "0.0.1-security", "jAlert": "^4.9.1", "jquery": "^3.6.3", "leaflet": "^1.9.3", "leaflet-image": "^0.4.0", "leaflet.markercluster": "^1.5.3", "path": "^0.12.7", "whitelist": "^1.0.2" }, "cordova": { "plugins": { "cordova-plugin-geolocation": { "GPS_REQUIRED": "true" }, "cordova-plugin-email-composer": { "ANDROID_SUPPORT_V4_VERSION": "27.+" }, "cordova-plugin-statusbar": {}, "cordova-plugin-screen-orientation": {}, "cordova-plugin-device": {}, "cordova-plugin-whitelist": {}, "cordova-pdf-generator": {}, "cordova-plugin-splashscreen": {}, "cordova-plugin-inappbrowser": {}, "cordova-plugin-is-debug": {}, "cordova-plugin-androidx-adapter": {}, "cordova-plugin-file": { "ANDROIDX_WEBKIT_VERSION": "1.4.0" }, "cordova-plugin-camera": { "ANDROID_SUPPORT_V4_VERSION": "27.+", "ANDROIDX_CORE_VERSION": "1.6.+" }, "cordova-plugin-network-information": {}, "cordova-plugin-app-version": {}, "@globules-io/cordova-plugin-ios-xhr": {}, "cordova-plugin-simple-image-resizer": { "ANDROID_EXIFINTERFACES_VERSION": "27.+" } }, "platforms": [ "ios", "android" ] }, "devDependencies": { "@globules-io/cordova-plugin-ios-xhr": "github:globules-io/cordova-plugin-ios-xhr", "command-line-args": "^5.2.1", "cordova-android": "^10.1.2", "cordova-plugin-androidx-adapter": "^1.1.3", "cordova-plugin-app-version": "^0.1.14", "cordova-plugin-camera": "^6.0.0", "cordova-plugin-email-composer": "github:jfoclpf/cordova-plugin-email-composer#pr-Fix_361-Android_11_support", "cordova-plugin-file": "^7.0.0", "cordova-plugin-network-information": "^3.0.0", "fs-extra": "^11.1.0", "handlebars": "^4.7.7", "html-minifier": "^4.0.0", "html-validate": "^7.13.1", "npm-check-updates": "^16.6.2", "semver": "^7.3.8", "standard": "^17.0.0", "uglify-js": "^3.17.4", "uglifycss": "0.0.29", "walk": "^2.3.15", "xml2js": "^0.4.23" }, "standard": { "ignore": [ "www/js/res/*", "test/www/*", "platforms/", "plugins/" ] } } ```

config.xml:

```xml No meu Bairro! Comunique ao seu município anomalias no seu bairro, como buracos na calçada ou lixo por recolher João Pimentel Ferreira Necessita da sua localização para indicar o endereço da ocorrência a ser enviada ao seu município Necessita da sua localização para indicar o endereço da ocorrência a ser enviada ao seu município Necessita da sua localização para indicar o endereço da ocorrência a ser enviada ao seu município Necessita da sua localização para indicar o endereço da ocorrência a ser enviada ao seu município Necessita da acesso à câmera para tirar fotos às anomalias Necessita da acesso à galeria, caso você já tenha tirado anteriormente a foto à ocorrência Necessita da acesso à galeria para guardar algumas fotos, mesmo que temporariamente ```
jfoclpf commented 1 year ago

@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?

breautek commented 1 year ago

@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.

Helaers commented 1 year ago

Came to this issue repeatedly when searching for a way to fix opening files on Android that broke since skd 29. Updated my util to 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);
}