Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to work with cordova-android@7.0.0 #729

Closed
tombell opened this issue Dec 7, 2017 · 43 comments
Closed

Update to work with cordova-android@7.0.0 #729

tombell opened this issue Dec 7, 2017 · 43 comments

Comments

@tombell
Copy link

tombell commented Dec 7, 2017

The android platform directory changed structure with 7.0.0, so some things like the libs/ directory is ending up in the wrong place. Even if I copied the libs into the right directory, there were additional errors of things not being found, when trying to build.

@brodycj
Copy link
Contributor

brodycj commented Dec 8, 2017

Marked as a bug, thanks for reporting. Hoping to resolve this in the near future. Until then cordova-android@7 should NOT be considered supported.

P.S. The fundamental issue is a problem with adding JAR and NDK library files due to wrong plugin.xml tag used.

@tombell
Copy link
Author

tombell commented Dec 8, 2017

Awesome, thanks!

brodycj pushed a commit to brodycj/Cordova-sqlite-storage-common-dev that referenced this issue Dec 8, 2017
brodycj pushed a commit to brodycj/Cordova-sqlite-storage-common-dev that referenced this issue Dec 11, 2017
@NeoLSN
Copy link

NeoLSN commented Dec 11, 2017

@brodycj
Copy link
Contributor

brodycj commented Dec 11, 2017

Should be like this?
https://github.com/freakypie/card.io-phonegap/blob/master/plugin.xml#L16-L20

@NeoLSN that looks right to me. For reference: https://cordova.apache.org/docs/en/latest/plugin_ref/spec.html#lib-file

I will investigate further when I get a chance. I wonder if it would work to use arch="*" to mean use library regardless of emulator or device.

Thanks for the hint!

@brodycj
Copy link
Contributor

brodycj commented Dec 12, 2017

@NeoLSN that looks right to me. For reference: https://cordova.apache.org/docs/en/latest/plugin_ref/spec.html#lib-file

I will investigate further when I get a chance. I wonder if it would work to use arch="*" to mean use library regardless of emulator or device.

Looks like there is a better solution at https://stackoverflow.com/questions/34115571/add-external-jar-library-to-build-cordova-plugin-ionic-framework/34129929#34129929: use lib-file element with no arch setting.

@NeoLSN
Copy link

NeoLSN commented Dec 15, 2017

Right now, seems this plugin will change the cordova context when installing it. So after this plugin installed, the other plugins would be install unsuccessfully.

@brodycj
Copy link
Contributor

brodycj commented Dec 15, 2017

Right now, seems this plugin will change the cordova context when installing it. So after this plugin installed, the other plugins would be install unsuccessfully.

I do not follow, can you please explain further?

@NeoLSN
Copy link

NeoLSN commented Dec 15, 2017

The installing order of my plugins is like this.

<plugin name="cordova-plugin-zeroconf" spec="^1.3.0" />
<plugin name="cordova-sqlite-storage" spec="^2.1.3" />
<plugin name="ionic-plugin-keyboard" spec="^2.2.1" />
<plugin name="ionic-plugin-deeplinks" spec="^1.0.15">

After installing cordova-sqlite-storage, ionic-plugin-deeplinks and ionic-plugin-keyboard would not install successs. The error message is like below.

Installing "cordova-sqlite-storage" for android

installing external dependencies via npm
npm install of external dependencies ok
Dependent plugin "es6-promise-plugin" already installed on android.

Installing "ionic-plugin-deeplinks" for android

