apache / cordova-android

Apache Cordova Android
https://cordova.apache.org/
Apache License 2.0
3.59k stars 1.52k forks source link

Failed to load assets image with Version > 10 #1316

Closed EinfachHans closed 3 months ago

EinfachHans commented 2 years ago

Bug Report

Problem

I'm currently migrating my project to cordova-android@10. In my Project i'm using Googlemaps and Markers that icons are in my assets Folder. These are not working anymore and displays the default Google Map Marker.

Information

I see the following Error when i run my App via debugger:

E/AsyncLoadImage: can not connect to https://localhost/assets/imgs/map/mapPinGreen/mapPinGreen.png
    java.net.ConnectException: Failed to connect to localhost/127.0.0.1:443
        at com.android.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:147)
        at com.android.okhttp.internal.io.RealConnection.connect(RealConnection.java:116)
        at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:186)
        at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128)
        at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
        at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289)
        at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:411)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:542)
        at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:106)
        at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:30)
        at plugin.google.maps.AsyncLoadImage.doInBackground(AsyncLoadImage.java:299)
        at plugin.google.maps.AsyncLoadImage.doInBackground(AsyncLoadImage.java:27)
        at android.os.AsyncTask$3.call(AsyncTask.java:394)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:305)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)

Maybe the migration from the whitelist Plugin is not correctly working?

Environment, Platform, Device

Google Pixel 4, Android 10

Version information

Ionic:

   Ionic CLI                     : 6.16.3 (/usr/local/lib/node_modules/@ionic/cli)
   Ionic Framework               : @ionic/angular 5.6.12
   @angular-devkit/build-angular : 12.1.4
   @angular-devkit/schematics    : 12.1.4
   @angular/cli                  : 12.1.4
   @ionic/angular-toolkit        : 4.0.0

Cordova:

   Cordova CLI       : 10.0.0
   Cordova Platforms : android 10.0.1
   Cordova Plugins   : cordova-plugin-ionic-keyboard 2.2.0, cordova-plugin-ionic-webview 5.0.0, (and 50 other plugins)

Utility:

   cordova-res (update available: 0.15.3) : 0.15.1
   native-run (update available: 1.4.0)   : 1.0.0

System:

   Android SDK Tools : 25.2.3 (/Users/hanskrywalsky/Library/Android/sdk)
   ios-deploy        : 1.10.0
   ios-sim           : 8.0.2
   NodeJS            : v14.15.4 (/usr/local/bin/node)
   npm               : 7.17.0
   OS                : macOS Big Sur
   Xcode             : Xcode 12.5.1 Build version 12E507

Checklist

breautek commented 2 years ago

ooph...

I think this might be because native code is external from the webview, and localhost server doesn't actually exist to anything outside the webview (I think...). Not 100% sure, but a theory.

There are two workarounds you can try...

Remapping https:// paths to local file:// paths

Anything you tell google maps to load can probably be remapped from https://localhost/assets/imgs/map/mapPinGreen/mapPinGreen.png to file:///android_assets/imgs/map/mapPinGreen/mapPinGreen.png

Basically replace https://localhost/assets with file:///android_assets/

Not really sure if this will work, but it's worth a try.

Opting out of WebAssetLoader

You can tell cordova to opt out of using the WebAssetLoader system by enabling the AndroidInsecureFileModeEnabled preference, which will make it use file based paths just like cordova-android@9. I'm pretty sure this will work.

EinfachHans commented 2 years ago

Currently i'm giving the Google Maps Plugin a Url like ./assets/imgs/...someFile.png and i'm unsure about what is done then. To make iOS work and as i don't want to adjust all icons to android vs iOS the first option is not an option for me.

The second option, setting the AndroidInsecureFileModeEnabled Preference, seems to work, but does this will have any side effects?

Is this something that the Google Maps Plugin has to fix/migrate?

erisu commented 2 years ago

Cordova Android 10.x by default will use scheme+hostname. And Cordova-iOS 6.x I believe will always use scheme+hostname.

The scheme can not match exactly because of limitations between each platform but the following example should work.