Failed to install 'ionic-plugin-deeplinks': Error: ENOENT: no such file or directory, open '<project_root>/platforms/android/AndroidManifest.xml'
    at Object.fs.openSync (fs.js:663:18)
    at Object.fs.readFileSync (fs.js:568:33)
    at Object.parseElementtreeSync (<project_root>/app/platforms/android/cordova/node_modules/cordova-common/src/util/xml-helpers.js:180:27)
    at new AndroidManifest (<project_root>p/platforms/android/cordova/lib/AndroidManifest.js:29:20)
    at AndroidProject.getPackageName (<project_root>/platforms/android/cordova/lib/AndroidProject.js:99:12)
    at Api.addPlugin (<project_root>/platforms/android/cordova/Api.js:223:57)
    at handleInstall (/Users/JasonYang/.nvm/versions/node/v9.3.0/lib/node_modules/cordova/node_modules/cordova-lib/src/plugman/install.js:594:10)
    at /Users/JasonYang/.nvm/versions/node/v9.3.0/lib/node_modules/cordova/node_modules/cordova-lib/src/plugman/install.js:357:28
    at _fulfilled (/Users/JasonYang/.nvm/versions/node/v9.3.0/lib/node_modules/cordova/node_modules/q/q.js:787:54)
    at self.promiseDispatch.done (/Users/JasonYang/.nvm/versions/node/v9.3.0/lib/node_modules/cordova/node_modules/q/q.js:816:30)

Error: ENOENT: no such file or directory, open '<project_root>/platforms/android/AndroidManifest.xml'

But if I remove the cordova-sqlite-storage. It will work fine.

@NeoLSN
Copy link

NeoLSN commented Dec 15, 2017

Maybe this issue is from what you mentioned.
P.S. The fundamental issue is a problem with adding JAR and NDK library files due to wrong plugin.xml tag used.
And it breaks the AndroidStudio project structure, so causes other plugins can not be installed well.
https://github.com/apache/cordova-android/blob/master/bin/templates/cordova/Api.js#L65-L93

@mmp-schmitt
Copy link

@NeoLSN : "Error: ENOENT: no such file or directory, open '<project_root>/platforms/android/AndroidManifest.xml'"
In fact this error message is a direct result of the modified directory structure. To overcome this error message I created a set of aliases, which enables old scripts to find the files back again.

cd /<project_root>/platforms/android
ln -s app/src/main/res .
ln -s app/src/main/AndroidManifest.xml .
ln -s app/src/main/java src

It will make this error message disappear, but I was not able to get the cordova-sqlite-ext running using cordova-android@7.0.0. It always reports "error: package io.liteglue does not exist".
I experimented with several combinations of the instruction in plugin.xml, but always got the same error message.
I would be grateful for any hint (even a temporary manual hack) to get this running. Thank you.

@NeoLSN
Copy link

NeoLSN commented Dec 26, 2017

@mmp-schmitt Because you are doing wrong thing. What you are doing is just let the 'add plugin' success, but this action also brakes Android project structure.

@mmp-schmitt
Copy link

@NeoLSN: If this is the wrong thing to do, then what is the right thing to do?
When I look at the resulting apk file (created the wrong way), then I can see that the libraries 'sqlite-connector.jar' and 'libsqlc-native-driver.so' have not been embedded. There is no 'lib' folder any more. So how can I add them to the 'apk'?

@NeoLSN
Copy link

NeoLSN commented Dec 26, 2017

@mmp-schmitt This issue is trying to figure out a correct way to add this plugin into new Android project via cordova CLI.

@brodycj
Copy link
Contributor

brodycj commented Dec 26, 2017

If I would adapt plugin.xml according to the patterns discussed above, like this:

diff --git a/plugin.xml b/plugin.xml
index 0442733..eec9c95 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -34,13 +34,13 @@
         <source-file src="src/android/io/sqlc/SQLiteConnectorDatabase.java" target-dir="src/io/sqlc"/>
 
         <!-- Android-sqlite-connector [jar]: -->
-        <source-file src="node_modules/cordova-sqlite-storage-dependencies/libs/sqlite-connector.jar" target-dir="libs"/>
+        <lib-file src="node_modules/cordova-sqlite-storage-dependencies/libs/sqlite-connector.jar" />
         <!-- Android-sqlite-connector native driver [native libs]: -->
-        <source-file src="node_modules/cordova-sqlite-storage-dependencies/libs/arm64-v8a/libsqlc-native-driver.so" target-dir="libs/arm64-v8a"/>
-        <source-file src="node_modules/cordova-sqlite-storage-dependencies/libs/armeabi/libsqlc-native-driver.so" target-dir="libs/armeabi"/>
-        <source-file src="node_modules/cordova-sqlite-storage-dependencies/libs/armeabi-v7a/libsqlc-native-driver.so" target-dir="libs/armeabi-v7a"/>
-        <source-file src="node_modules/cordova-sqlite-storage-dependencies/libs/x86/libsqlc-native-driver.so" target-dir="libs/x86"/>
-        <source-file src="node_modules/cordova-sqlite-storage-dependencies/libs/x86_64/libsqlc-native-driver.so" target-dir="libs/x86_64"/>
+        <lib-file src="node_modules/cordova-sqlite-storage-dependencies/libs/arm64-v8a" />
+        <lib-file src="node_modules/cordova-sqlite-storage-dependencies/libs/armeabi" />
+        <lib-file src="node_modules/cordova-sqlite-storage-dependencies/libs/armeabi-v7a" />
+        <lib-file src="node_modules/cordova-sqlite-storage-dependencies/libs/x86" />
+        <lib-file src="node_modules/cordova-sqlite-storage-dependencies/libs/x86_64" />
     </platform>
 
     <!-- iOS -->
Brodys-MacBook-Pro:cordova-sqlite-common-dev brodybits$ 

it still works on cordova-android@6 but NOT on cordova-android@7.

I discovered someone made the following change in filionf/cordova-sqlite-legacy@74adc97 to support cordova-android@7 as installed by Cordova 8:

diff --git a/plugin.xml b/plugin.xml
index e5cc6f2..f275f6c 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -35,13 +35,13 @@
         <source-file src="src/android/io/sqlc/SQLiteConnectorDatabase.java" target-dir="src/io/liteglue"/>
 
         <!-- Android-sqlite-connector [jar]: -->
-        <source-file src="src/android/libs/sqlite-connector.jar" target-dir="libs"/>
+        <source-file src="src/android/libs/sqlite-connector.jar" target-dir="app/libs"/>
         <!-- Android-sqlite-connector native driver [native libs]: -->
-        <source-file src="src/android/libs/arm64-v8a/libsqlc-native-driver.so" target-dir="libs/arm64-v8a"/>
-        <source-file src="src/android/libs/armeabi/libsqlc-native-driver.so" target-dir="libs/armeabi"/>
-        <source-file src="src/android/libs/armeabi-v7a/libsqlc-native-driver.so" target-dir="libs/armeabi-v7a"/>
-        <source-file src="src/android/libs/x86/libsqlc-native-driver.so" target-dir="libs/x86"/>
-        <source-file src="src/android/libs/x86_64/libsqlc-native-driver.so" target-dir="libs/x86_64"/>
+        <source-file src="src/android/libs/arm64-v8a/libsqlc-native-driver.so" target-dir="app/libs/arm64-v8a"/>
+        <source-file src="src/android/libs/armeabi/libsqlc-native-driver.so" target-dir="app/libs/armeabi"/>
+        <source-file src="src/android/libs/armeabi-v7a/libsqlc-native-driver.so" target-dir="app/libs/armeabi-v7a"/>
+        <source-file src="src/android/libs/x86/libsqlc-native-driver.so" target-dir="app/libs/x86"/>
+        <source-file src="src/android/libs/x86_64/libsqlc-native-driver.so" target-dir="app/libs/x86_64"/>
 
     </platform>
 

Will work but would need a major release since it would not work on earlier versions of cordova-android.

Best solution is probably to use Gradle.