But needs to be tested as I do not use this plugin.

Setup:

Let’s say you have an image uploaded at this directory path.

<project-root>/www/assets/image.png

WebView Paths:

With Cordova iOS 6.x default settings, the path would be: app://localhost/assets/image.png.

With Cordova Android 10.x and default settings, the path would be: https://localhost/assets/image.png

Recommendation:

Would be recommended to pass in the absolute path, /assets/image.png.

You might also be able to use //localhost/assets/image.png. With this pattern is should automatically detect the scheme but the hostname setting must be identical.

Catches:

Now, not everyone can use the https protocol because they might load external third party resources or api requests to the http scheme and these can’t mix. At least https, a secure protocol can not call and insecure protocol. If you are faced with this issue, in case of Android, you can change the scheme to http.

Now for the AndroidInsecureFileModeEnabled setting… This preference is for people who want to revert to the file protocol which 9.x and earlier used. It’s not recommended to serve from the file protocol as there can be security concerns. But depending on how the app is written and if the third party scripts is coming from a trusted source, and you know everything that is executed, then maybe it’s less of a problem.

First try the above recommendation: Remove the . and make your path absolute, with default android and iOS settings for scheme+hostname, and see how that turns out.

EinfachHans commented 2 years ago

Passing the absoulte Path like /assets/image.png or like //localhost/assets/image.png both doesn't work:

W/System.err: java.io.FileNotFoundException: /assets/imgs/map/mapPinGreen/mapPinGreen.png: open failed: ENOENT (No such file or directory)
W/System.err: java.io.FileNotFoundException: /localhost/assets/imgs/map/mapPinGreen/mapPinGreen.png: open failed: ENOENT (No such file or directory)
erisu commented 2 years ago

Sorry, I was mistaken about where the error was. I thought it was from the WebView side but, I see it was the native side of the plugin.

It will need to be the path that @breautek suggested.

file:///android_assets/imgs/map/mapPinGreen/mapPinGreen.png

But, this path will not work for iOS.

Try his other suggestion, to confirm that it will work: AndroidInsecureFileModeEnabled

Maybe the correct solution would involve a change/feature request to the plugin developers. Since you are passing the path to the plugin, and the plugin is trying to fetch the file with native code, maybe the plugin should accept various string values and determine the correct path.

E.g. Various String Values:

Maybe, there could be something implemented on our platform's code to help translate the file paths to native paths, etc, but then it would include app developers updating the app code and wrap file paths. For some reason, I would feel that this isn't the best solution compared with the plugin change.

@breautek any thoughts?

EinfachHans commented 2 years ago

Yeah the AndroidInsecureFileModeEnabled Preference seems to work so far. I think at the end of the day the plugin needs to update this 🤔

breautek commented 2 years ago

Thanks for confirming, i think my suspicions are accurate.

When using the web asset loader, the only thing that understands the https://localhost url is the webview itself. Anything external won't be able to use this url.

I think at the end of the day the plugin needs to update this

I think so too, but I'm not quite sure what the solution would be. Native could probably parse the url and transform it to a regular file:// url. I think the cordova plugin class could also provide this url transform implementation. I think this is what @erisu was hinting but wasn't quite recommending. Obviously the problem is plugins will need to be updated, but I'm not sure if there is a way around that.

Do you have a similar issue with iOS? I have one app that uses gmaps & custom markers, but it hasn't been updated. If necessarily I can find time to get the latest platforms installed to confirm

EinfachHans commented 2 years ago

iOS works fine with latest cordova-ios version

ebhsgit commented 2 years ago

@EinfachHans I've made a fix for myself. I've created PR https://github.com/mapsplugin/cordova-plugin-googlemaps/pull/2887 if you want to use it.

nanaykubo commented 4 months ago

@EinfachHans I've made a fix for myself. I've created PR mapsplugin/cordova-plugin-googlemaps#2887 if you want to use it.

This helps

and include to install this also

https://www.npmjs.com/package/cordova-plugin-file/v/8.0.1

jcesarmobile commented 3 months ago

This is something the plugin needs to handle, not a cordova-android bug