For next major release (#687) the plan is to use builtin sqlite library on Android/iOS/macOS in this plugin version (cordova-sqlite-storage) and to deal with Android NDK library in cordova-sqlite-ext.

@mmp-schmitt
Copy link

I tried the solution above, but the 'apk' is still missing the libraries 'sqlite-connector.jar' and 'libsqlc-native-driver.so'. I cannot see any folder 'app/libs/' or 'libs' in the 'apk'.

@ahsan154
Copy link

ahsan154 commented Jan 2, 2018

waiting for this issue to be resolved with android@7, i am able to save data in db, but when i fetch from db , it hangs. :(

@jasonz1987
Copy link

+1 waiting..

@mburger81
Copy link

@brodybits This problems has opened also other issues on other frameworks and plugins, like ionic

ionic-team/ionic-plugin-keyboard#304

As mentioned, after installing your plugin we can not install any other plugin. Do you thinkg you can resolve this issue as fast as possible?
This has really big impact on the cordova environment.

@mburger81
Copy link

@brodybits
another exception running it with cordova 8 and cordova-android 7 I got is this

> cordova build android
cp: copyFileSync: could not write to dest file (code=ENOENT):/home/michi/tmp/abc/platforms/android/res/xml/config.xml

Parsing /abc/platforms/android/res/xml/config.xml failed
(node:26482) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: ENOENT: no such file or directory, open '/abc/platforms/android/res/xml/config.xml'
(node:26482) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

@brodycj
Copy link
Contributor

brodycj commented Jan 2, 2018

I will need 1-2 days to issue a special major release to resolve this issue by dropping use of Android-sqlite-connector / Android-sqlite-native-driver. Windows platform will likely be dropped from cordova-sqlite-storage at the same time as discussed in #687.

@brodycj
Copy link
Contributor

brodycj commented Jan 4, 2018 via email

@tombell
Copy link
Author

tombell commented Jan 4, 2018

@brodybits just finished testing, and works perfectly! 👍

@brodycj
Copy link
Contributor

brodycj commented Jan 4, 2018

@brodybits just finished testing, and works perfectly! 👍

@tombell thanks again for reporting and testing:)

@mburger81
Copy link

@brodybits I also tested it, it seems to work on cordova-android@6.4.0, for now I can not test on 7.0.0 because there are other plugins which has also problems with latest version.

THX MAN

@brodycj
Copy link
Contributor

brodycj commented Jan 4, 2018

THX MAN

@mburger81 absolutely and thanks for the feedback. While I would regard some of the error output to be an issue with the tooling (not clear enough) it is definitely better to avoid this kind of problem in the first place.

In case of any further issues with cordova-android@7 please report here or in a new issue.

@Medtrack
Copy link

Medtrack commented Jan 8, 2018

Hello, thanks for your work. I updated cordova (with cordova-android@7) today and found this issue creating my app.

Should I downgrade cordova? Or could you help me on how to include it using gradle?

Thanks

@brodycj
Copy link
Contributor

brodycj commented Jan 8, 2018

Hello, thanks for your work. I updated cordova (with cordova-android@7) today and found this issue creating my app.

@Medtrack I already tested and published a solution for this issue. As I stated in a comment above:

I solved this issue by bundling the NDK objects in a JAR file and using lib-file tags in config.xml to add the JAR dependencies on Android. This seems to work fine with both cordova-android@6 and cordova-android@7 on Android 4.x / 5.x / 6.x / 7.x / 8.x.

In case it does not work for you please give https://github.com/brodybits/cordova-sqlite-storage-starter-app a try. I just updated it to reference the latest version of this plugin and it works for me on Android with Cordova CLI 8 / cordova-android@7.0.0.

Should I downgrade cordova?

Possible but you should not have to do this. You should be able to do cordova plugin add android@6 in case this issue continues to persist for any reason.

Or could you help me on how to include it using gradle?

Gradle for Android would be the best solution. I will probably try this on SQLCipher first since they already support Gradle ref: storesafe/cordova-sqlcipher-adapter#64

In case this issue continues to persist for you then I will need some more information to diagnose what may be going wrong. In case you need private or higher priority support please contact sales@litehelpers.net for more information.

@Medtrack
Copy link

Medtrack commented Jan 9, 2018

Thank you very much @brodybits
In fact I am using the SQLCipher plugin. I downgraded cordova since I have other issues with custom plugins. I will keep an eye on this issue and try to update everything on my side.
Running cordova platform add android@6 works.

Many thanks for your answer.

@brodycj
Copy link
Contributor

brodycj commented Jan 29, 2018

@Medtrack I also fixed the SQLCipher plugin version to support cordova-android@7 by referencing AAR (ref: storesafe/cordova-sqlcipher-adapter#64).

@Medtrack
Copy link

Hi @brodybits, it's been a while but it is working now :-)

Thank you very much for your work!!!

@sebastian-zarzycki-apzumi

For some reason, this is still not working:

cordova platform add android@latest
Using cordova-fetch for cordova-android@latest
Adding android project...
Creating Cordova project for the Android platform:
	Path: platforms/android
	Package: com.chanlhealth.app
	Name: Chanl_Health
	Activity: MainActivity
	Android target: android-27
Subproject Path: CordovaLib
Subproject Path: app
Android project created with cordova-android@7.1.0
Android Studio project detected
Android Studio project detected
Installing "cordova-android-support-gradle-release" for android
Subproject Path: CordovaLib
Subproject Path: app
Installing "cordova-plugin-app-event" for android
cordova-android-support-gradle-release : WROTE  /Users/rattkin/Documents/Workspaces/WebStorm/chanlhealth-app-ionic2/platforms/android/build.gradle  >  26.+
Installing "cordova-plugin-app-version" for android
Installing "cordova-plugin-browsertab" for android
Plugin dependency "cordova-plugin-compat@1.2.0" already fetched, using that version.
Installing "cordova-plugin-compat" for android
Plugin doesn't support this project's cordova-android version. cordova-android: 7.1.0, failed version requirement: 
      <6.3.0
Skipping 'cordova-plugin-compat' for android
Subproject Path: CordovaLib
Subproject Path: app
Installing "cordova-plugin-compat" for android
Plugin doesn't support this project's cordova-android version. cordova-android: 7.1.0, failed version requirement: 
      <6.3.0
Skipping 'cordova-plugin-compat' for android
Installing "cordova-plugin-device" for android
Installing "cordova-plugin-network-information" for android
Installing "cordova-plugin-splashscreen" for android
Installing "cordova-plugin-statusbar" for android
Installing "cordova-plugin-transport-security" for android
Installing "cordova-plugin-whitelist" for android

               This plugin is only applicable for versions of cordova-android greater than 4.0. If you have a previous platform version, you do *not* need this plugin since the whitelist will be built in.
          
Installing "cordova-plugin-youtube-video-player" for android
Installing "cordova-sqlite-storage" for android
installing external dependencies via npm
npm install of external dependencies ok
Failed to install 'cordova-sqlite-storage': Error: ENOENT: no such file or directory, open '/Users/rattkin/Documents/Workspaces/WebStorm/chanlhealth-app-ionic2/platforms/android/AndroidManifest.xml'
    at Object.fs.openSync (fs.js:646:18)
    at Object.fs.readFileSync (fs.js:551:33)
    at Object.parseElementtreeSync (/Users/rattkin/Documents/Workspaces/WebStorm/chanlhealth-app-ionic2/platforms/android/cordova/node_modules/cordova-common/src/util/xml-helpers.js:180:27)
    at new AndroidManifest (/Users/rattkin/Documents/Workspaces/WebStorm/chanlhealth-app-ionic2/platforms/android/cordova/lib/AndroidManifest.js:29:20)
    at AndroidProject.getPackageName (/Users/rattkin/Documents/Workspaces/WebStorm/chanlhealth-app-ionic2/platforms/android/cordova/lib/AndroidProject.js:99:12)
    at Api.addPlugin (/Users/rattkin/Documents/Workspaces/WebStorm/chanlhealth-app-ionic2/platforms/android/cordova/Api.js:223:57)
    at handleInstall (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/src/plugman/install.js:594:10)
    at /usr/local/lib/node_modules/cordova/node_modules/cordova-lib/src/plugman/install.js:357:28
    at _fulfilled (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:787:54)
    at self.promiseDispatch.done (/usr/local/lib/node_modules/cordova/node_modules/cordova-lib/node_modules/q/q.js:816:30)
(node:25575) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: ENOENT: no such file or directory, open '/Users/rattkin/Documents/Workspaces/WebStorm/chanlhealth-app-ionic2/platforms/android/AndroidManifest.xml'
(node:25575) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I've tried the cordova-sqlite-storage-starter-app and it adds the platform there without any issues. I'm very confused as to where's the difference. Could it be that other plugins are influencing this?

@brodycj
Copy link
Contributor

brodycj commented Mar 20, 2018

Hi @sebastian-zarzycki-es it is possible that other plugins can influence your results, also possible that your Android platform project is really messed up. I would recommend you try the following:

First completely remove your Android project (rm -rf ..../platforms/android) and then try your cordova platform add android command again.

If that doesn't work then I recommend that you start with a clean project and add one plugin at a time until you can determine what does and does not work.

@sebastian-zarzycki-apzumi

Yes, my apologies. From that output, it looked like cordova-sqlite-storage was the culprit, where, in fact, it was a plugin right before that - namely, cordova-plugin-youtube-video-player. Removing the plugin fixed the build.

@joeljeske
Copy link

I have found success overriding the cordova lib check using this hook:

https://gist.github.com/joeljeske/68121fa6d643e0937f50458d0172e16e

This avoids being blocked by a potentially bad plugin and waiting for them to upgrade.

@brodycj
Copy link
Contributor

brodycj commented Apr 2, 2018

Thanks @joeljeske. I am thinking right now this is something that should be fixed on Cordova itself, open to feedback though.

brodycj pushed a commit to brodycj/cordova-sqlite-ext that referenced this issue Apr 10, 2018
- Use cordova-sqlite-ext-deps 1.1.0 with SQLite 3.22.0,
  with Android sqlite-native-driver NDK build in JAR
  (along with other compile-time option updates)
  to resolve issue on cordova-android@7
  ref: storesafe/cordova-sqlite-storage#729
- SQLITE_DEFAULT_SYNCHRONOUS=3 (EXTRA DURABLE) compile-time setting on
  all platforms (Android/iOS/macOS/Windows)
  ref: storesafe/cordova-sqlite-storage#736
- plugin.xml use lib-file for Android sqlite-native-driver NDK build in
  JAR from cordova-sqlite-ext-deps (1.1.0) to resolve issue on
  cordova-android@7 (along with other compile-time option updates)
  ref: storesafe/cordova-sqlite-storage#729
- SQLITE_THREADSAFE=1 for iOS/macOS along with others (Android/Windows)
  ref: storesafe/cordova-sqlite-storage#754
- Enable FTS5 & JSON1 on all platforms
@matejkramny
Copy link

I'm still having this issue for some reason. Is there something I'm missing?

It's happening on a lot of other plugins still, and it's been ~5 months since 7.0 was released

@minute-med
Copy link

i'm having the issue aswell with android 7.0, any idea why ?
Can't get UPDATE queries to work ...

@dpa99c
Copy link

dpa99c commented Nov 6, 2018

After much trial and error, I found the libs path for cordova-android@7 has changed to app/src/main/jniLibs/, so you can do something like:

<source-file src="src/android/libs/armeabi-v7a/foo.so" 
    target-dir="app/src/main/jniLibs/armeabi-v7a/" />

I've fixed this in my cordova-plugin-hello-c example plugin and tested it with cordova-android@7.1.1.

@brodycj
Copy link
Contributor

brodycj commented Nov 6, 2018

Thanks @dpa99c for the input. Personally I would heavily favor using JAR, or AAR from Mavin, to avoid dependency on the generated directory structure.

@storesafe storesafe locked as resolved and limited conversation to collaborators Nov 6, 2018
@brodycj
Copy link
Contributor

brodycj commented Nov 6, 2018

This issue is now locked as resolved on this plugin.

In case of further issues with Android NDK libraries I would recommend starting with https://github.com/brodybits/cordova-sqlite-storage-starter-app and adding one plugin at a time to discover which still need to be fixed.

I raised apache/cordova-docs#902 to get this documented for other plugins on Cordova itself and would prefer to keep further discussion there.

I also raised storesafe/cordova-sqlite-storage-help#60 in case anyone has any questions about Android NDK (*.so) libraries related to this plugin.

In case anyone can reproduce an issue with Android NDK libraries on this plugin, with a minimal, complete, and verifiable example (https://stackoverflow.com/help/mcve), then a new issue with the mvce would be appreciated.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests