Compare commits

...

237 Commits
7.1.2 ... 9.0.0

Author SHA1 Message Date
Erisu
cdcb4ddcd9 Set VERSION to 9.0.0 (via coho) 2020-06-23 18:33:21 +09:00
Erisu
8e53e2aa56 Update JS snapshot to version 9.0.0 (via coho) 2020-06-23 18:33:21 +09:00
Erisu
5276f56cc4 android-v9.0.0 Updated version and RELEASENOTES.md for release 9.0.0 2020-06-23 18:31:47 +09:00
エリス
9df6793f34 chore: set AndroidX off by default (#1005) 2020-06-23 16:13:04 +09:00
Duy Mac Van
d90e191837 Accept multiple mime types on file input (#971) 2020-06-18 09:59:44 -03:00
Norman Breau
d5d448888d fix: support both adaptive and standard icons at the same time (#1001) 2020-06-18 09:58:42 -03:00
Norman Breau
8d8600b442 fix: Plugin install fails when preview sdk is installed (#985) 2020-06-18 09:58:14 -03:00
Chris Brody
0bf6455153 cleanup yaml files (#994)
* remove trailing spaces from .asf.yaml
* remove trailing spaces from .eslintrc.yml
* add newline to end of .eslintrc.yml
2020-06-16 19:34:10 -04:00
Chris Brody
80b7a7f6ac cleanup: remove trailing spaces from Java sources (#999)
* remove trailing spaces from framework/src/org/apache/cordova/*.java
* remove trailing spaces from framework/src/org/apache/cordova/engine/*.java
2020-06-16 19:26:48 -04:00
Chris Brody
6b789c57e8 update some dependencies (#992)
* update cordova-common -> ^4.0.1
* update execa -> ^4.0.2
* update fs-extra -> ^9.0.1
2020-06-10 11:47:17 -04:00
Chris Brody
b8a344fc17 cleanup: remove trailing spaces from framework build files (#998)
* remove trailing spaces from framework/build.xml
* remove trailing spaces from framework/cordova.gradle
2020-06-10 09:32:43 -04:00
Chris Brody
1b6319e9cf cleanup: remove trailing spaces from project template (#997)
* remove trailing spaces from bin/templates/project/AndroidManifest.xml
* remove trailing spaces from bin/templates/project/assets/www/css/index.css
2020-06-10 09:31:51 -04:00
Chris Brody
2cc81253ff cleanup: remove trailing spaces from bat files (#996)
* remove trailing spaces from bin/*.bat
* remove trailing spaces from bin/templates/cordova/lib/*.bat
2020-06-10 09:30:51 -04:00
Chris Brody
499c694146 remove trailing spaces from markdown files (#995)
* remove trailing spaces from .github/ISSUE_TEMPLATE.md
* remove trailing spaces from .github/ISSUE_TEMPLATE/BUG_REPORT.md
* remove trailing spaces from .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
* remove trailing spaces from CONTRIBUTING.md
2020-06-10 09:29:14 -04:00
Chris Brody
bc32cca281 update devDependencies (#993)
* update jasmine-spec-reporter -> ^5.0.2 in devDependencies

* update nyc -> ^15.1.0 in devDependencies
2020-06-10 09:27:11 -04:00
Chris Brody
dead4b4ab6 breaking: reduce combined response cutoff to 16 MB (#987)
* breaking: reduce combined response cutoff to 16 MB
* rename `MAX_PAYLOAD_SIZE` to `COMBINED_RESPONSE_CUTOFF`
* update some comments
* cleanup: split `if` statements into multiple lines
2020-06-09 19:48:33 -04:00
Chris Brody
305cb2cdd5 major: Gradle 6.5 & Android Gradle plugin 4.0.0 updates (#988)
* feat: Gradle 6.5 & Android Gradle plugin 4.0.0 updates

* update test/android/wrapper.gradle to reference Gradle 6.5

* update test/androidx/wrapper.gradle to reference Gradle 6.5

* update spec/fixtures/android_studio_project/build.gradle
  to reference com.android.tools.build:gradle:4.0.0

* update test/android/build.gradle
  to reference com.android.tools.build:gradle:4.0.0

* update test/androidx/build.gradle
  to reference com.android.tools.build:gradle:4.0.0
2020-06-08 20:47:10 -04:00
Chris Brody
287bfcbaa3 cleanup: remove trailing spaces from app/build.gradle (#990)
(in the template)
2020-06-08 12:42:38 -04:00
Chris Brody
2a61b751ab breaking: remove legacy/build.gradle from template (#989) 2020-06-08 10:42:15 -04:00
Norman Breau
a830145f36 fix: wait_for_boot waiting forever (#978) 2020-05-22 15:07:18 -03:00
GimpArm
08dc1dd9b9 Increased detectArchitecture() timeout (#965)
The timeout for detechArchitecture() is sometimes too low when devices are on wifi network connections and even sometimes over USB. The command can take up to 3 seconds to execute and return. Currently the timeout is set to 1000 ms and setting it to 5000 ms seems to be a good compromise.

Co-authored-by: Scott Downing <Scott.Downing@marc-cain.de>
2020-05-22 15:06:11 -03:00
Norman Breau
e86b211cd1 breaking: Bump android gradle plugin to 3.6.0 (#962) 2020-04-25 14:34:15 -03:00
Erisu
6c5febc189 chore(asf): update git notification settings 2020-04-22 13:29:31 +09:00
Norman Breau
4d0d60c294 Feature: JVM Args flag (#948)
* feat: JVM args flag

* test: JVM args flag

* feat: Do not display recommended memory warning unless if memory is less than cordova default
2020-04-16 09:39:22 -03:00
Norman Breau
16a88ec631 fix: ANDROID_SDK_ROOT variable (#951)
This commit does the following:
- Makes ANDROID_SDK_ROOT the primary variable to look for the Android SDK location.
- Makes ANDROID_HOME the fallback variable, if ANDROID_SDK_ROOT is not present/valid.

Gradle updates:
Note that the following gradle updates were required, otherwise the android gradle plugin did not honour the ANDROID_SDK_ROOT variable.

- Updates the framework's android studio's gradle plugin from version 3.3.0 to 3.5.3.
	Not only this is required for android's gradle to obey ANDROID_SDK_ROOT, it is now in sync with the Android test project/
- Updates the Androidx test project to use gralde plugin from version 3.3.0 to 3.5.3, to match Android Test & framework.
	- Consequentially, this required to also upgrade AndroidX test project to use Gradle 6.1, which also matches both the Android test project & framework

These changes above fixes #949

Additionally, since we update the environment variables dynamically, the environment variable printout produced misleading information.
The environment variable printout will now print out the variable as defined by the user (before the tooling messes with them). An additional log
is printed that tells the user exactly what Cordova is going to use for the Android SDK path. This should fix #670
2020-04-15 23:43:17 -03:00
Norman Breau
1ce2b6b315 test: synced androidx gradle versions to the same version as the android test (#959) 2020-04-15 23:42:33 -03:00
Norman Breau
43a6805902 feat: com.android.tools.build:gradle:3.5.3 (#960) 2020-04-15 23:42:15 -03:00
エリス
566262c923 chore(npm): add package-lock.json (#956) 2020-04-15 12:46:23 +09:00
エリス
e4bff281b2 chore(npm): add ignore list (#958) 2020-04-15 12:41:24 +09:00
エリス
9a675912f8 chore: various cleanup (#957)
* chore: various cleanup
  * update LICENSE year
  * update third-party appended licenses
  * update NOTICE year & format
  * use correct CI badges on README.md
  * remove extra markdown from RELEASENOTES.md
* chore: update reviewboard url
2020-04-15 12:40:59 +09:00
エリス
f86044e6ce chore(eslint): bump package & apply eslint fix (#955) 2020-04-15 12:36:40 +09:00
エリス
43fdaa91a3 breaking(npm): bump packages (#954)
* breaking(npm): bump dev dependencies
  * jasmine-spec-reporter@^5.0.1
  * nyc@^15.0.1
  * rewire@^5.0.0

* breaking(npm): bump dependencies
  * android-versions@^1.5.0
  * cordova-common@^4.0.0
  * execa@^4.0.0
  * fs-extra@^9.0.0
  * nopt@^4.0.3
  * which@^2.0.2
2020-04-15 12:20:28 +09:00
エリス
80f46aefcd chore(npm): use short notation in package.json (#953) 2020-04-15 11:53:22 +09:00
João Gonçalves
71f63d7b33 Prevent exit fullscreen mode from closing application (#823)
* (android) wrap custom view in FrameLayout

Wraps the custom view in a FrameLayout in order
to capture key events and redirect them to SystemWebView's
dispatchKeyEvent.

* Update framework/src/org/apache/cordova/CordovaWebViewImpl.java

Co-Authored-By: エリス <erisu@users.noreply.github.com>

* Update framework/src/org/apache/cordova/CordovaWebViewImpl.java

Co-Authored-By: エリス <erisu@users.noreply.github.com>

* Update framework/src/org/apache/cordova/CordovaWebViewImpl.java

Co-Authored-By: エリス <erisu@users.noreply.github.com>

* remove empty line below @override

Co-authored-by: Norman Breau <norman@normanbreau.com>
Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-04-14 18:48:06 +00:00
Niklas Merz
8d47cd73c0 Update CONTRIBUTING.md 2020-04-13 13:56:17 +02:00
Norman Breau
6d451bc6f9 fix: Removed redundent logcat print (#950)
The chromium webview will print an informational log already,
we don't need to override the method to do what the webview will already.
2020-04-11 23:25:20 -03:00
Bas Bosman
6402e7b755 Bump minSdkVersion to 22 and drop pre-Lollipop specific code (#915)
* feat: bump minSdkVersion to 22

BREAKING CHANGE: drop KitKat support

* chore: remove obsolete comment

* feat: remove pre-Lollipop specific code

* chore: remove KitKat from needsKitKatContentUrlFix

* chore: other minor cleanup
2020-04-03 19:54:08 +02:00
Norman Breau
c93f93f637 fix: GH-873 App bundle builds to obey command-line arguments (#941) 2020-04-01 01:59:39 -03:00
エリス
6e51943d15 ci: drop travis & move codecov to gh-actions (#940) 2020-04-01 13:35:07 +09:00
Norman Breau
c81cd871f8 Updated README to reflect what Android requires more accurately, which is Java 8, not anything less, not anything greater. Java 1.8.x is required. (#929) 2020-04-01 00:55:47 -03:00
Norman Breau
8ab1dbc373 fix: GH-935 replaced compare-func with native sort method (#937) 2020-04-01 00:43:36 -03:00
エリス
fb26050fab fix: test failure with shebang interpreter in rewired files (#939)
* breaking: remove shebang from create
* breaking: remove shebang from version
* breaking: remove shebang from logical files
2020-04-01 12:04:35 +09:00
Sukant Pal
c56cd4d5a8 refactor: use es6 class (#911)
Refactored to Classes:
* Api
* AndroidManifest
* AndroidProject
2020-02-01 17:34:51 +09:00
エリス
de105e8651 refactor (eslint): use cordova-eslint (#910) 2020-01-31 22:02:48 +09:00
エリス
8e98de6e7c chore: remove appveyor residual (#909) 2020-01-29 19:28:07 +09:00
エリス
6372ca3fac feat: add github actions (#895) 2020-01-29 14:29:05 +09:00
Norman Breau
3712619f5c refactor: remove shelljs dependency (#842)
Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-01-29 10:12:55 +09:00
エリス
dee1e77d0b feat: add kotlin support (#896)
Co-authored-by: Joshua Chandler <joshchandler88@gmail.com>
2020-01-27 16:14:58 +09:00
エリス
d01ed80a61 feat: add androidx support (#901) 2020-01-27 15:52:08 +09:00
Norman Breau
92268b2e76 fix: cordova requirements consider the android-targetSdkVersion (#849)
* Made cordova requirements consider the android-targetSdkVersion preference
* refator: get_target method
Added comments.
Added JSDoc block
Reduced error exit point to one spot

Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-01-24 10:53:49 +09:00
エリス
0924654a47 fix (adb): shell to return expected stdout (#904) 2020-01-21 23:37:22 +09:00
Norman Breau
8ef742e79d feat: upgrade gradle to 6.1 & gradle build tools to 3.5.3 (#792)
* feat!: upgrade gradle to 6.1
* feat!: upgrade gradle build tools to 3.5.3
* feat: added `npm run clean-tests`
* fix!: Removed useDeprecatedNdk as this option is now completely removed from gradle.
* feat!: bump gradle to 6.1 & gradle build tools to 3.5.3

Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-01-21 19:24:00 +09:00
エリス
64ef13c6e0 chore: remove .project file & add .settings to gitignore (#902) 2020-01-20 11:41:56 +09:00
Raphael von der Grün
66ad2c948e refactor: simplify doFindLatestInstalledBuildTools (#900)
* chore: update com.g00fy2:versioncompare to 1.3.4

* refactor: flatten error handling in doFindLatestInstalledBuildTools

* refactor: inline & simplify getAvailableBuildTools

* refactor: use string interpolation for error messages
2020-01-17 13:43:25 +01:00
Kristian Heljas
60e022fedd feat: use java package name for loading BuildConfig (#751) 2020-01-17 20:46:45 +09:00
エリス
09256b766f chore: rename gradle plugin google services preference options (#898) 2020-01-17 08:22:06 +09:00
エリス
a951793431 feat: add google services support (#893)
* feat: Add support for GoogleServicesEnabled preference option
* fix: wrap google-services classpath with GoogleServicesEnabled flag
* chore: bump google-services version to 4.2.0
* feat: Add support for GoogleServicesVersion preference option

Co-authored-by: Maksim Chemerisuk <chemerisuk@users.noreply.github.com>
2020-01-16 08:40:54 +09:00
Ramazan VAPURCU
f4b8f44d4a feat: add version-compare library to compare build-tools versions properly. (#709)
Closes #708
2020-01-15 07:28:54 +09:00
Norman Breau
08ab7d4b59 Ignore auto-generated eclipse buildship files (#831)
* git ignore eclipse build ship files that gets auto-generated in Eclipse and some other code editors.

* Changed **./project to .project as suggested

Co-Authored-By: Raphael von der Grün <raphinesse@gmail.com>

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
2020-01-07 15:32:19 -04:00
Norman Breau
5889001465 Increased default target sdk to 29 (#848)
* (android) increased default target sdk to 29

* Updated travis and appveyor to use build tools/api level 29

* Fixed two other files that was missing the API 29 update
2020-01-07 11:47:27 -04:00
Norman Breau
91d2716122 [Major] Removed unnecessary project name restriction (#859)
* removed unnecessary restriction that prevented project names from starting with a number. Project names starting with a number is perfectly valid.

* Reworded validateProjectName jsdoc

Co-Authored-By: Raphael von der Grün <raphinesse@gmail.com>

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
2020-01-07 10:10:04 -04:00
エリス
0e6ad28e56 chore: drop q module (#833)
* chore: drop q module
* chore: fix & complete dropping q
* Fix faulty transformation of Q.when
* Simplify thenResolve transformation
  * Removes unnecesary Promise wrapping in onFulfilled callback.
* Transform .done calls to .then or .catch
  * The important thing is that we always handle rejections.
* Remove Q from specs
Requires Jasmine 3.5
* Replace Q.timeout w/ Promise.race & custom function

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
2020-01-07 21:22:59 +09:00
Raphael von der Grün
fd57909730 chore: replace superspawn & child_process with execa (#862)
* chore: added execa dependency

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in android_sdk

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in build

* chore: execa - drop superspawn in check_reqs

Plus: Remove useless trimming of execa output

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in emulator

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in device

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in run_java_unit_tests

* chore: execa - drop superspawn in ProjectBuilder

Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>

* chore: execa - drop superspawn in adb

* chore: execa - drop superspawn in plugin.spec

* chore: execa - replace child_process in log

* chore: execa - replace child_process in check_reqs

* chore: execa - replace child_process in emulator

Co-authored-by: エリス <erisu@users.noreply.github.com>
2020-01-06 23:15:22 +01:00
Raphael von der Grün
e3cc75caff feat: don't filter gradle's stderr anymore (#860) 2020-01-06 12:15:25 +09:00
エリス
e26142f43b chore: drop node 6 and 8 support (#832) 2020-01-06 12:10:03 +09:00
エリス
d0f972e128 chore: bump version to 9.0.0-dev (#890) 2020-01-06 12:07:05 +09:00
任跃兵
e42fedc820 Optimization code (#697)
This judgment condition has no use.
2019-11-14 19:24:10 +00:00
Adri Van Houdt
f0c9814c04 chore: removed comment that serves no purpose (#863) 2019-11-04 15:42:49 +00:00
Raphael von der Grün
529278190c Update jasmine to 3.5.0 (#861) 2019-11-03 21:44:16 +01:00
Raphael von der Grün
fcaab36484 Modernize our one E2E test (#858)
The test should still do the same thing as before.
No change tested code intended.

This also gets rid of the barely used and outdated E2E helpers
2019-10-30 16:24:32 +01:00
Raphael von der Grün
5dfa995a4b Ensure to lint as many files as possible (#854)
* Lint everything, including bins w/out extension

* Apply eslint --fix to all linted files

* Manually fix all remaining lint rule violations

* Remove ESLint inline config
2019-10-21 18:26:17 +02:00
Erisu
c35a990c09 Update JS snapshot to version 8.2.0-dev (via coho) 2019-09-11 13:58:08 +09:00
Erisu
11f40bd2cc Set VERSION to 8.2.0-dev (via coho) 2019-09-11 13:58:04 +09:00
Erisu
7e8b47d012 minor-8.1.0 Updated version and RELEASENOTES.md for release 8.1.0 2019-09-11 12:41:25 +09:00
エリス
902aa32dda chore: bump dependencies for release 8.1.0 (#827) 2019-09-11 11:34:33 +09:00
MatusFiala
42c0cba7f7 feat: added multiple selection for filepicker (#651)
* GH-621 (Cordova-Android)
2019-09-09 08:44:40 +09:00
Jan Piotrowski
f2b84d8d83 chore: compress files in /res with tinypng.com (#672) 2019-09-07 22:45:43 +09:00
Norman Breau
1b11206174 fix: clean command (#815)
* fix clean command
* added unit tests for the getArgs clean
* Cleaned up ProjectBuilder.getArgs for readability
2019-09-07 13:54:32 +09:00
Darryl Pogue
c93e3e9f6f Merge pull request #750 from goffioul/patch-1
Don't request focus explicitly if not needed
2019-08-28 15:31:25 -07:00
Norman Breau
9808a0d4d3 GH-799 (android) Stop webview from restarting when activity resizes (#800) 2019-08-26 19:57:11 +00:00
Norman Breau
bd1697dbd2 feat: Build app bundles (.aab files) (#764)
* (android) Added android bundle support

  with some corrected tests

  added bundle specific output

* with --packageType flag to have consistency with cordova-ios

* warn about missing required signing params only if at least one signing param is present

* produce error on run if packageType = bundle

* added comments relating to shelljs as suggested

* unit test case added by @brodybits - Chris Brody

* Filled in error message and unit test spec

Primary author: @breautek - Norman Breau <norman@normanbreau.com>

Co-authored-by: Norman Breau <norman@normanbreau.com>
Co-authored-by: Chris Brody <chris@brody.consulting>
2019-08-08 12:53:10 -04:00
Raphael von der Grün
b3b8690bbd Simplify apkSorter using compare-func package (#788) 2019-07-18 11:59:54 +02:00
Raphael von der Grün
ad742ec93c Simplify and fix promise handling in specs (#787) 2019-07-17 14:56:36 +02:00
Raphael von der Grün
1de7c38134 Properly handle promise in create script (#784)
* Properly handle promise in create script

* Add regression test
2019-07-17 09:52:19 +02:00
Raphael von der Grün
997943a194 Do not clobber process properties with test mocks (#783) 2019-07-17 03:01:56 +02:00
Raphael von der Grün
47c6048d53 Do not clobber console.log to spy on it (#782) 2019-07-17 00:18:12 +02:00
エリス
a64d459c8e Add Node.js 12 to CI Services (#724)
and remove trailing whitespace from .travis.yml

Co-authored-by: エリス <erisu@users.noreply.github.com>
Co-authored-by: Christopher J. Brody <brodybits@users.noreply.github.com>
2019-07-14 17:20:47 -04:00
Chris Brody
a5ad440f17 ci(travis): set dist: trusty in .travis.yml (#777)
to avoid errored Travis CI build on Node.js 12

including NOTE with a TODO item
2019-07-14 16:54:26 -04:00
Chris Brody
acad24d62a Consistent order from ProjectBuilder.apkSorter (#779)
This function used to give a different order depending on the behavior
of Array.prototype.sort(), which led to a test failure on Node.js 12
(see apache/cordova-android#767).

This update gives a consistent sort order, regardless of the
JavaScript engine implementation, now succeeds on Node.js
versions 6, 8, 10, and 12.

Resolves #767

For reference:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
2019-07-14 16:35:44 -04:00
Chris Brody
989b4cc913 test: use verbose spec reporter (#778)
Co-authored-by: Christopher J. Brody <chris@brody.consulting>
Co-authored-by: Raphael von der Grün <raphinesse@users.noreply.github.com>
2019-07-14 15:36:46 -04:00
Norman Breau
38c6627999 rewire workaround for NodeJS 12 (#774)
* rewire workaround for NodeJS 12

* additional comment with a link to the underlying issue in jhnns/rewire#167
2019-07-12 02:09:57 -04:00
Chris Brody
4b9e18c6b8 nyc@14 update in devDependencies (#772) 2019-07-12 01:46:55 -04:00
Jan Piotrowski
906f8cc002 ci(travis): Fix Android SDK (#765)
* add node 12

* try to cleanup and fix

* fix

* fix

* no node 12 then...
2019-07-11 12:16:29 +02:00
goffioul
01ab11644c Don't request focus explicitly if not needed
Requesting the focus explicitly actually makes the child WebView to move the focus to the first visible focusable element on the page. This makes it impossible to simply let the WebView restore the focus to the last focused element, before the activity was paused. To prevent this problem on devices other that the Samsung Galaxy Note 3, only request the focus if necessary (it might as well be possible that the original fix is not needed anymore on newer versions of Android and/or WebView).
2019-06-20 15:28:08 -04:00
Raphael von der Grün
4cf3dcfaae Do not explicitly require modules from project directory (#713)
* Allow to pass-through projectPath to Builder

* Do not explicitly require modules from project directory
2019-04-13 17:34:59 +02:00
エリス
b177f84825 Added allprojects repositories for Framework Release Builds (#676) 2019-04-06 13:33:04 +09:00
エリス
485e6e0e4d Improve Gradle Build Arguments (#699)
* Remove `uses-sdk` from AndroidManifest
* Remove dependency `elementtree`
* Updated Build Command Help Menu Printout
  * Cleanup `minSdkVersion` printout
  * Added  `maxSdkVersion`, but not recommended to set.
  * Added `targetSdkVersion`
* Update the `GradlePropertiesParser` & Test Spec
  * Always Set Overriding Changes
  * Update existing properties
  * Update configure method
2019-04-06 13:28:25 +09:00
Darryl Pogue
516c3411aa Merge pull request #710 from dpogue/cookie-deprecation
Fix deprecation warning in SystemCookieManager
2019-04-02 13:27:20 -07:00
Darryl Pogue
908354e7fa Fix deprecation warning in SystemCookieManager 2019-04-02 07:16:08 -07:00
Darryl Pogue
9531dbbc7b Merge pull request #691 from dpogue/common-configparser
Run prepare with the correct ConfigParser version
2019-03-15 11:08:35 -07:00
Darryl Pogue
d10dd1c0b4 Remove unused browserify paths 2019-03-14 23:03:14 -07:00
Darryl Pogue
6533474070 GH-690: Run prepare with the correct ConfigParser 2019-03-14 23:02:53 -07:00
エリス
576edb53bb Updated ANDROID_HOME Test to Follow #656 Change (#673) 2019-02-18 09:34:55 +09:00
エリス
20e390af85 Update JS snapshot to version 8.1.0-dev (via coho) 2019-02-13 15:22:10 +09:00
エリス
931251a5a8 Set VERSION to 8.1.0-dev (via coho) 2019-02-13 15:22:06 +09:00
エリス
9697550488 GH-10 Updated version and RELEASENOTES.md for release 8.0.0 2019-02-13 15:19:45 +09:00
エリス
3dac984613 Added Missing License Headers (#669) 2019-02-13 11:35:43 +09:00
Chris Brody
867da56e2e Use custom Gradle properties to read minSdkVersion value from config.xml (#655)
Co-authored-by: エリス <ellis.bryan@gmail.com>
Co-authored-by: Christopher J. Brody <chris.brody@gmail.com>
2019-02-12 20:11:32 -05:00
Chris Brody
719acd3ab1 Merge pull request #668 from brodybits/revert-pr-664
Revert "Remove uses-sdk from AndroidManifest.xml (#664)"
2019-02-12 19:05:51 -05:00
Christopher J. Brody
549cae0a06 Revert "Remove uses-sdk from AndroidManifest.xml (#664)"
This reverts commit bb45f4f3ba.

Resolves #666
2019-02-12 19:01:07 -05:00
Chris Brody
bb45f4f3ba Remove uses-sdk from AndroidManifest.xml (#664)
* Remove uses-sdk from AndroidManifest.xml

    since uses-sdk values are now superseded by Gradle files

* remove elementtree no longer needed

* remove internal capitalize function no longer needed

* remove AndroidManifest SDK version tests no longer needed

resolves #629
2019-02-12 11:22:49 -05:00
Chris Brody
b4de6f55c4 Quick fix to support ANDROID_SDK_ROOT (#656)
resolves #617
2019-02-12 09:20:17 -05:00
Chris Brody
e99c145d55 Ignore more Gradle build artifacts in Android project (#632)
including a couple suggestions from:
- https://github.com/github/gitignore/blob/master/Gradle.gitignore
2019-02-11 14:58:20 -05:00
Jan Piotrowski
9cb9f32fbb Add or update GitHub pull request and issue template 2019-02-11 18:36:02 +01:00
Chris Brody
a6f30b6977 Android tools 3.3 & Gradle 4.10.3 update (#642)
Gradle 4.10.1 or higher is needed for Android Studio 3.3.0 ref:
- https://developer.android.com/studio/releases/gradle-plugin#3-3-0

Resolves #634 (Update Gradle classpath for Android Studio 3.3)
Resolves #596 (Support gradle 4.9+)
2019-02-09 19:35:46 -05:00
Chris Brody
73692e60d8 Quick updates to top-level project.properties (#654)
* Update header comments according to reality, in a similar fashion to
  bin/templates/project/project.properties

* update default target API to 28
  this change updates the target SDK value to 28 in both generated
  platforms/android/CordovaLib/AndroidManifest.xml and
  the resulting Android build, if not overridden by the Cordova user

Co-authored-by: Christopher J. Brody <chris.brody@gmail.com>
Co-authored-by: Joe Bowser <bowserj@apache.org>
2019-02-08 13:23:16 -05:00
Chris Brody
4abe3d3a49 Ignore Android Studio .idea files in project (#635) 2019-02-08 13:13:59 -05:00
Fabian Te
54df4a116c Add missing log to Java version check (#624)
* Add missing log to Java version check
* Simplify version check output

Resolves #623
2019-01-27 10:52:22 -05:00
Keith Lord
715ce2f9ac Update emulator.js to fix issue #608 (#630)
Fixes  "Device still authorizing issue" #608 (https://github.com/apache/cordova-android/issues/608)

Resolves #608
2019-01-18 15:24:28 -05:00
Chris Brody
7a98708eef .gitignore add package-lock.json (#626) 2019-01-17 19:23:55 -05:00
Chris Brody
d9c08f12a7 Fix requirements error messages for JDK 8 (#620) 2019-01-17 10:13:00 -05:00
Chris Brody
e41fbc5708 javac error message fixes in requirements check (#619)
- fix download link
- put download link on its own line
- no punctuation at the end of the download link
  (fixes #618)
- no extra newline at the end
- extra newline spacing in case JAVA_HOME is invalid
2019-01-16 22:51:45 -05:00
エリス
634f92efb6 Update Bug Issue Tracker 2019-01-15 21:57:08 -08:00
エリス
98a7b759e8 Remove unused dependecy promise-matchers 2019-01-15 21:57:08 -08:00
エリス
3976685e4e Cleanup package scripts 2019-01-15 21:57:08 -08:00
エリス
f7688bc64e Replaced code coverage istanbul with nyc 2019-01-15 21:57:08 -08:00
エリス
18e28294d6 Updated Dependencies 2019-01-15 21:57:08 -08:00
エリス
39bd0d6463 Bumped Dev Dependencies + ESLint Correction 2019-01-15 21:57:08 -08:00
エリス
c1819cc027 Bumped cordova-common@^3.1.0 2019-01-15 21:57:08 -08:00
エリス
f228d90ca2 Copy node_modules if the directory exists (#607) 2018-12-19 10:33:16 +09:00
Jan Piotrowski
5f44af3757 improve test readme (#582)
<!--
Please make sure the checklist boxes are all checked before submitting the PR. The checklist
is intended as a quick reference, for complete details please see our Contributor Guidelines:

http://cordova.apache.org/contribute/contribute_guidelines.html

Thanks!
-->

### Platforms affected


### What does this PR do?


### What testing has been done on this change?


### Checklist
- [ ] [Reported an issue](http://cordova.apache.org/contribute/issues.html) in the JIRA database
- [ ] Commit message follows the format: "CB-3232: (android) Fix bug with resolving file paths", where CB-xxxx is the JIRA ID & "android" is the platform affected.
- [ ] Added automated test coverage as appropriate for this change.
2018-12-04 20:38:31 +01:00
Jule-
8a4ae311ce Rewrite install dir resolution for legacy plugins (#589)
* Improve target-dir restriction for detecting new android project structure used in plugin.xml. (#575)

* Clarify old source-file declaration way from the new one and improve ambiguous code.

* Better check `src/main` forms.

* Replace path search with RegExp vars.

* Fix RegExp in order to match `/` or `EOL`.

* Remove template strings for NodeJS 4 support (wanted in case we port these changes to `7.1.x` at some point).

* Add pointer to deprecation plan in GH-580.
2018-11-28 06:46:45 -05:00
Chris Brody
ef2434188e Resolve issue with plugin target-dir="app*" subdirs (#572)
(subdirectories) such as "appco", with unit tests to verify

Needed for @katzer plugins that use de/appplant subdirectory,
for example:
* cordova-plugin-local-notifications
* cordova-plugin-badge
* cordova-plugin-background-mode

Also needed for cordova-plugin-inappbrowser

Co-authored-by: Christopher J. Brody <chris.brody@gmail.com>
Co-authored-by: Julio César <jcesarmobile@gmail.com>
Co-authored-by: Jan Piotrowski <piotrowski+github@gmail.com>
2018-11-22 08:59:33 -05:00
Jan Piotrowski
a014228f7c Output current package name if package name can't be validated
We have this of the package name. It only outputs that the current one is bad, not what the current one actually is. Added an output of the current one to the error.
2018-11-20 21:54:55 -08:00
エリス
2163c8ae05 Merge pull request #507 from dpogue/gradle-updates
Gradle Updates
2018-11-21 14:44:00 +09:00
Darryl Pogue
47bb5184a0 Silence some deprecation linter warnings 2018-11-20 21:13:02 -08:00
Darryl Pogue
f1f1ac3cbd Updates for latest gradle/build tools
Ref GH-504.
2018-11-20 21:12:59 -08:00
Chris Brody
f1396c7aad eslint ignore version file (#559)
Quick workaround to resolve build error

needed since cordova-coho generates version file with double-quotes
2018-11-14 20:35:47 -05:00
Chris Brody
21ae48eada Merge pull request #550 from brodybits/gh-547-bugfix
Fix for old plugins with non-Java sources (GH-547)
2018-11-14 17:44:31 -05:00
Christopher J. Brody
53e1c1b65c Set version & VERSION to 8.0.0-dev (coho) 2018-11-14 15:37:22 -05:00
Christopher J. Brody
858611f95e Update JS to version 8.0.0-dev (via coho) 2018-11-14 15:37:22 -05:00
Christopher J. Brody
5c334b6c10 CB-9366 cordova.js log error.stack (cordova-js@4.2.4)
(Update cordova.js from cordova-js@4.2.4, using coho copy-js)
2018-11-14 15:30:26 -05:00
Christopher J. Brody
cf5a02ea0f Update cordova.js from cordova-js@4.2.3
using coho copy-js

with the following change from cordova-js-src:
* Remove obsolete check for JellyBean to work properly on Android Pie
  (GH-534)
2018-11-14 15:24:48 -05:00
Christopher J. Brody
a67bc75b93 GH-547 Fix for old plugins with non-Java sources
(source-file entries)

including aidl, aar, jar, and so files
2018-11-14 13:19:58 -05:00
Christopher J. Brody
895ab0c970 Fix comments in getInstallDestination
(in pluginHandlers.js)
2018-11-14 11:40:33 -05:00
Christopher J. Brody
21f84732c8 Cleanup getInstallDestination in pluginHandlers.js 2018-11-14 11:38:39 -05:00
David Boho
7eed65e9b4 GH-552 (android) check for build-extras.gradle in the app-parent directory (#553)
as documented in https://cordova.apache.org/docs/en/latest/guide/platforms/android/?#extending-buildgradle

and deal with multiple build-extras.gradle locations

Co-authored-by: David Boho <david.boho@tu-ilmenau.de>
Co-authored-by: Christopher J. Brody <chris.brody@gmail.com>
2018-11-14 08:00:36 -05:00
Darryl Pogue
cf5915ad01 Merge pull request #551 from DavidWiesner/cast-min-sdk-version
(android) add missing cast for cdvMinSdkVersion
2018-11-13 23:06:38 -08:00
David Boho
a4caa87d29 add missing cast for cdvMinSdkVersion 2018-11-14 06:18:53 +01:00
Christopher J. Brody
bbb3913a36 Test old plugin aidl & lib mapping - repros GH-547
(reproduces GH-547)
2018-11-13 13:06:48 -05:00
Christopher J. Brody
92f07d0418 Check old compat of other extension (CB-14125)
of plugin source file installed into app/src/main with
old target-dir scheme

NOTE: These tests do *not* check compatibility of
plugins with old lib target-dir scheme.
2018-11-12 23:27:59 -05:00
Christopher J. Brody
578a642209 Check target-dir mapping of plugin xml source file
Possibly related to: CB-13830: Add handlers for plugins
that use non-Java source files, such as Camera
2018-11-12 23:27:53 -05:00
Christopher J. Brody
9d03e4ec0a Fix tests of plugin files with new app dir scheme
(new app target-dir scheme)
2018-11-12 22:54:40 -05:00
Chris Brody
576ad183e7 Merge pull request #542 from brodybits/gh-540-hotfix
Resolve GH-539 & GH-540 on master
2018-11-12 13:23:23 -05:00
Chris Brody
e31596f60e Remove obsolete check for JellyBean (GH-534) (#544)
to work properly on Android Pie

was introduced in dc0bfeb0c (CB-11828)

Resolves #534

Co-authored-by: <pradiv-kumar@users.noreply.github.com>
Co-authored-by: Christopher J. Brody <chris.brody@gmail.com>
2018-11-12 12:06:45 -05:00
Christopher J. Brody
ef493b4c0f GH-540 fix for source-file with app target-dir 2018-11-11 15:26:04 -05:00
Christopher J. Brody
c15312ee70 unit test uninstall of <source-file> with app dest
for Java source, JAR, and AAR

Co-authored-by: Christopher J. Brody <chris.brody@gmail.com>
Co-authored-by: Kyle Kirbatski <kkirbatski@gmrmarketing.com>
Co-authored-by: Antonio Facciolo <afdev82@users.noreply.github.com>
2018-11-11 15:19:13 -05:00
Christopher J. Brody
3caefcae49 unit test source-file with custom lib target-dir
for JAR and AAR

(GH-540)

Co-Authored-By: Kyle Kirbatski <kkirbatski@gmrmarketing.com>
Co-Authored-By: Christopher J. Brody <chris.brody@gmail.com>
Co-Authored-By: @afdev82 (Antonio Facciolo)
2018-11-11 15:18:00 -05:00
Christopher J. Brody
c2f6631f91 GH-539 fix destination path fallback
Fallback to old path mapping if no Android Studio path mapping exists

Co-authored-by: Christopher J. Brody <chris.brody@gmail.com>
Co-authored-by: Kyle Kirbatski <kkirbatski@gmrmarketing.com>
2018-11-11 15:16:19 -05:00
Christopher J. Brody
fb1dfb27df unit test uninstall of <source-file> with app dest
for Java source only (GH-539)

Co-Authored-By: Christopher J. Brody <chris.brody@gmail.com>
Co-Authored-By: Kyle Kirbatski <kkirbatski@gmrmarketing.com>
2018-11-11 15:15:36 -05:00
Kyle Kirbatski
898a6a8d8d Add a unit test to test source-file target-dir /app/src/main/… 2018-11-11 15:15:35 -05:00
Darryl Pogue
7da53741cd Merge pull request #538 from erisu/bump-common-3x
Dependency bump cordova-common@^3.0.0
2018-11-07 18:04:28 -08:00
エリス
47aabc631d Dependency bump cordova-common@^3.0.0 2018-11-08 09:23:01 +09:00
Darryl Pogue
fb75ac371d Merge pull request #536 from erisu/remove-node_module-copy
Only copy platform node_modules when created by binary
2018-11-06 10:06:46 -08:00
エリス
98c866704a Only copy platform node_modules when create by bin 2018-11-06 16:08:34 +09:00
Darryl Pogue
e58453d3e6 Merge pull request #465 from erisu/CB-14201
CB-14201: (android) Removes Gradle property in-line command arguments…
2018-10-31 21:25:26 -07:00
Darryl Pogue
03c62e1ce2 Merge pull request #523 from dpogue/gradle-repo-order
Always put the Google repo above jcenter
2018-10-23 10:33:57 -07:00
Darryl Pogue
2c10545cd8 Always put the Google repo above jcenter 2018-10-23 08:21:05 -07:00
Jan Piotrowski
ff2ec7eed1 remove JIRA link 2018-10-04 01:12:02 +02:00
Darryl Pogue
8dfddef6f9 Merge pull request #486 from jedrivisser/7.1.x
GH-485: (android) Replace deprecated "compile" with "implementation"
2018-09-25 22:30:46 -07:00
Chris Brody
73edf4de7b Update comments (#496)
as followup to GH-495
2018-09-12 07:52:11 -04:00
Chris Brody
cfa58a5661 Merge pull request #495 from Darshan-Chauhan/incorrect-default-sdk-version
Incorrect default sdk version issue fix
2018-09-12 07:17:16 -04:00
Darshan-Chauhan
905bff3d59 Incorrect default sdk version issue fix 2018-09-12 15:34:00 +05:30
Darryl Pogue
eadcd33e84 Merge pull request #493 from raphinesse/remove-bundled-dependencies
Remove bundled dependencies
2018-09-11 13:30:12 -07:00
Raphael von der Grün
76fe89dd1f Remove bundled node_modules 2018-09-11 21:23:24 +02:00
Raphael von der Grün
3f487c5c28 Ignore the whole node_modules folder 2018-09-11 21:20:47 +02:00
Raphael von der Grün
c35a46ec45 Remove bundledDependencies key from package.json 2018-09-11 21:18:18 +02:00
Jedri Visser
e3eb933182 GH-485: (android) Change deprecated "compile" to "implementation" 2018-09-06 22:54:26 +02:00
エリス
dacb0e5c72 CB-14201: (android) Removes Gradle property in-line command arguments for gradle.properties 2018-09-06 14:16:05 +09:00
エリス
23b24491c3 Fixes build & run related bugs from builder refactor PR #461 (#490)
- General Code Refactor
- Removed builder type argument from getBuilder API
- Removed any reference of conditional statements around builder type
- Remove plugin handler install and uninstall option flag android_studio
- Remove --gradle flag references
- Fixed plugin handler install and uninstall pathing issues
- Added parseBuildOptions export so run can get build related options.
- Use the nobuild flag option to control the run build.
- Updated test spec to reflect the changes.
2018-09-06 11:06:18 +09:00
Gearóid
7ab0cf123d Unit tests for android_sdk and AndroidProject (#464)
<!--
Please make sure the checklist boxes are all checked before submitting the PR. The checklist
is intended as a quick reference, for complete details please see our Contributor Guidelines:

http://cordova.apache.org/contribute/contribute_guidelines.html

Thanks!
-->

### Platforms affected
Android

### What does this PR do?
This is the last unit test PR for today, I promise! The `AndroidProject` tests were originally in #458. This PR also contains increased test coverage for `android_sdk.js`. I have also refactored that to remove `Q`, as with the other PRs I submitted today.

### What testing has been done on this change?
Run unit tests

### Checklist
- [x] [Reported an issue](http://cordova.apache.org/contribute/issues.html) in the JIRA database
- [x] Commit message follows the format: "CB-3232: (android) Fix bug with resolving file paths", where CB-xxxx is the JIRA ID & "android" is the platform affected.
- [x] Added automated test coverage as appropriate for this change.
2018-09-02 04:44:37 +02:00
エリス
8f2a4c7231 CB-13685 android: Adaptive Icon Support
- Update default project template's icons to be adaptive.
- Added backwards support for non-adaptive icon supported devices.
2018-08-29 10:13:14 -07:00
Darryl Pogue
ce53154555 GH-473: Do not attempt an activity for internal url loads
Merge pull request #487 from joeljeske/bugfix/473-crash-on-android-7
2018-08-29 10:10:23 -07:00
Joel Jeske
c0188ab95a Fixes apache/cordova-android#473: Do not attempt an activity intent AND a url load into the webview, return from the internal webview load. 2018-08-29 10:52:12 -05:00
Raphael von der Grün
ebbd91f87d Merge pull request #461 from Menardi/refactor_builders_with_tests
Remove old builders code
2018-07-21 16:43:34 +02:00
Gearoid M
4b0725dfc2 Remove no longer needed AndroidStudio.js 2018-07-10 11:53:52 +09:00
Gearoid M
350d35fb24 Refactor ProjectBuilder to use class instead of prototype 2018-07-10 11:53:52 +09:00
Gearoid M
8ee3a73dd1 Remove unused builders and add unit tests 2018-07-10 11:53:52 +09:00
Darryl Pogue
b6e4598e7c Merge pull request #463 from Menardi/emulator_tests
Emulator: Add unit tests and remove Q
2018-07-02 21:21:43 -07:00
Darryl Pogue
bd2ad99402 Merge pull request #462 from Menardi/device_tests
Device: Add unit tests and remove Q
2018-07-02 21:19:05 -07:00
Gearoid M
d26c1199ab Increase unit test coverage for emulator 2018-07-03 11:39:29 +09:00
Gearoid M
1ea7c1366a CB-14158: Refactor emulator to remove Q 2018-07-03 11:19:17 +09:00
Gearoid M
b2263fe35e Add unit tests for device 2018-07-03 10:43:30 +09:00
Gearoid M
bd07907a4c CB-14158: Refactor device to remove Q 2018-07-03 10:43:30 +09:00
Gearóid
ca8931c8af Remove mention of ICLA from contribution guidelines (#460) 2018-06-29 08:47:34 +08:00
Rizal M. S
46a036ef26 CB-14165 Emulator: handle "device still connecting" error (#457)
Keep waiting for emulator when connection fails with "device still connecting" error
2018-06-25 08:44:37 +02:00
Raphael von der Grün
e9750b5cab Merge pull request #456 from Menardi/more_android_tests
More android tests
2018-06-22 03:17:19 +02:00
Gearoid M
0bf0e48698 Prevent Api.js tests from polluting the reporter output 2018-06-22 10:02:31 +09:00
Gearoid M
4f1aa97ea9 Add unit tests for AndroidManifest.js 2018-06-22 10:02:31 +09:00
Gearoid M
f90f004dea Add unit tests for Adb.js 2018-06-22 10:02:31 +09:00
Gearoid M
559b0833b4 Add unit tests for run and retryPromise
Co-authored-by: Raphael von der Grün <raphinesse@gmail.com>
2018-06-20 13:24:50 +02:00
Raphael von der Grün
31bad290d5 Allow mocks to reject without a reason 2018-06-20 13:24:50 +02:00
Gearoid M
2e88f7ead2 Update Jasmine to support automatic promise handling 2018-06-20 13:24:50 +02:00
Gearoid M
1a8154c90e Fix unsafe property access 2018-06-20 13:24:50 +02:00
Gearoid M
8fb49ec7ec Fix typo in variable name in retry.js 2018-06-20 13:24:50 +02:00
Raphael von der Grün
393dad6349 Lint JS files w/out extension too (#453)
Prior to this change, JS files without extension had not been linted.

To match them, we need a combination of positive and negative globbing
that does not mix well with the existing file selection for linting. Thus, we use
npm-run-all to run two ESLint invocations unconditionally in sequence
while still preserving the error code.

* Lint JS w/out extension too
* Fix newly surfaced linting errors
* Make use of npm-run-all for test script too
2018-06-18 23:48:02 +02:00
Raphael von der Grün
2c3db19310 Emit log event instead of logging directly 2018-06-16 20:08:44 +02:00
jcesarmobile
472ab626d6 Merge pull request #449 from jcesarmobile/CB-14125
CB-14125:(android) Increase old plugin compatibility
2018-06-13 23:00:55 +02:00
Raphael von der Grün
2106e2e081 Merge pull request #442 from raphinesse/fix-tests
Fixes and cleanup for Java tests and CI
2018-06-13 16:28:29 +02:00
Raphael von der Grün
d3c80ea5d5 CB-14072 Update supported Node.js versions 2018-06-13 16:09:12 +02:00
Raphael von der Grün
6dad25668c Delete unused gradle-wrapper.properties for Java tests 2018-06-13 16:09:11 +02:00
Raphael von der Grün
d862d42e76 Unignore tracked build.gradle for Java tests 2018-06-13 16:09:10 +02:00
Raphael von der Grün
828b5f053a Cleanup CI configurations
* Use latest Android SDK tools for easier license handling
* Reduce installed SDK components to minimum (tools & build tools)
* Reduce unnecessary PATH manipulation
* Use preinstalled Gradle on Travis CI
* Improve Gradle output on Travis CI
* Use default image on AppVeyor
* Improve formatting & other minor tweaks
2018-06-13 16:09:09 +02:00
Raphael von der Grün
032d1fa9cb Cleanup Java tests runner
* Print Gradle version used for Java tests during build
* Allow Java tests to be run from any directory
* Simplify Promise wrapping in Java tests runner
* Minor improvements
2018-06-13 16:09:08 +02:00
Raphael von der Grün
83ad646734 Fix Java tests on AppVeyor by disabling Gradle daemon 2018-06-13 16:09:07 +02:00
Raphael von der Grün
88c25a6a6a Quick fix for Java tests on Travis CI
This accepts any unaccepted Android SDK licenses in Travis.
2018-06-13 16:09:06 +02:00
Raphael von der Grün
a254cfc841 Fix Java tests for older Gradle versions
Before this, Gradle 4.4 was required to build the Gradle wrapper and
thus run the Java tests. This was because of all the stuff that had to
be configured when running the wrapper task using the build.gradle file.

Now we use a config file that only specifies the required Gradle version
and nothing else to run the wrapper task. This allows tests to be run
with Gradle versions beginning with 2.
2018-06-13 16:09:04 +02:00
Raphael von der Grün
0d4b9f4ba6 Fix output and exit code for Java tests
This fixes the following issues:
* run_java_unit_tests.js always has exit code of 0 thus never failing
  the npm tests.
* "Tests completed successfully" is printed after failing to create the
  Gradle wrapper and never running the tests.
* Gradle errors are printed twice
2018-06-13 16:09:03 +02:00
Raphael von der Grün
bf29fe0e10 CB-14101 Fix Java version check for Java >= 9 (#446)
This also checks that we have exactly 1.8 since nothing else works with
the Android SDK. The user facing error was updated accordingly.
2018-06-12 22:18:55 +02:00
jcesarmobile
3df8f8b120 Merge pull request #450 from jcesarmobile/CB-14127
CB-14127: (android) Move google maven repo ahead of jcenter
2018-06-12 11:46:50 +02:00
Julio César
38d0e684f1 CB-14127: (android) Move google maven repo ahead of jcenter 2018-06-11 23:12:58 +02:00
Julio César
83e9aefff5 CB-14125:(android) Increase old plugin compatibility 2018-06-11 00:48:24 +02:00
Raphael von der Grün
02ee925103 Remove obsolete jshint comments (#444) 2018-05-21 18:02:14 +08:00
Shazron Abdullah
fc69da7a42 Merge pull request #443 from raphinesse/eslint
Improve ESLint script
2018-05-21 18:00:44 +08:00
Raphael von der Grün
5dc179ebef Improve ESLint script
It now lints all files in one go and lints a few extra files.
2018-05-19 23:15:15 +02:00
Joe Bowser
59e3b907e9 Merge pull request #437 from jcesarmobile/CB-14038
CB-14038 (android): fix false positive detecting project type

I agree @jcesarmobile, it can wait for a future release.
2018-04-19 11:08:54 -07:00
Julio César
fe7629e8fc CB-14038 (android): fix false positive detecting project type 2018-04-18 13:52:04 +02:00
Joe Bowser
76180d3ea1 Merge pull request #436 from infil00p/update_gradle_deps
CB-14008: Updating Gradle Libraries to work with Android Studio 3.1.0
2018-04-10 13:58:31 -07:00
Joe Bowser
5c4f8ca246 CB-14008: Updating Gradle Libraries to work with Android Studio 3.1.0 2018-04-04 13:54:39 -07:00
Joe Bowser
b8530a6b70 Merge pull request #435 from richturner/master
CB-13975: (android) Fix to fire pause event when cdvStartInBackground=true
2018-03-27 13:50:26 -07:00
Joe Bowser
83686542b2 Merge pull request #434 from infil00p/CB-13830
CB-13830: Add handlers for plugins that use non-Java source files
2018-03-26 16:08:22 -07:00
Rich Turner
b1551cad98 CB-13975: (android) Fix to fire pause event when cdvStartInBackground=true 2018-03-23 20:19:52 +00:00
Joe Bowser
5af6b016c1 CB-13830: Add handlers for plugins that use non-Java source files, such as Camera 2018-03-19 13:20:09 -07:00
Joe Bowser
6abd6d6b47 Merge pull request #433 from AnthonyWard/master
CB-13923 (android) fix -1 length for compressed files
2018-03-07 13:26:28 -08:00
Anthony Ward
e456a325f9 CB-13923 (android) fix -1 length for compressed files 2018-02-27 12:39:30 +00:00
Steve Gill
eddad666ff Update JS snapshot to version 7.2.0-dev (via coho) 2018-02-21 10:52:02 -08:00
Steve Gill
ca9a25e860 Set VERSION to 7.2.0-dev (via coho) 2018-02-21 10:52:01 -08:00
1064 changed files with 15628 additions and 98627 deletions

22
.asf.yaml Normal file
View File

@@ -0,0 +1,22 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
notifications:
commits: commits@cordova.apache.org
issues: issues@cordova.apache.org
pullrequests_status: issues@cordova.apache.org
pullrequests_comment: issues@cordova.apache.org

View File

@@ -1 +1,3 @@
bin/templates/project/assets/www/cordova.js
bin/templates/project/assets/www/cordova.js
test/android/app
test/androidx/app

View File

@@ -1,10 +1,27 @@
root: true
extends: semistandard
rules:
indent:
- error
- 4
camelcase: off
padded-blocks: off
operator-linebreak: off
no-throw-literal: off
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
extends: '@cordova/eslint-config/node'
overrides:
- files: [spec/**/*.js]
extends: '@cordova/eslint-config/node-tests'
rules:
prefer-promise-reject-errors: off
- files: [cordova-js-src/**/*.js]
extends: '@cordova/eslint-config/browser'

42
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,42 @@
<!--
Please have a look at the issue templates you get when you click "New issue" in the GitHub UI.
We very much prefer issues created by using one of these templates.
-->
### Issue Type
<!-- Please check the boxes by putting an x in the [ ] like so: [x] -->
- [ ] Bug Report
- [ ] Feature Request
- [ ] Support Question
## Description
## Information
<!-- Include all relevant information that might help understand and reproduce the problem -->
### Command or Code
<!-- What command or code is needed to reproduce the problem? -->
### Environment, Platform, Device
<!-- In what environment, on what platform or on which device are you experiencing the issue? -->
### Version information
<!--
What are relevant versions you are using?
For example:
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
Other Frameworks: Ionic Framework and CLI version
Operating System, Android Studio, Xcode etc.
-->
## Checklist
<!-- Please check the boxes by putting an `x` in the `[ ]` like so: `[x]` -->
- [ ] I searched for already existing GitHub issues about this
- [ ] I updated all Cordova tooling to their most recent version
- [ ] I included all the necessary information above

50
.github/ISSUE_TEMPLATE/BUG_REPORT.md vendored Normal file
View File

@@ -0,0 +1,50 @@
---
name: 🐛 Bug Report
about: If something isn't working as expected.
---
# Bug Report
## Problem
### What is expected to happen?
### What does actually happen?
## Information
<!-- Include all relevant information that might help understand and reproduce the problem -->
### Command or Code
<!-- What command or code is needed to reproduce the problem? -->
### Environment, Platform, Device
<!-- In what environment, on what platform or on which device are you experiencing the issue? -->
### Version information
<!--
What are relevant versions you are using?
For example:
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
Other Frameworks: Ionic Framework and CLI version
Operating System, Android Studio, Xcode etc.
-->
## Checklist
<!-- Please check the boxes by putting an x in the [ ] like so: [x] -->
- [ ] I searched for existing GitHub issues
- [ ] I updated all Cordova tooling to most recent version
- [ ] I included all the necessary information above

View File

@@ -0,0 +1,29 @@
---
name: 🚀 Feature Request
about: A suggestion for a new functionality
---
# Feature Request
## Motivation Behind Feature
<!-- Why should this feature be implemented? What problem does it solve? -->
## Feature Description
<!--
Describe your feature request in detail
Please provide any code examples or screenshots of what this feature would look like
Are there any drawbacks? Will this break anything for existing users?
-->
## Alternatives or Workarounds
<!--
Describe alternatives or workarounds you are currently using
Are there ways to do this with existing functionality?
-->

View File

@@ -0,0 +1,27 @@
---
name: 💬 Support Question
about: If you have a question, please check out our Slack or StackOverflow!
---
<!------------^ Click "Preview" for a nicer view! -->
Apache Cordova uses GitHub Issues as a feature request and bug tracker _only_.
For usage and support questions, please check out the resources below. Thanks!
---
You can get answers to your usage and support questions about **Apache Cordova** on:
* Slack Community Chat: https://cordova.slack.com (you can sign-up at http://slack.cordova.io/)
* StackOverflow: https://stackoverflow.com/questions/tagged/cordova using the tag `cordova`
---
If you are using a tool that uses Cordova internally, like e.g. Ionic, check their support channels:
* **Ionic Framework**
* [Ionic Community Forum](https://forum.ionicframework.com/)
* [Ionic Worldwide Slack](https://ionicworldwide.herokuapp.com/)
* **PhoneGap**
* [PhoneGap Developer Community](https://forums.adobe.com/community/phonegap)

View File

@@ -1,6 +1,5 @@
<!--
Please make sure the checklist boxes are all checked before submitting the PR. The checklist
is intended as a quick reference, for complete details please see our Contributor Guidelines:
Please make sure the checklist boxes are all checked before submitting the PR. The checklist is intended as a quick reference, for complete details please see our Contributor Guidelines:
http://cordova.apache.org/contribute/contribute_guidelines.html
@@ -10,13 +9,27 @@ Thanks!
### Platforms affected
### What does this PR do?
### Motivation and Context
<!-- Why is this change required? What problem does it solve? -->
<!-- If it fixes an open issue, please link to the issue here. -->
### What testing has been done on this change?
### Description
<!-- Describe your changes in detail -->
### Testing
<!-- Please describe in detail how you tested your changes. -->
### Checklist
- [ ] [Reported an issue](http://cordova.apache.org/contribute/issues.html) in the JIRA database
- [ ] Commit message follows the format: "CB-3232: (android) Fix bug with resolving file paths", where CB-xxxx is the JIRA ID & "android" is the platform affected.
- [ ] Added automated test coverage as appropriate for this change.
- [ ] I've run the tests to see all new and existing tests pass
- [ ] I added automated test coverage as appropriate for this change
- [ ] Commit is prefixed with `(platform)` if this change only applies to one platform (e.g. `(android)`)
- [ ] If this Pull Request resolves an issue, I linked to the issue in the text above (and used the correct [keyword to close issues using keywords](https://help.github.com/articles/closing-issues-using-keywords/))
- [ ] I've updated the documentation if necessary

61
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
name: Node CI
on: [push, pull_request]
jobs:
test:
name: NodeJS ${{ matrix.node-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [10.x, 12.x]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Environment Information
run: |
node --version
npm --version
gradle --version
- name: npm install and test
run: |
npm i
npm t
env:
CI: true
- uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true

109
.gitignore vendored
View File

@@ -26,10 +26,14 @@ example
/framework/javadoc-public
/framework/javadoc-private
/test/.externalNativeBuild
/test/build.gradle
/test/gradle
/test/gradlew
/test/gradlew.bat
/test/android/gradle
/test/android/gradlew
/test/android/gradlew.bat
/test/androidx/gradle
/test/androidx/gradlew
/test/androidx/gradlew.bat
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/bin
@@ -43,93 +47,10 @@ tmp/**/*
**/.idea/**/*
*.iml
npm-debug.log
node_modules/jshint
node_modules/promise-matchers
node_modules/jasmine
node_modules/rewire
node_modules/istanbul
node_modules/.bin/cake
node_modules/.bin/coffee
node_modules/.bin/escodegen
node_modules/.bin/esgenerate
node_modules/.bin/esparse
node_modules/.bin/esvalidate
node_modules/.bin/handlebars
node_modules/.bin/istanbul
node_modules/.bin/jasmine
node_modules/.bin/js-yaml
node_modules/.bin/jshint
node_modules/.bin/mkdirp
node_modules/.bin/r.js
node_modules/.bin/r_js
node_modules/.bin/strip-json-comments
node_modules/.bin/uglifyjs
node_modules/.bin/which
node_modules/align-text/
node_modules/amdefine/
node_modules/argparse/
node_modules/async/
node_modules/camelcase/
node_modules/center-align/
node_modules/cli/
node_modules/cliui/
node_modules/coffee-script/
node_modules/console-browserify/
node_modules/core-util-is/
node_modules/date-now/
node_modules/decamelize/
node_modules/deep-is/
node_modules/dom-serializer/
node_modules/domelementtype/
node_modules/domhandler/
node_modules/domutils/
node_modules/entities/
node_modules/escodegen/
node_modules/esprima/
node_modules/estraverse/
node_modules/esutils/
node_modules/exit/
node_modules/fast-levenshtein/
node_modules/fileset/
node_modules/gaze/
node_modules/growl/
node_modules/handlebars/
node_modules/has-flag/
node_modules/htmlparser2/
node_modules/is-buffer/
node_modules/isarray/
node_modules/isexe/
node_modules/jasmine-growl-reporter/
node_modules/jasmine-reporters/
node_modules/js-yaml/
node_modules/kind-of/
node_modules/lazy-cache/
node_modules/levn/
node_modules/longest/
node_modules/lru-cache/
node_modules/minimist/
node_modules/mkdirp/
node_modules/optimist/
node_modules/optionator/
node_modules/prelude-ls/
node_modules/readable-stream/
node_modules/repeat-string/
node_modules/requirejs/
node_modules/resolve/
node_modules/right-align/
node_modules/sigmund/
node_modules/source-map/
node_modules/sprintf-js/
node_modules/string_decoder/
node_modules/strip-json-comments/
node_modules/supports-color/
node_modules/type-check/
node_modules/uglify-js/
node_modules/uglify-to-browserify/
node_modules/walkdir/
node_modules/which/
node_modules/window-size/
node_modules/wordwrap/
node_modules/yargs/
node_modules/jasmine-core/
node_modules/fs.realpath/
node_modules/
coverage/
.nyc_output/
# Eclipse Buildship files
.project
.settings
.classpath

5
.npmignore Normal file
View File

@@ -0,0 +1,5 @@
.*
coverage
test
spec
framework/build

View File

@@ -3,7 +3,6 @@ bin
gen
proguard-project.txt
spec
appveyor.yml
framework/build
ic_launcher.png
build

View File

@@ -4,5 +4,5 @@
GUESS_FIELDS = True
OPEN_BROWSER = True
TARGET_GROUPS = 'cordova'
REVIEWBOARD_URL = 'http://reviews.apache.org'
REVIEWBOARD_URL = 'https://reviews.apache.org'

View File

@@ -1,28 +0,0 @@
language: android
sudo: false
jdk:
- oraclejdk8
env:
global:
- ANDROID_TOOLS=${ANDROID_HOME}/tools
before_install:
- nvm install 6
# ensure at least gradle 3.3 is in place.
- wget http://services.gradle.org/distributions/gradle-3.3-bin.zip
- unzip gradle-3.3-bin.zip
- export GRADLE_HOME=$PWD/gradle-3.3
- export PATH=${GRADLE_HOME}/bin:${ANDROID_HOME}:${ANDROID_HOME}/emulator:${ANDROID_TOOLS}:${ANDROID_TOOLS}/bin:${ANDROID_HOME}/platform-tools:$PATH
- node --version
- gradle --version
- echo y | android --silent update sdk --no-ui --all --filter platform-tools,tools,build-tools-26.0.2,android-26,android-25,extra-google-m2repository,extra-android-m2repository
android:
components:
- tools
install:
- npm install
- npm install -g codecov
script:
- npm test
- npm run cover
after_script:
- codecov

View File

@@ -25,13 +25,12 @@ Anyone can contribute to Cordova. And we need your contributions.
There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/contribute/).
The details are explained there, but the important items are:
- Sign and submit an Apache ICLA (Contributor License Agreement).
- Have a Jira issue open that corresponds to your contribution.
- Check for Github issues that corresponds to your contribution and link or create them if necessary.
- Run the tests so your patch doesn't break existing functionality.
We look forward to your contributions!

101
LICENSE
View File

@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Apache Cordova
Copyright 2015-2020 Apache Cordova
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -207,7 +207,7 @@
bin/node_modules/q
================================================================================
Copyright 20092012 Kristopher Michael Kowal. All rights reserved.
Copyright 20092017 Kristopher Michael Kowal. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
@@ -226,89 +226,42 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
================================================================================
bin/node_modules/shelljs
================================================================================
Copyright (c) 2012, Artur Adib <aadib@mozilla.com>
All rights reserved.
You may use this project under the terms of the New BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Artur Adib nor the
names of the contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
bin/node_modules/nopt
================================================================================
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The ISC License
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
Copyright (c) Isaac Z. Schlueter and Contributors
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
================================================================================
bin/node_modules/which
================================================================================
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
All rights reserved.
The ISC License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
Copyright (c) Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

14
NOTICE
View File

@@ -1,15 +1,5 @@
Apache Cordova
Copyright 2015 The Apache Software Foundation
Copyright 2015-2020 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org)
=========================================================================
== NOTICE file corresponding to the section 4 d of ==
== the Apache License, Version 2.0, ==
== in this case for the Android-specific code. ==
=========================================================================
This product includes software developed as part of
The Android Open Source Project (http://source.android.com).
The Apache Software Foundation (http://www.apache.org/).

View File

@@ -19,26 +19,21 @@
#
-->
[![Build status](https://ci.appveyor.com/api/projects/status/github/apache/cordova-android?branch=master)](https://ci.appveyor.com/project/Humbedooh/cordova-android)
[![Build Status](https://travis-ci.org/apache/cordova-android.svg?branch=master)](https://travis-ci.org/apache/cordova-android)
[![codecov.io](https://codecov.io/github/apache/cordova-android/coverage.svg?branch=master)](https://codecov.io/github/apache/cordova-android?branch=master)
# Cordova Android
Cordova Android is an Android application library that allows for Cordova-based
projects to be built for the Android Platform. Cordova based applications are,
at the core, applications written with web technology: HTML, CSS and JavaScript.
[![NPM](https://nodei.co/npm/cordova-android.png)](https://nodei.co/npm/cordova-android/)
[![Node CI](https://github.com/apache/cordova-android/workflows/Node%20CI/badge.svg?branch=master)](https://github.com/apache/cordova-android/actions?query=branch%3Amaster)
[![codecov.io](https://codecov.io/github/apache/cordova-android/coverage.svg?branch=master)](https://codecov.io/github/apache/cordova-android?branch=master)
Cordova Android is an Android application library that allows for Cordova-based projects to be built for the Android Platform. Cordova based applications are, at the core, applications written with web technology: HTML, CSS and JavaScript.
[Apache Cordova](https://cordova.apache.org) is a project of The Apache Software Foundation (ASF).
:warning: Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Android%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC)
## Requires
- Java JDK 1.8 or greater
- Android SDK [http://developer.android.com](http://developer.android.com)
- Java JDK 1.8
- Android SDK [http://developer.android.com](https://developer.android.com/)
## Cordova Android Developer Tools
@@ -65,6 +60,4 @@ These commands live in a generated Cordova Android project. Any interactions wit
## Running the Native Tests
The `test/` directory in this project contains an Android test project that can
be used to run different kinds of native tests. Check out the
[README contained therein](test/README.md) for more details!
The `test/` directory in this project contains an Android test project that can be used to run different kinds of native tests. Check out the [README contained therein](test/README.md) for more details!

View File

@@ -18,7 +18,157 @@
# under the License.
#
-->
## Release Notes for Cordova (Android) ##
## Release Notes for Cordova (Android)
### 9.0.0 (Jun 23, 2020)
* [GH-1005](https://github.com/apache/cordova-android/pull/1005) chore: set AndroidX off by default
* [GH-971](https://github.com/apache/cordova-android/pull/971) fix: Accept multiple mime types on file input
* [GH-1001](https://github.com/apache/cordova-android/pull/1001) fix: support both adaptive and standard icons at the same time
* [GH-985](https://github.com/apache/cordova-android/pull/985) fix: Plugin install fails when preview sdk is installed
* [GH-994](https://github.com/apache/cordova-android/pull/994) chore: cleanup yaml files
* [GH-999](https://github.com/apache/cordova-android/pull/999) chore: remove trailing spaces from Java sources
* [GH-992](https://github.com/apache/cordova-android/pull/992) chore: update some dependencies
* [GH-998](https://github.com/apache/cordova-android/pull/998) chore: remove trailing spaces from framework build files
* [GH-997](https://github.com/apache/cordova-android/pull/997) chore: remove trailing spaces from project template
* [GH-996](https://github.com/apache/cordova-android/pull/996) chore: remove trailing spaces from bat files
* [GH-995](https://github.com/apache/cordova-android/pull/995) remove trailing spaces from markdown files
* [GH-993](https://github.com/apache/cordova-android/pull/993) chore: update `devDependencies`
* [GH-987](https://github.com/apache/cordova-android/pull/987) breaking: reduce combined response cutoff to 16 MB
* [GH-988](https://github.com/apache/cordova-android/pull/988) major: Gradle 6.5 & **Android** Gradle plugin 4.0.0 updates
* [GH-990](https://github.com/apache/cordova-android/pull/990) chore: remove trailing spaces from `app/build.gradle`
* [GH-989](https://github.com/apache/cordova-android/pull/989) breaking: remove `legacy/build.gradle` from template
* [GH-978](https://github.com/apache/cordova-android/pull/978) fix: `wait_for_boot` waiting forever
* [GH-965](https://github.com/apache/cordova-android/pull/965) fix: Increased `detectArchitecture()` timeout
* [GH-962](https://github.com/apache/cordova-android/pull/962) breaking: Bump **Android** gradle plugin to 3.6.0
* [GH-948](https://github.com/apache/cordova-android/pull/948) feature: JVM Args flag
* [GH-951](https://github.com/apache/cordova-android/pull/951) fix: `ANDROID_SDK_ROOT` variable
* [GH-959](https://github.com/apache/cordova-android/pull/959) test: synced AndroidX gradle versions to the same version as the **Android** test
* [GH-960](https://github.com/apache/cordova-android/pull/960) feat: `com.android.tools.build:gradle:3.5.3`
* [GH-956](https://github.com/apache/cordova-android/pull/956) chore(npm): add `package-lock.json`
* [GH-958](https://github.com/apache/cordova-android/pull/958) chore(npm): add ignore list
* [GH-957](https://github.com/apache/cordova-android/pull/957) chore: various cleanup
* [GH-955](https://github.com/apache/cordova-android/pull/955) chore(eslint): bump package & apply eslint fix
* [GH-954](https://github.com/apache/cordova-android/pull/954) breaking(npm): bump packages
* [GH-953](https://github.com/apache/cordova-android/pull/953) chore(npm): use short notation in `package.json`
* [GH-823](https://github.com/apache/cordova-android/pull/823) fix: prevent exit fullscreen mode from closing application
* [GH-950](https://github.com/apache/cordova-android/pull/950) fix: removed redundent logcat print
* [GH-915](https://github.com/apache/cordova-android/pull/915) breaking: bump minSdkVersion to 22 and drop pre-Lollipop specific code
* [GH-941](https://github.com/apache/cordova-android/pull/941) fix: GH-873 App bundle builds to obey command-line arguments
* [GH-940](https://github.com/apache/cordova-android/pull/940) ci: drop travis & move codecov to gh-actions
* [GH-929](https://github.com/apache/cordova-android/pull/929) chore: updated `README` to reflect what **Android** requires more accurately, which is Java 8, not anything less, not anything greater. Java 1.8.x is required.
* [GH-937](https://github.com/apache/cordova-android/pull/937) fix: GH-935 replaced `compare-func` with native sort method
* [GH-939](https://github.com/apache/cordova-android/pull/939) fix: test failure with shebang interpreter in `rewired` files
* [GH-911](https://github.com/apache/cordova-android/pull/911) refactor: use es6 class
* [GH-910](https://github.com/apache/cordova-android/pull/910) refactor (eslint): use `cordova-eslint`
* [GH-909](https://github.com/apache/cordova-android/pull/909) chore: remove appveyor residual
* [GH-895](https://github.com/apache/cordova-android/pull/895) feat: add github actions
* [GH-842](https://github.com/apache/cordova-android/pull/842) refactor: remove `shelljs` dependency
* [GH-896](https://github.com/apache/cordova-android/pull/896) feat: add Kotlin support
* [GH-901](https://github.com/apache/cordova-android/pull/901) feat: add AndroidX support
* [GH-849](https://github.com/apache/cordova-android/pull/849) fix: cordova requirements consider the `android-targetSdkVersion`
* [GH-904](https://github.com/apache/cordova-android/pull/904) fix (adb): shell to return expected stdout
* [GH-792](https://github.com/apache/cordova-android/pull/792) feat: upgrade `gradle` to 6.1 & gradle build tools to 3.5.3
* [GH-902](https://github.com/apache/cordova-android/pull/902) chore: remove `.project` file & add `.settings` to `gitignore`
* [GH-900](https://github.com/apache/cordova-android/pull/900) refactor: simplify `doFindLatestInstalledBuildTools`
* [GH-751](https://github.com/apache/cordova-android/pull/751) feat: use Java package name for loading `BuildConfig`
* [GH-898](https://github.com/apache/cordova-android/pull/898) chore: rename gradle plugin google services `preference` options
* [GH-893](https://github.com/apache/cordova-android/pull/893) feat: add Google Services support
* [GH-709](https://github.com/apache/cordova-android/pull/709) feat: add `version-compare` library to compare `build-tools` versions properly.
* [GH-831](https://github.com/apache/cordova-android/pull/831) chore: ignore auto-generated eclipse buildship files
* [GH-848](https://github.com/apache/cordova-android/pull/848) breaking: increased default target sdk to 29
* [GH-859](https://github.com/apache/cordova-android/pull/859) breaking: removed unnecessary project name restriction
* [GH-833](https://github.com/apache/cordova-android/pull/833) chore: drop `q` module
* [GH-862](https://github.com/apache/cordova-android/pull/862) chore: replace `superspawn` & `child_process` with `execa`
* [GH-860](https://github.com/apache/cordova-android/pull/860) feat: don't filter gradle's stderr anymore
* [GH-832](https://github.com/apache/cordova-android/pull/832) chore: drop node 6 and 8 support
* [GH-890](https://github.com/apache/cordova-android/pull/890) chore: bump version to 9.0.0-dev
* [GH-697](https://github.com/apache/cordova-android/pull/697) chore: optimization code
* [GH-863](https://github.com/apache/cordova-android/pull/863) chore: removed comment that serves no purpose
* [GH-861](https://github.com/apache/cordova-android/pull/861) chore: update `jasmine` to 3.5.0
* [GH-858](https://github.com/apache/cordova-android/pull/858) chore: modernize our one E2E test
* [GH-854](https://github.com/apache/cordova-android/pull/854) chore: ensure to lint as many files as possible
### 8.1.0 (Sep 11, 2019)
* [GH-827](https://github.com/apache/cordova-android/pull/827) chore: bump dependencies for release 8.1.0
* [GH-651](https://github.com/apache/cordova-android/pull/651) feat: added multiple selection for filepicker
* [GH-672](https://github.com/apache/cordova-android/pull/672) chore: compress files in /res with tinypng.com
* [GH-815](https://github.com/apache/cordova-android/pull/815) fix: `clean` command
* [GH-750](https://github.com/apache/cordova-android/pull/750) Don't request focus explicitly if not needed
* [GH-800](https://github.com/apache/cordova-android/pull/800) [GH-799](https://github.com/apache/cordova-android/pull/799) (android) Stop webview from restarting when activity resizes
* [GH-764](https://github.com/apache/cordova-android/pull/764) feat: Build app bundles (.aab files)
* [GH-788](https://github.com/apache/cordova-android/pull/788) Simplify `apkSorter` using `compare-func` package
* [GH-787](https://github.com/apache/cordova-android/pull/787) Simplify and fix promise handling in specs
* [GH-784](https://github.com/apache/cordova-android/pull/784) Properly handle promise in create script
* [GH-783](https://github.com/apache/cordova-android/pull/783) Do not clobber process properties with test mocks
* [GH-782](https://github.com/apache/cordova-android/pull/782) Do not clobber `console.log` to spy on it
* [GH-724](https://github.com/apache/cordova-android/pull/724) Add Node.js 12 to CI Services
* [GH-777](https://github.com/apache/cordova-android/pull/777) ci(travis): set `dist: trusty` in `.travis.yml`
* [GH-779](https://github.com/apache/cordova-android/pull/779) Consistent order from `ProjectBuilder.apkSorter`
* [GH-778](https://github.com/apache/cordova-android/pull/778) test: use verbose spec reporter
* [GH-774](https://github.com/apache/cordova-android/pull/774) `rewire` workaround for NodeJS 12
* [GH-772](https://github.com/apache/cordova-android/pull/772) `nyc@14` update in devDependencies
* [GH-765](https://github.com/apache/cordova-android/pull/765) ci(travis): Fix **Android** SDK
* [GH-713](https://github.com/apache/cordova-android/pull/713) Do not explicitly require modules from project directory
* [GH-676](https://github.com/apache/cordova-android/pull/676) Added allprojects repositories for Framework Release Builds
* [GH-699](https://github.com/apache/cordova-android/pull/699) Improve Gradle Build Arguments
* [GH-710](https://github.com/apache/cordova-android/pull/710) Fix deprecation warning in `SystemCookieManager`
* [GH-691](https://github.com/apache/cordova-android/pull/691) [GH-690](https://github.com/apache/cordova-android/pull/690): Run `prepare` with the correct `ConfigParser`
* [GH-673](https://github.com/apache/cordova-android/pull/673) Updated `Android_HOME` Test to Follow [GH-656](https://github.com/apache/cordova-android/pull/656) Change
### 8.0.0 (Feb 13, 2019)
* [GH-669](https://github.com/apache/cordova-android/pull/669) Added Missing License Headers
* [GH-655](https://github.com/apache/cordova-android/pull/655) Use custom Gradle properties to read minSdkVersion value from `config.xml`
* [GH-656](https://github.com/apache/cordova-android/pull/656) Quick fix to support **Android**_SDK_ROOT
* [GH-632](https://github.com/apache/cordova-android/pull/632) Ignore more Gradle build artifacts in **Android** project
* [GH-642](https://github.com/apache/cordova-android/pull/642) **Android** tools 3.3 & **Gradle** 4.10.3 update
* [GH-654](https://github.com/apache/cordova-android/pull/654) Quick updates to top-level `project.properties`
* [GH-635](https://github.com/apache/cordova-android/pull/635) Ignore **Android** Studio `.idea` files in project
* [GH-624](https://github.com/apache/cordova-android/pull/624) Add missing log to Java version check
* [GH-630](https://github.com/apache/cordova-android/pull/630) Update `emulator.js` to fix issue [GH-608](https://github.com/apache/cordova-android/pull/608)
* [GH-626](https://github.com/apache/cordova-android/pull/626) Added `package-lock.json` to `.gitignore`
* [GH-620](https://github.com/apache/cordova-android/pull/620) Fix requirements error messages for JDK 8
* [GH-619](https://github.com/apache/cordova-android/pull/619) javac error message fixes in requirements check
* [GH-612](https://github.com/apache/cordova-android/pull/612) Android Platform Release Preparation (Cordova 9)
* [GH-607](https://github.com/apache/cordova-android/pull/607) Copy `node_modules` if the directory exists
* [GH-582](https://github.com/apache/cordova-android/pull/582) Improve Test `README`
* [GH-589](https://github.com/apache/cordova-android/pull/589) Rewrite install dir resolution for legacy plugins
* [GH-572](https://github.com/apache/cordova-android/pull/572) Resolve issue with plugin `target-dir="app*"` subdirs
* [GH-567](https://github.com/apache/cordova-android/pull/567) Output current package name if package name can't be validated
* [GH-507](https://github.com/apache/cordova-android/pull/507) Gradle Updates
* [GH-559](https://github.com/apache/cordova-android/pull/559) Eslint ignore version file
* [GH-550](https://github.com/apache/cordova-android/pull/550) Fix for old plugins with non-Java sources
* [GH-558](https://github.com/apache/cordova-android/pull/558) Update `cordova.js` from `cordova-js@4.2.3`
* [GH-553](https://github.com/apache/cordova-android/pull/553) Check for `build-extras.gradle` in the app-parent directory
* [GH-551](https://github.com/apache/cordova-android/pull/551) Add missing cast for `cdvMinSdkVersion`
* [GH-539](https://github.com/apache/cordova-android/pull/539) Fix destination path fallback
* [GH-544](https://github.com/apache/cordova-android/pull/544) Remove obsolete check for JellyBean
* [GH-465](https://github.com/apache/cordova-android/pull/465) Removes Gradle property in-line command arguments for `gradle.properties`
* [GH-523](https://github.com/apache/cordova-android/pull/523) Always put the Google repo above jcenter
* [GH-486](https://github.com/apache/cordova-android/pull/486) Change deprecated "compile" to "implementation"
* [GH-495](https://github.com/apache/cordova-android/pull/495) Incorrect default sdk version issue fix
* [GH-493](https://github.com/apache/cordova-android/pull/493) Remove bundled dependencies
* [GH-490](https://github.com/apache/cordova-android/pull/490) Fixes build & run related bugs from builder refactor
* [GH-464](https://github.com/apache/cordova-android/pull/464) Unit tests for **Android**_sdk and **Android**Project
* [GH-448](https://github.com/apache/cordova-android/pull/448) [CB-13685](https://issues.apache.org/jira/browse/CB-13685) Adaptive Icon Support
* [GH-487](https://github.com/apache/cordova-android/pull/487) Do not attempt an activity intent AND a url load into the webview, return from the internal webview load.
* [GH-461](https://github.com/apache/cordova-android/pull/461) Remove old builders code
* [GH-463](https://github.com/apache/cordova-android/pull/463) Emulator: Add unit tests and remove Q
* [GH-462](https://github.com/apache/cordova-android/pull/462) Device: Add unit tests and remove Q
* [GH-457](https://github.com/apache/cordova-android/pull/457) Emulator: handle "device still connecting" error
* [GH-445](https://github.com/apache/cordova-android/pull/445) Run and retryPromise improvements and tests
* [GH-453](https://github.com/apache/cordova-android/pull/453) Lint JS files w/out extension too
* [GH-452](https://github.com/apache/cordova-android/pull/452) Emit log event instead of logging directly
* [GH-449](https://github.com/apache/cordova-android/pull/449) Increase old plugin compatibility
* [GH-442](https://github.com/apache/cordova-android/pull/442) Fixes and cleanup for Java tests and CI
* [GH-446](https://github.com/apache/cordova-android/pull/446) [CB-14101](https://issues.apache.org/jira/browse/CB-14101) Fix Java version check for Java >= 9
* [CB-14127](https://issues.apache.org/jira/browse/CB-14127) Move google maven repo ahead of jcenter
* [CB-14038](https://issues.apache.org/jira/browse/CB-14038) Fix false positive detecting project type
* [CB-14008](https://issues.apache.org/jira/browse/CB-14008) Updating Gradle Libraries to work with **Android** Studio 3.1.0
* [CB-13975](https://issues.apache.org/jira/browse/CB-13975) Fix to fire pause event when cdvStartInBackground=true
* [CB-13830](https://issues.apache.org/jira/browse/CB-13830) Add handlers for plugins that use non-Java source files, such as Camera
* [CB-13923](https://issues.apache.org/jira/browse/CB-13923) Fix -1 length for compressed files
### 7.1.0 (Feb 20, 2018)
* [CB-13879](https://issues.apache.org/jira/browse/CB-13879) updated gradle tools dependency to 3.0.1 for project template

View File

@@ -1 +1 @@
7.1.0-dev
9.0.0

View File

@@ -1,38 +0,0 @@
image:
- Previous Visual Studio 2015
environment:
ANDROID_HOME: "C:\\android"
matrix:
- nodejs_version: "4"
- nodejs_version: "6"
- nodejs_version: "8"
init:
- mkdir "%ANDROID_HOME%
- cd "%ANDROID_HOME%"
- appveyor DownloadFile "https://dl.google.com/android/repository/tools_r25.2.3-windows.zip"
- 7z x "tools_r25.2.3-windows.zip" > nul
- cd "C:\projects\cordova-android"
install:
- choco install gradle -version 3.4.1
- gradle -version
- echo y | "%ANDROID_HOME%\tools\android.bat" --silent update sdk --no-ui --all --filter platform-tools,tools,build-tools-26.0.2,android-26,android-25,extra-google-m2repository,extra-android-m2repository
# on windows we need to accept sublicenses for the new tooling, wee. 30 is an arbitrary number,
# but should be the maximum number of licenses we explicitly need to type "Y ENTER" for.
# also, the sdkmanager in all its glory leaks a bit of output to stderr, and powershell
# and appveyor interpret that as errors, and blows up. so, when piping in our "Y ENTER"
# responses, we invoke cmd so we can redirect stderr to stdout, and tell it to --update itself.
- ps: for($i=0;$i -lt 30;$i++) { $response += "y`n"}; $response | cmd /c 'C:\android\tools\bin\sdkmanager.bat 2>&1' --update
- ps: Install-Product node $env:nodejs_version
- npm install
# below is a workaround on using gradle installed via choco on appveyor
- set path=C:\ProgramData\chocolatey\lib\gradle\tools\gradle-3.4.1\bin;%path%
build: off
test_script:
- node --version
- npm --version
- npm test

View File

@@ -21,9 +21,7 @@
var android_sdk = require('./templates/cordova/lib/android_sdk');
android_sdk.print_newest_available_sdk_target().done(null, function(err) {
android_sdk.print_newest_available_sdk_target().catch(err => {
console.error(err);
process.exit(2);
});

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

View File

@@ -21,10 +21,11 @@
var check_reqs = require('./templates/cordova/lib/check_reqs');
check_reqs.run().done(
function success() {
check_reqs.run().then(
function success () {
console.log('Looks like your environment fully supports cordova-android development!');
}, function fail(err) {
},
function fail (err) {
console.log(err);
process.exit(2);
}

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

28
bin/clean_test.js Normal file
View File

@@ -0,0 +1,28 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const fs = require('fs');
if (fs.existsSync('./test/gradlew')) {
fs.unlinkSync('./test/gradlew');
}
if (fs.existsSync('./test/gradlew.bat')) {
fs.unlinkSync('./test/gradlew.bat');
}

View File

@@ -23,12 +23,12 @@ var ConfigParser = require('cordova-common').ConfigParser;
var Api = require('./templates/cordova/Api');
var argv = require('nopt')({
'help' : Boolean,
'cli' : Boolean,
'shared' : Boolean,
'link' : Boolean,
'activity-name' : [String, undefined]
}, { 'd' : '--verbose' });
help: Boolean,
cli: Boolean,
shared: Boolean,
link: Boolean,
'activity-name': [String, undefined]
}, { d: '--verbose' });
if (argv.help || argv.argv.remain.length === 0) {
console.log('Usage: ' + path.relative(process.cwd(), path.join(__dirname, 'create')) + ' <path_to_new_project> <package_name> <project_name> [<template_path>] [--activity-name <activity_name>] [--link]');
@@ -55,4 +55,7 @@ var options = {
require('./templates/cordova/loggingHelper').adjustLoggerLevel(argv);
Api.createPlatform(argv.argv.remain[0], config, options).done();
Api.createPlatform(argv.argv.remain[0], config, options).catch(err => {
console.error(err);
process.exitCode = 1;
});

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -23,4 +23,4 @@ IF EXIST %script_path% (
ECHO.
ECHO ERROR: Could not find 'create' script in 'bin' folder, aborting...>&2
EXIT /B 1
)
)

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,10 +17,9 @@
under the License.
*/
var shell = require('shelljs');
var Q = require('q');
var path = require('path');
var fs = require('fs');
var fs = require('fs-extra');
var utils = require('../../bin/lib/utils');
var check_reqs = require('./../templates/cordova/lib/check_reqs');
var ROOT = path.join(__dirname, '..', '..');
@@ -34,20 +31,12 @@ var AndroidManifest = require('../templates/cordova/lib/AndroidManifest');
// (since we can then mock and control behaviour of all of these functions)
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;
exports.setShellFatal = setShellFatal;
exports.copyJsAndLibrary = copyJsAndLibrary;
exports.copyScripts = copyScripts;
exports.copyBuildRules = copyBuildRules;
exports.writeProjectProperties = writeProjectProperties;
exports.prepBuildFiles = prepBuildFiles;
function setShellFatal (value, func) {
var oldVal = shell.config.fatal;
shell.config.fatal = value;
func();
shell.config.fatal = oldVal;
}
function getFrameworkDir (projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
}
@@ -56,53 +45,33 @@ function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js');
var app_path = path.join(projectPath, 'app', 'src', 'main');
const platform_www = path.join(projectPath, 'platform_www');
if (isLegacy) {
app_path = projectPath;
}
shell.cp('-f', srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
fs.copySync(srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
// Copy the cordova.js file to platforms/<platform>/platform_www/
// The www dir is nuked on each prepare so we keep cordova.js in platform_www
shell.mkdir('-p', path.join(projectPath, 'platform_www'));
shell.cp('-f', srcCordovaJsPath, path.join(projectPath, 'platform_www'));
fs.ensureDirSync(platform_www);
fs.copySync(srcCordovaJsPath, path.join(platform_www, 'cordova.js'));
// Copy cordova-js-src directory into platform_www directory.
// We need these files to build cordova.js if using browserify method.
shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www'));
fs.copySync(path.join(ROOT, 'cordova-js-src'), path.join(platform_www, 'cordova-js-src'));
// Don't fail if there are no old jars.
exports.setShellFatal(false, function () {
shell.ls(path.join(app_path, 'libs', 'cordova-*.jar')).forEach(function (oldJar) {
console.log('Deleting ' + oldJar);
shell.rm('-f', oldJar);
});
var wasSymlink = true;
try {
// Delete the symlink if it was one.
fs.unlinkSync(nestedCordovaLibPath);
} catch (e) {
wasSymlink = false;
}
// Delete old library project if it existed.
if (shared) {
shell.rm('-rf', nestedCordovaLibPath);
} else if (!wasSymlink) {
// Delete only the src, since Eclipse / Android Studio can't handle their project files being deleted.
shell.rm('-rf', path.join(nestedCordovaLibPath, 'src'));
}
});
if (shared) {
var relativeFrameworkPath = path.relative(projectPath, getFrameworkDir(projectPath, true));
fs.symlinkSync(relativeFrameworkPath, nestedCordovaLibPath, 'dir');
} else {
shell.mkdir('-p', nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'build.gradle'), nestedCordovaLibPath);
shell.cp('-f', path.join(ROOT, 'framework', 'cordova.gradle'), nestedCordovaLibPath);
shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath);
fs.ensureDirSync(nestedCordovaLibPath);
fs.copySync(path.join(ROOT, 'framework', 'AndroidManifest.xml'), path.join(nestedCordovaLibPath, 'AndroidManifest.xml'));
fs.copySync(path.join(ROOT, 'framework', 'project.properties'), path.join(nestedCordovaLibPath, 'project.properties'));
fs.copySync(path.join(ROOT, 'framework', 'build.gradle'), path.join(nestedCordovaLibPath, 'build.gradle'));
fs.copySync(path.join(ROOT, 'framework', 'cordova.gradle'), path.join(nestedCordovaLibPath, 'cordova.gradle'));
fs.copySync(path.join(ROOT, 'framework', 'src'), path.join(nestedCordovaLibPath, 'src'));
}
}
@@ -141,9 +110,9 @@ function writeProjectProperties (projectPath, target_api) {
}
// This makes no sense, what if you're building with a different build system?
function prepBuildFiles (projectPath, builder) {
var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders'));
buildModule.getBuilder(builder).prepBuildFiles();
function prepBuildFiles (projectPath) {
var buildModule = require('../templates/cordova/lib/builders/builders');
buildModule.getBuilder(projectPath).prepBuildFiles();
}
function copyBuildRules (projectPath, isLegacy) {
@@ -151,12 +120,12 @@ function copyBuildRules (projectPath, isLegacy) {
if (isLegacy) {
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
shell.cp('-f', path.join(srcDir, 'legacy', 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
fs.copySync(path.join(srcDir, 'legacy', 'build.gradle'), path.join(projectPath, 'legacy', 'build.gradle'));
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
} else {
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app'));
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
fs.copySync(path.join(srcDir, 'build.gradle'), path.join(projectPath, 'build.gradle'));
fs.copySync(path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app', 'build.gradle'));
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
}
}
@@ -165,21 +134,29 @@ function copyScripts (projectPath) {
var srcScriptsDir = path.join(bin, 'templates', 'cordova');
var destScriptsDir = path.join(projectPath, 'cordova');
// Delete old scripts directory if this is an update.
shell.rm('-rf', destScriptsDir);
fs.removeSync(destScriptsDir);
// Copy in the new ones.
shell.cp('-r', srcScriptsDir, projectPath);
shell.cp('-r', path.join(ROOT, 'node_modules'), destScriptsDir);
shell.cp(path.join(bin, 'check_reqs*'), destScriptsDir);
shell.cp(path.join(bin, 'android_sdk_version*'), destScriptsDir);
fs.copySync(srcScriptsDir, destScriptsDir);
const nodeModulesDir = path.join(ROOT, 'node_modules');
if (fs.existsSync(nodeModulesDir)) fs.copySync(nodeModulesDir, path.join(destScriptsDir, 'node_modules'));
fs.copySync(path.join(bin, 'check_reqs'), path.join(destScriptsDir, 'check_reqs'));
fs.copySync(path.join(bin, 'check_reqs.bat'), path.join(destScriptsDir, 'check_reqs.bat'));
fs.copySync(path.join(bin, 'android_sdk_version'), path.join(destScriptsDir, 'android_sdk_version'));
fs.copySync(path.join(bin, 'android_sdk_version.bat'), path.join(destScriptsDir, 'android_sdk_version.bat'));
var check_reqs = path.join(destScriptsDir, 'check_reqs');
var android_sdk_version = path.join(destScriptsDir, 'android_sdk_version');
// TODO: the two files being edited on-the-fly here are shared between
// platform and project-level commands. the below `sed` is updating the
// platform and project-level commands. the below is updating the
// `require` path for the two libraries. if there's a better way to share
// modules across both the repo and generated projects, we should make sure
// to remove/update this.
shell.sed('-i', /templates\/cordova\//, '', android_sdk_version);
shell.sed('-i', /templates\/cordova\//, '', check_reqs);
const templatesCordovaRegex = /templates\/cordova\//;
utils.replaceFileContents(android_sdk_version, templatesCordovaRegex, '');
utils.replaceFileContents(check_reqs, templatesCordovaRegex, '');
}
/**
@@ -194,19 +171,19 @@ function validatePackageName (package_name) {
var msg = 'Error validating package name. ';
if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) {
return Q.reject(new CordovaError(msg + 'Package name must look like: com.company.Name'));
return Promise.reject(new CordovaError(msg + 'Must look like: `com.company.Name`. Currently is: `' + package_name + '`'));
}
// Class is a reserved word
if (/\b[Cc]lass\b/.test(package_name)) {
return Q.reject(new CordovaError(msg + '"class" is a reserved word'));
return Promise.reject(new CordovaError(msg + '"class" is a reserved word'));
}
return Q.resolve();
return Promise.resolve();
}
/**
* Test whether a project name is acceptable for use as an android class.
* Test whether given string is acceptable for use as a project name
* Returns a promise, fulfilled if the project name is acceptable; rejected
* otherwise.
*/
@@ -214,20 +191,10 @@ function validateProjectName (project_name) {
var msg = 'Error validating project name. ';
// Make sure there's something there
if (project_name === '') {
return Q.reject(new CordovaError(msg + 'Project name cannot be empty'));
return Promise.reject(new CordovaError(msg + 'Project name cannot be empty'));
}
// Enforce stupid name error
if (project_name === 'CordovaActivity') {
return Q.reject(new CordovaError(msg + 'Project name cannot be CordovaActivity'));
}
// Classes in Java don't begin with numbers
if (/^[0-9]/.test(project_name)) {
return Q.reject(new CordovaError(msg + 'Project name must not begin with a number'));
}
return Q.resolve();
return Promise.resolve();
}
/**
@@ -249,19 +216,18 @@ function validateProjectName (project_name) {
* @return {Promise<String>} Directory where application has been created
*/
exports.create = function (project_path, config, options, events) {
options = options || {};
// Set default values for path, package and name
project_path = path.relative(process.cwd(), (project_path || 'CordovaExample'));
// Check if project already exists
if (fs.existsSync(project_path)) {
return Q.reject(new CordovaError('Project already exists! Delete and recreate'));
return Promise.reject(new CordovaError('Project already exists! Delete and recreate'));
}
var package_name = config.android_packageName() || config.packageName() || 'my.cordova.project';
var project_name = config.name() ?
config.name().replace(/[^\w.]/g, '_') : 'CordovaExample';
var project_name = config.name()
? config.name().replace(/[^\w.]/g, '_') : 'CordovaExample';
var safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity';
var target_api = check_reqs.get_target();
@@ -269,7 +235,7 @@ exports.create = function (project_path, config, options, events) {
// Make the package conform to Java package types
return exports.validatePackageName(package_name)
.then(function () {
exports.validateProjectName(project_name);
return exports.validateProjectName(project_name);
}).then(function () {
// Log the given values for the project
events.emit('log', 'Creating Cordova project for the Android platform:');
@@ -281,57 +247,55 @@ exports.create = function (project_path, config, options, events) {
events.emit('verbose', 'Copying android template project to ' + project_path);
exports.setShellFatal(true, function () {
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main');
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main');
// copy project template
shell.mkdir('-p', app_path);
shell.cp('-r', path.join(project_template_dir, 'assets'), app_path);
shell.cp('-r', path.join(project_template_dir, 'res'), app_path);
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
// copy project template
fs.ensureDirSync(app_path);
fs.copySync(path.join(project_template_dir, 'assets'), path.join(app_path, 'assets'));
fs.copySync(path.join(project_template_dir, 'res'), path.join(app_path, 'res'));
fs.copySync(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir(path.join(app_path, 'libs'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
fs.ensureDirSync(path.join(app_path, 'libs'));
// copy cordova.js, cordova.jar
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name);
// copy cordova.js, cordova.jar
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name);
// Set up ther Android Studio paths
var java_path = path.join(app_path, 'java');
var assets_path = path.join(app_path, 'assets');
var resource_path = path.join(app_path, 'res');
shell.mkdir('-p', java_path);
shell.mkdir('-p', assets_path);
shell.mkdir('-p', resource_path);
// Set up ther Android Studio paths
var java_path = path.join(app_path, 'java');
var assets_path = path.join(app_path, 'assets');
var resource_path = path.join(app_path, 'res');
fs.ensureDirSync(java_path);
fs.ensureDirSync(assets_path);
fs.ensureDirSync(resource_path);
// interpolate the activity name and package
var packagePath = package_name.replace(/\./g, path.sep);
var activity_dir = path.join(java_path, packagePath);
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
// interpolate the activity name and package
var packagePath = package_name.replace(/\./g, path.sep);
var activity_dir = path.join(java_path, packagePath);
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
shell.mkdir('-p', activity_dir);
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path);
shell.sed('-i', /__NAME__/, project_name, path.join(app_path, 'res', 'values', 'strings.xml'));
shell.sed('-i', /__ID__/, package_name, activity_path);
fs.ensureDirSync(activity_dir);
fs.copySync(path.join(project_template_dir, 'Activity.java'), activity_path);
utils.replaceFileContents(activity_path, /__ACTIVITY__/, safe_activity_name);
utils.replaceFileContents(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, project_name);
utils.replaceFileContents(activity_path, /__ID__/, package_name);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.setPackageId(package_name)
.setTargetSdkVersion(target_api.split('-')[1])
.getActivity().setName(safe_activity_name);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
manifest.setPackageId(package_name)
.getActivity().setName(safe_activity_name);
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
exports.copyScripts(project_path);
exports.copyBuildRules(project_path);
exports.copyScripts(project_path);
exports.copyBuildRules(project_path);
});
// Link it to local android install.
exports.writeProjectProperties(project_path, target_api);
exports.prepBuildFiles(project_path, 'studio');
exports.prepBuildFiles(project_path);
events.emit('log', generateDoneMessage('create', options.link));
}).thenResolve(project_path);
}).then(() => project_path);
};
function generateDoneMessage (type, link) {
@@ -345,7 +309,6 @@ function generateDoneMessage (type, link) {
// Returns a promise.
exports.update = function (projectPath, options, events) {
var errorString =
'An in-place platform update is not supported. \n' +
'The `platforms` folder is always treated as a build artifact in the CLI workflow.\n' +
@@ -356,5 +319,5 @@ exports.update = function (projectPath, options, events) {
'\tcordova platform add android\n'
;
return Q.reject(errorString);
return Promise.reject(errorString);
};

47
bin/lib/utils.js Normal file
View File

@@ -0,0 +1,47 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
/*
Provides a set of utility methods, which can also be spied on during unit tests.
*/
// TODO: Perhaps this should live in cordova-common?
const fs = require('fs-extra');
/**
* Reads, searches, and replaces the found occurences with replacementString and then writes the file back out.
* A backup is not made.
*
* @param {string} file A file path to a readable & writable file
* @param {RegExp} searchRegex The search regex
* @param {string} replacementString The string to replace the found occurences
* @returns {void}
*/
exports.replaceFileContents = function (file, searchRegex, replacementString) {
// let contents;
try {
var contents = fs.readFileSync(file).toString();
} catch (ex) {
console.log('TRYING TO READ: ', file);
throw ex;
}
contents = contents.replace(searchRegex, replacementString);
fs.writeFileSync(file, contents);
};

View File

@@ -17,15 +17,24 @@
under the License.
*/
/**
* @todo update coho to update this line.
* @todo use `package.json` instead but first
* figure out how this fit in with the platform-centered workflow structure.
* This workflow would not have the `package.json` file.
*/
// Coho updates this line
const VERSION = '9.0.0';
var path = require('path');
var Q = require('q');
var AndroidProject = require('./lib/AndroidProject');
var AndroidStudio = require('./lib/AndroidStudio');
var PluginManager = require('cordova-common').PluginManager;
var CordovaLogger = require('cordova-common').CordovaLogger;
var selfEvents = require('cordova-common').events;
var ConfigParser = require('cordova-common').ConfigParser;
const prepare = require('./lib/prepare').prepare;
var PLATFORM = 'android';
@@ -52,360 +61,324 @@ function setupEvents (externalEventEmitter) {
* The PlatformApi instance also should define the following field:
*
* * platform: String that defines a platform name.
* @class Api
*/
function Api (platform, platformRootDir, events) {
this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..');
this.builder = 'gradle';
class Api {
constructor (platform, platformRootDir, events) {
this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..');
setupEvents(events);
setupEvents(events);
var self = this;
const appMain = path.join(this.root, 'app', 'src', 'main');
const appRes = path.join(appMain, 'res');
this.locations = {
root: self.root,
www: path.join(self.root, 'assets/www'),
res: path.join(self.root, 'res'),
platformWww: path.join(self.root, 'platform_www'),
configXml: path.join(self.root, 'res/xml/config.xml'),
defaultConfigXml: path.join(self.root, 'cordova/defaults.xml'),
strings: path.join(self.root, 'res/values/strings.xml'),
manifest: path.join(self.root, 'AndroidManifest.xml'),
build: path.join(self.root, 'build'),
javaSrc: path.join(self.root, 'src'),
// NOTE: Due to platformApi spec we need to return relative paths here
cordovaJs: 'bin/templates/project/assets/www/cordova.js',
cordovaJsSrc: 'cordova-js-src'
};
// XXX Override some locations for Android Studio projects
if (AndroidStudio.isAndroidStudioProject(self.root) === true) {
selfEvents.emit('log', 'Android Studio project detected');
this.builder = 'studio';
this.android_studio = true;
this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml');
this.locations.strings = path.join(self.root, 'app/src/main/res/values/strings.xml');
this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml');
// We could have Java Source, we could have other languages
this.locations.javaSrc = path.join(self.root, 'app/src/main/java/');
this.locations.www = path.join(self.root, 'app/src/main/assets/www');
this.locations.res = path.join(self.root, 'app/src/main/res');
}
}
/**
* Installs platform to specified directory and creates a platform project.
*
* @param {String} destination Destination directory, where insatll platform to
* @param {ConfigParser} [config] ConfgiParser instance, used to retrieve
* project creation options, such as package id and project name.
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
Api.createPlatform = function (destination, config, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'createPlatform is not callable from the android project API.');
throw (e);
}
return result;
};
/**
* Updates already installed platform.
*
* @param {String} destination Destination directory, where platform installed
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
Api.updatePlatform = function (destination, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('android', destination, events);
});
} catch (e) {
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
throw (e);
}
return result;
};
/**
* Gets a CordovaPlatform object, that represents the platform structure.
*
* @return {CordovaPlatform} A structure that contains the description of
* platform's file structure and other properties of platform.
*/
Api.prototype.getPlatformInfo = function () {
var result = {};
result.locations = this.locations;
result.root = this.root;
result.name = this.platform;
result.version = require('./version');
result.projectConfig = this._config;
return result;
};
/**
* Updates installed platform with provided www assets and new app
* configuration. This method is required for CLI workflow and will be called
* each time before build, so the changes, made to app configuration and www
* code, will be applied to platform.
*
* @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a
* project structure and configuration, that should be applied to platform
* (contains project's www location and ConfigParser instance for project's
* config).
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.prepare = function (cordovaProject, prepareOptions) {
return require('./lib/prepare').prepare.call(this, cordovaProject, prepareOptions);
};
/**
* Installs a new plugin into platform. This method only copies non-www files
* (sources, libs, etc.) to platform. It also doesn't resolves the
* dependencies of plugin. Both of handling of www files, such as assets and
* js-files and resolving dependencies are the responsibility of caller.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
* @param {Object} installOptions An options object. Possible options below:
* @param {Boolean} installOptions.link: Flag that specifies that plugin
* sources will be symlinked to app's directory instead of copying (if
* possible).
* @param {Object} installOptions.variables An object that represents
* variables that will be used to install plugin. See more details on plugin
* variables in documentation:
* https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.addPlugin = function (plugin, installOptions) {
var project = AndroidProject.getProjectFile(this.root);
var self = this;
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
this.locations = {
root: this.root,
www: path.join(appMain, 'assets', 'www'),
res: appRes,
platformWww: path.join(this.root, 'platform_www'),
configXml: path.join(appRes, 'xml', 'config.xml'),
defaultConfigXml: path.join(this.root, 'cordova', 'defaults.xml'),
strings: path.join(appRes, 'values', 'strings.xml'),
manifest: path.join(appMain, 'AndroidManifest.xml'),
build: path.join(this.root, 'build'),
javaSrc: path.join(appMain, 'java')
};
}
if (this.android_studio === true) {
installOptions.android_studio = true;
/**
* Gets a CordovaPlatform object, that represents the platform structure.
*
* @return {CordovaPlatform} A structure that contains the description of
* platform's file structure and other properties of platform.
*/
getPlatformInfo () {
var result = {};
result.locations = this.locations;
result.root = this.root;
result.name = this.platform;
result.version = Api.version();
result.projectConfig = this._config;
return result;
}
return Q().then(function () {
// CB-11964: Do a clean when installing the plugin code to get around
// the Gradle bug introduced by the Android Gradle Plugin Version 2.2
// TODO: Delete when the next version of Android Gradle plugin comes out
// Since clean doesn't just clean the build, it also wipes out www, we need
// to pass additional options.
/**
* Updates installed platform with provided www assets and new app
* configuration. This method is required for CLI workflow and will be called
* each time before build, so the changes, made to app configuration and www
* code, will be applied to platform.
*
* @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a
* project structure and configuration, that should be applied to platform
* (contains project's www location and ConfigParser instance for project's
* config).
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
prepare (cordovaProject, prepareOptions) {
cordovaProject.projectConfig = new ConfigParser(cordovaProject.locations.rootConfigXml || cordovaProject.projectConfig.path);
// Do some basic argument parsing
var opts = {};
return prepare.call(this, cordovaProject, prepareOptions);
}
// Skip cleaning prepared files when not invoking via cordova CLI.
opts.noPrepare = true;
/**
* Installs a new plugin into platform. This method only copies non-www files
* (sources, libs, etc.) to platform. It also doesn't resolves the
* dependencies of plugin. Both of handling of www files, such as assets and
* js-files and resolving dependencies are the responsibility of caller.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
* @param {Object} installOptions An options object. Possible options below:
* @param {Boolean} installOptions.link: Flag that specifies that plugin
* sources will be symlinked to app's directory instead of copying (if
* possible).
* @param {Object} installOptions.variables An object that represents
* variables that will be used to install plugin. See more details on plugin
* variables in documentation:
* https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
addPlugin (plugin, installOptions) {
var project = AndroidProject.getProjectFile(this.root);
var self = this;
if (!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) {
return self.clean(opts);
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
}).then(function () {
return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions);
}).then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
// This should pick the correct builder, not just get gradle
require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
};
/**
* Removes an installed plugin from platform.
*
* Since method accepts PluginInfo instance as input parameter instead of plugin
* id, caller shoud take care of managing/storing PluginInfo instances for
* future uninstalls.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
Api.prototype.removePlugin = function (plugin, uninstallOptions) {
var project = AndroidProject.getProjectFile(this.root);
if (uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) {
uninstallOptions.usePlatformWww = false;
uninstallOptions.android_studio = true;
}
return PluginManager.get(this.platform, this.locations, project)
.removePlugin(plugin, uninstallOptions)
.then(function () {
return Promise.resolve().then(function () {
return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions);
}).then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder(this.builder).prepBuildFiles();
// This should pick the correct builder, not just get gradle
require('./lib/builders/builders').getBuilder().prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
};
/**
* Builds an application package for current platform.
*
* @param {Object} buildOptions A build options. This object's structure is
* highly depends on platform's specific. The most common options are:
* @param {Boolean} buildOptions.debug Indicates that packages should be
* built with debug configuration. This is set to true by default unless the
* 'release' option is not specified.
* @param {Boolean} buildOptions.release Indicates that packages should be
* built with release configuration. If not set to true, debug configuration
* will be used.
* @param {Boolean} buildOptions.device Specifies that built app is intended
* to run on device
* @param {Boolean} buildOptions.emulator: Specifies that built app is
* intended to run on emulator
* @param {String} buildOptions.target Specifies the device id that will be
* used to run built application.
* @param {Boolean} buildOptions.nobuild Indicates that this should be a
* dry-run call, so no build artifacts will be produced.
* @param {String[]} buildOptions.archs Specifies chip architectures which
* app packages should be built for. List of valid architectures is depends on
* platform.
* @param {String} buildOptions.buildConfig The path to build configuration
* file. The format of this file is depends on platform.
* @param {String[]} buildOptions.argv Raw array of command-line arguments,
* passed to `build` command. The purpose of this property is to pass a
* platform-specific arguments, and eventually let platform define own
* arguments processing logic.
*
* @return {Promise<Object[]>} A promise either fulfilled with an array of build
* artifacts (application packages) if package was built successfully,
* or rejected with CordovaError. The resultant build artifact objects is not
* strictly typed and may conatin arbitrary set of fields as in sample below.
*
* {
* architecture: 'x86',
* buildType: 'debug',
* path: '/path/to/build',
* type: 'app'
* }
*
* The return value in most cases will contain only one item but in some cases
* there could be multiple items in output array, e.g. when multiple
* arhcitectures is specified.
*/
Api.prototype.build = function (buildOptions) {
var self = this;
if (this.android_studio) {
buildOptions.studio = true;
// CB-11022 Return truthy value to prevent running prepare after
.then(() => true);
}
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').run.call(self, buildOptions);
}).then(function (buildResults) {
// Cast build result to array of build artifacts
return buildResults.apkPaths.map(function (apkPath) {
return {
buildType: buildResults.buildType,
buildMethod: buildResults.buildMethod,
path: apkPath,
type: 'apk'
};
/**
* Removes an installed plugin from platform.
*
* Since method accepts PluginInfo instance as input parameter instead of plugin
* id, caller shoud take care of managing/storing PluginInfo instances for
* future uninstalls.
*
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
* that will be installed.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError instance.
*/
removePlugin (plugin, uninstallOptions) {
var project = AndroidProject.getProjectFile(this.root);
if (uninstallOptions && uninstallOptions.usePlatformWww === true) {
uninstallOptions.usePlatformWww = false;
}
return PluginManager.get(this.platform, this.locations, project)
.removePlugin(plugin, uninstallOptions)
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder().prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.then(() => true);
}
/**
* Builds an application package for current platform.
*
* @param {Object} buildOptions A build options. This object's structure is
* highly depends on platform's specific. The most common options are:
* @param {Boolean} buildOptions.debug Indicates that packages should be
* built with debug configuration. This is set to true by default unless the
* 'release' option is not specified.
* @param {Boolean} buildOptions.release Indicates that packages should be
* built with release configuration. If not set to true, debug configuration
* will be used.
* @param {Boolean} buildOptions.device Specifies that built app is intended
* to run on device
* @param {Boolean} buildOptions.emulator: Specifies that built app is
* intended to run on emulator
* @param {String} buildOptions.target Specifies the device id that will be
* used to run built application.
* @param {Boolean} buildOptions.nobuild Indicates that this should be a
* dry-run call, so no build artifacts will be produced.
* @param {String[]} buildOptions.archs Specifies chip architectures which
* app packages should be built for. List of valid architectures is depends on
* platform.
* @param {String} buildOptions.buildConfig The path to build configuration
* file. The format of this file is depends on platform.
* @param {String[]} buildOptions.argv Raw array of command-line arguments,
* passed to `build` command. The purpose of this property is to pass a
* platform-specific arguments, and eventually let platform define own
* arguments processing logic.
*
* @return {Promise<Object[]>} A promise either fulfilled with an array of build
* artifacts (application packages) if package was built successfully,
* or rejected with CordovaError. The resultant build artifact objects is not
* strictly typed and may conatin arbitrary set of fields as in sample below.
*
* {
* architecture: 'x86',
* buildType: 'debug',
* path: '/path/to/build',
* type: 'app'
* }
*
* The return value in most cases will contain only one item but in some cases
* there could be multiple items in output array, e.g. when multiple
* arhcitectures is specified.
*/
build (buildOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').run.call(self, buildOptions);
}).then(function (buildResults) {
// Cast build result to array of build artifacts
return buildResults.paths.map(function (apkPath) {
return {
buildType: buildResults.buildType,
buildMethod: buildResults.buildMethod,
path: apkPath,
type: path.extname(apkPath).replace(/\./g, '')
};
});
});
});
};
}
/**
* Builds an application package for current platform and runs it on
* specified/default device. If no 'device'/'emulator'/'target' options are
* specified, then tries to run app on default device if connected, otherwise
* runs the app on emulator.
*
* @param {Object} runOptions An options object. The structure is the same
* as for build options.
*
* @return {Promise} A promise either fulfilled if package was built and ran
* successfully, or rejected with CordovaError.
*/
Api.prototype.run = function (runOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/run').run.call(self, runOptions);
});
};
/**
* Builds an application package for current platform and runs it on
* specified/default device. If no 'device'/'emulator'/'target' options are
* specified, then tries to run app on default device if connected, otherwise
* runs the app on emulator.
*
* @param {Object} runOptions An options object. The structure is the same
* as for build options.
*
* @return {Promise} A promise either fulfilled if package was built and ran
* successfully, or rejected with CordovaError.
*/
run (runOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/run').run.call(self, runOptions);
});
}
/**
* Cleans out the build artifacts from platform's directory, and also
* cleans out the platform www directory if called without options specified.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError.
*/
Api.prototype.clean = function (cleanOptions) {
var self = this;
if (this.android_studio) {
/**
* Cleans out the build artifacts from platform's directory, and also
* cleans out the platform www directory if called without options specified.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError.
*/
clean (cleanOptions) {
var self = this;
// This will lint, checking for null won't
if (typeof cleanOptions === 'undefined') {
cleanOptions = {};
}
cleanOptions.studio = true;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').runClean.call(self, cleanOptions);
}).then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions);
});
}
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').runClean.call(self, cleanOptions);
}).then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions);
});
};
/**
* Performs a requirements check for current platform. Each platform defines its
* own set of requirements, which should be resolved before platform can be
* built successfully.
*
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
* objects for current platform.
*/
requirements () {
return require('./lib/check_reqs').check_all();
}
/**
* Performs a requirements check for current platform. Each platform defines its
* own set of requirements, which should be resolved before platform can be
* built successfully.
*
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
* objects for current platform.
*/
Api.prototype.requirements = function () {
return require('./lib/check_reqs').check_all();
};
/**
* Installs platform to specified directory and creates a platform project.
*
* @param {String} destination Destination directory, where insatll platform to
* @param {ConfigParser} [config] ConfgiParser instance, used to retrieve
* project creation options, such as package id and project name.
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
static createPlatform (destination, config, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'createPlatform is not callable from the android project API.');
throw (e);
}
return result;
}
/**
* Updates already installed platform.
*
* @param {String} destination Destination directory, where platform installed
* @param {Object} [options] An options object. The most common options are:
* @param {String} [options.customTemplate] A path to custom template, that
* should override the default one from platform.
* @param {Boolean} [options.link] Flag that indicates that platform's
* sources will be linked to installed platform instead of copying.
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
* logging purposes. If no EventEmitter provided, all events will be logged to
* console
*
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
* instance or rejected with CordovaError.
*/
static updatePlatform (destination, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
return new Api(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
throw (e);
}
return result;
}
static version () {
return VERSION;
}
}
module.exports = Api;

View File

@@ -19,24 +19,25 @@
under the License.
*/
var args = process.argv;
var args = process.argv;
var Api = require('./Api');
var nopt = require('nopt');
var path = require('path');
// Support basic help commands
if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0)
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(args[2]) >= 0) {
require('./lib/build').help();
}
// Do some basic argument parsing
var buildOpts = nopt({
'verbose' : Boolean,
'silent' : Boolean,
'debug' : Boolean,
'release' : Boolean,
'nobuild': Boolean,
'buildConfig' : path
}, { 'd' : '--verbose' });
verbose: Boolean,
silent: Boolean,
debug: Boolean,
release: Boolean,
nobuild: Boolean,
buildConfig: path
}, { d: '--verbose' });
// Make buildOptions compatible with PlatformApi build method spec
buildOpts.argv = buildOpts.argv.original;
@@ -44,7 +45,7 @@ buildOpts.argv = buildOpts.argv.original;
require('./loggingHelper').adjustLoggerLevel(buildOpts);
new Api().build(buildOpts)
.catch(function(err) {
console.error(err.stack);
process.exit(2);
});
.catch(function (err) {
console.error(err.stack);
process.exit(2);
});

View File

@@ -20,11 +20,11 @@
*/
var Api = require('./Api');
var path = require('path');
var path = require('path');
var nopt = require('nopt');
// Support basic help commands
if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
console.log('Cleans the project directory.');
process.exit(0);
@@ -32,9 +32,9 @@ if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >=
// Do some basic argument parsing
var opts = nopt({
'verbose' : Boolean,
'silent' : Boolean
}, { 'd' : '--verbose' });
verbose: Boolean,
silent: Boolean
}, { d: '--verbose' });
// Make buildOptions compatible with PlatformApi clean method spec
opts.argv = opts.argv.original;
@@ -45,7 +45,7 @@ opts.noPrepare = true;
require('./loggingHelper').adjustLoggerLevel(opts);
new Api().clean(opts)
.catch(function(err) {
console.error(err.stack);
process.exit(2);
});
.catch(function (err) {
console.error(err.stack);
process.exit(2);
});

View File

@@ -17,10 +17,9 @@
under the License.
*/
var Q = require('q');
var os = require('os');
var execa = require('execa');
var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var Adb = {};
@@ -44,7 +43,7 @@ function isEmulator (line) {
* devices/emulators
*/
Adb.devices = function (opts) {
return spawn('adb', ['devices'], {cwd: os.tmpdir()}).then(function (output) {
return execa('adb', ['devices'], { cwd: os.tmpdir() }).then(({ stdout: output }) => {
return output.split('\n').filter(function (line) {
// Filter out either real devices or emulators, depending on options
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
@@ -58,7 +57,7 @@ Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r');
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}).then(function (output) {
return execa('adb', args.concat(packagePath), { cwd: os.tmpdir() }).then(({ stdout: output }) => {
// 'adb install' seems to always returns no error, even if installation fails
// so we catching output to detect installation failure
if (output.match(/Failure/)) {
@@ -70,31 +69,30 @@ Adb.install = function (target, packagePath, opts) {
'\nEither uninstall an app or increment the versionCode.';
}
return Q.reject(new CordovaError('Failed to install apk to device: ' + output));
return Promise.reject(new CordovaError('Failed to install apk to device: ' + output));
}
});
};
Adb.uninstall = function (target, packageId) {
events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...');
return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()});
return execa('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() }).then(({ stdout }) => stdout);
};
Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/);
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}).catch(function (output) {
return Q.reject(new CordovaError('Failed to execute shell command "' +
shellCommand + '"" on device: ' + output));
});
return execa('adb', args.concat(shellCommand), { cwd: os.tmpdir() })
.then(({ stdout }) => stdout)
.catch(error => Promise.reject(new CordovaError(`Failed to execute shell command "${shellCommand}" on device: ${error}`)));
};
Adb.start = function (target, activityName) {
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch(function (output) {
return Q.reject(new CordovaError('Failed to start application "' +
activityName + '"" on device: ' + output));
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch((error) => {
return Promise.reject(new CordovaError('Failed to start application "' +
activityName + '"" on device: ' + error));
});
};

View File

@@ -18,143 +18,111 @@
*/
var fs = require('fs');
var et = require('elementtree');
var xml = require('cordova-common').xmlHelpers;
var DEFAULT_ORIENTATION = 'default';
/** Wraps an AndroidManifest file */
function AndroidManifest (path) {
this.path = path;
this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') {
throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")');
class AndroidManifest {
constructor (path) {
this.path = path;
this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') {
throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")');
}
}
getVersionName () {
return this.doc.getroot().attrib['android:versionName'];
}
setVersionName (versionName) {
this.doc.getroot().attrib['android:versionName'] = versionName;
return this;
}
getVersionCode () {
return this.doc.getroot().attrib['android:versionCode'];
}
setVersionCode (versionCode) {
this.doc.getroot().attrib['android:versionCode'] = versionCode;
return this;
}
getPackageId () {
return this.doc.getroot().attrib.package;
}
setPackageId (pkgId) {
this.doc.getroot().attrib.package = pkgId;
return this;
}
getActivity () {
var activity = this.doc.getroot().find('./application/activity');
return {
getName: function () {
return activity.attrib['android:name'];
},
setName: function (name) {
if (!name) {
delete activity.attrib['android:name'];
} else {
activity.attrib['android:name'] = name;
}
return this;
},
getOrientation: function () {
return activity.attrib['android:screenOrientation'];
},
setOrientation: function (orientation) {
if (!orientation || orientation.toLowerCase() === DEFAULT_ORIENTATION) {
delete activity.attrib['android:screenOrientation'];
} else {
activity.attrib['android:screenOrientation'] = orientation;
}
return this;
},
getLaunchMode: function () {
return activity.attrib['android:launchMode'];
},
setLaunchMode: function (launchMode) {
if (!launchMode) {
delete activity.attrib['android:launchMode'];
} else {
activity.attrib['android:launchMode'] = launchMode;
}
return this;
}
};
}
getDebuggable () {
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
}
setDebuggable (value) {
var application = this.doc.getroot().find('./application');
if (value) {
application.attrib['android:debuggable'] = 'true';
} else {
// The default value is "false", so we can remove attribute at all.
delete application.attrib['android:debuggable'];
}
return this;
}
/**
* Writes manifest to disk syncronously. If filename is specified, then manifest
* will be written to that file
*
* @param {String} [destPath] File to write manifest to. If omitted,
* manifest will be written to file it has been read from.
*/
write (destPath) {
fs.writeFileSync(destPath || this.path, this.doc.write({ indent: 4 }), 'utf-8');
}
}
AndroidManifest.prototype.getVersionName = function () {
return this.doc.getroot().attrib['android:versionName'];
};
AndroidManifest.prototype.setVersionName = function (versionName) {
this.doc.getroot().attrib['android:versionName'] = versionName;
return this;
};
AndroidManifest.prototype.getVersionCode = function () {
return this.doc.getroot().attrib['android:versionCode'];
};
AndroidManifest.prototype.setVersionCode = function (versionCode) {
this.doc.getroot().attrib['android:versionCode'] = versionCode;
return this;
};
AndroidManifest.prototype.getPackageId = function () {
/* jshint -W069 */
return this.doc.getroot().attrib['package'];
/* jshint +W069 */
};
AndroidManifest.prototype.setPackageId = function (pkgId) {
/* jshint -W069 */
this.doc.getroot().attrib['package'] = pkgId;
/* jshint +W069 */
return this;
};
AndroidManifest.prototype.getActivity = function () {
var activity = this.doc.getroot().find('./application/activity');
return {
getName: function () {
return activity.attrib['android:name'];
},
setName: function (name) {
if (!name) {
delete activity.attrib['android:name'];
} else {
activity.attrib['android:name'] = name;
}
return this;
},
getOrientation: function () {
return activity.attrib['android:screenOrientation'];
},
setOrientation: function (orientation) {
if (!orientation || orientation.toLowerCase() === DEFAULT_ORIENTATION) {
delete activity.attrib['android:screenOrientation'];
} else {
activity.attrib['android:screenOrientation'] = orientation;
}
return this;
},
getLaunchMode: function () {
return activity.attrib['android:launchMode'];
},
setLaunchMode: function (launchMode) {
if (!launchMode) {
delete activity.attrib['android:launchMode'];
} else {
activity.attrib['android:launchMode'] = launchMode;
}
return this;
}
};
};
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function (sdkPrefName) {
// Copy variable reference to avoid closure issues
var prefName = sdkPrefName;
AndroidManifest.prototype['get' + capitalize(prefName)] = function () {
var usesSdk = this.doc.getroot().find('./uses-sdk');
return usesSdk && usesSdk.attrib['android:' + prefName];
};
AndroidManifest.prototype['set' + capitalize(prefName)] = function (prefValue) {
var usesSdk = this.doc.getroot().find('./uses-sdk');
if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first
usesSdk = new et.Element('uses-sdk');
this.doc.getroot().append(usesSdk);
}
if (prefValue) {
usesSdk.attrib['android:' + prefName] = prefValue;
}
return this;
};
});
AndroidManifest.prototype.getDebuggable = function () {
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
};
AndroidManifest.prototype.setDebuggable = function (value) {
var application = this.doc.getroot().find('./application');
if (value) {
application.attrib['android:debuggable'] = 'true';
} else {
// The default value is "false", so we can remove attribute at all.
delete application.attrib['android:debuggable'];
}
return this;
};
/**
* Writes manifest to disk syncronously. If filename is specified, then manifest
* will be written to that file
*
* @param {String} [destPath] File to write manifest to. If omitted,
* manifest will be written to file it has been read from.
*/
AndroidManifest.prototype.write = function (destPath) {
fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8');
};
module.exports = AndroidManifest;
function capitalize (str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}

View File

@@ -21,7 +21,6 @@ var fs = require('fs');
var path = require('path');
var properties_parser = require('properties-parser');
var AndroidManifest = require('./AndroidManifest');
var AndroidStudio = require('./AndroidStudio');
var pluginHandlers = require('./pluginHandlers');
var projectFileCache = {};
@@ -56,154 +55,148 @@ function getRelativeLibraryPath (parentDir, subDir) {
return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
}
function AndroidProject (projectDir) {
this._propertiesEditors = {};
this._subProjectDirs = {};
this._dirty = false;
this.projectDir = projectDir;
this.platformWww = path.join(this.projectDir, 'platform_www');
this.www = path.join(this.projectDir, 'assets/www');
if (AndroidStudio.isAndroidStudioProject(projectDir) === true) {
class AndroidProject {
constructor (projectDir) {
this._propertiesEditors = {};
this._subProjectDirs = {};
this._dirty = false;
this.projectDir = projectDir;
this.platformWww = path.join(this.projectDir, 'platform_www');
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
}
/**
* Reads the package name out of the Android Manifest file
*
* @param {String} projectDir The absolute path to the directory containing the project
* @return {String} The name of the package
*/
getPackageName () {
var manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
return new AndroidManifest(manifestPath).getPackageId();
}
getCustomSubprojectRelativeDir (plugin_id, src) {
// All custom subprojects are prefixed with the last portion of the package id.
// This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
var packageName = this.getPackageName();
var lastDotIndex = packageName.lastIndexOf('.');
var prefix = packageName.substring(lastDotIndex + 1);
var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
return subRelativeDir;
}
addSubProject (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var subProjectFile = path.resolve(subDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
// TODO: Setting the target needs to happen only for pre-3.7.0 projects
if (fs.existsSync(subProjectFile)) {
var subProperties = this._getPropertiesFile(subProjectFile);
subProperties.set('target', parentProperties.get('target'));
subProperties.dirty = true;
this._subProjectDirs[subDir] = true;
}
addToPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
removeSubProject (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
delete this._subProjectDirs[subDir];
this._dirty = true;
}
addGradleReference (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
removeGradleReference (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
}
addSystemLibrary (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
}
removeSystemLibrary (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
}
write () {
if (!this._dirty) {
return;
}
this._dirty = false;
for (var filename in this._propertiesEditors) {
var editor = this._propertiesEditors[filename];
if (editor.dirty) {
fs.writeFileSync(filename, editor.toString());
editor.dirty = false;
}
}
}
getInstaller (type) {
return pluginHandlers.getInstaller(type);
}
getUninstaller (type) {
return pluginHandlers.getUninstaller(type);
}
/*
* This checks if an Android project is clean or has old build artifacts
*/
isClean () {
var build_path = path.join(this.projectDir, 'build');
// If the build directory doesn't exist, it's clean
return !(fs.existsSync(build_path));
}
_getPropertiesFile (filename) {
if (!this._propertiesEditors[filename]) {
if (fs.existsSync(filename)) {
this._propertiesEditors[filename] = properties_parser.createEditor(filename);
} else {
this._propertiesEditors[filename] = properties_parser.createEditor();
}
}
return this._propertiesEditors[filename];
}
static getProjectFile (projectDir) {
if (!projectFileCache[projectDir]) {
projectFileCache[projectDir] = new AndroidProject(projectDir);
}
return projectFileCache[projectDir];
}
static purgeCache (projectDir) {
if (projectDir) {
delete projectFileCache[projectDir];
} else {
projectFileCache = {};
}
}
}
AndroidProject.getProjectFile = function (projectDir) {
if (!projectFileCache[projectDir]) {
projectFileCache[projectDir] = new AndroidProject(projectDir);
}
return projectFileCache[projectDir];
};
AndroidProject.purgeCache = function (projectDir) {
if (projectDir) {
delete projectFileCache[projectDir];
} else {
projectFileCache = {};
}
};
/**
* Reads the package name out of the Android Manifest file
*
* @param {String} projectDir The absolute path to the directory containing the project
*
* @return {String} The name of the package
*/
AndroidProject.prototype.getPackageName = function () {
var manifestPath = path.join(this.projectDir, 'AndroidManifest.xml');
if (AndroidStudio.isAndroidStudioProject(this.projectDir) === true) {
manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
}
return new AndroidManifest(manifestPath).getPackageId();
};
AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, src) {
// All custom subprojects are prefixed with the last portion of the package id.
// This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
var packageName = this.getPackageName();
var lastDotIndex = packageName.lastIndexOf('.');
var prefix = packageName.substring(lastDotIndex + 1);
var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
return subRelativeDir;
};
AndroidProject.prototype.addSubProject = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var subProjectFile = path.resolve(subDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
// TODO: Setting the target needs to happen only for pre-3.7.0 projects
if (fs.existsSync(subProjectFile)) {
var subProperties = this._getPropertiesFile(subProjectFile);
subProperties.set('target', parentProperties.get('target'));
subProperties.dirty = true;
this._subProjectDirs[subDir] = true;
}
addToPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
AndroidProject.prototype.removeSubProject = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
delete this._subProjectDirs[subDir];
this._dirty = true;
};
AndroidProject.prototype.addGradleReference = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
AndroidProject.prototype.removeGradleReference = function (parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
AndroidProject.prototype.addSystemLibrary = function (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
};
AndroidProject.prototype.removeSystemLibrary = function (parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true;
};
AndroidProject.prototype.write = function () {
if (!this._dirty) {
return;
}
this._dirty = false;
for (var filename in this._propertiesEditors) {
var editor = this._propertiesEditors[filename];
if (editor.dirty) {
fs.writeFileSync(filename, editor.toString());
editor.dirty = false;
}
}
};
AndroidProject.prototype._getPropertiesFile = function (filename) {
if (!this._propertiesEditors[filename]) {
if (fs.existsSync(filename)) {
this._propertiesEditors[filename] = properties_parser.createEditor(filename);
} else {
this._propertiesEditors[filename] = properties_parser.createEditor();
}
}
return this._propertiesEditors[filename];
};
AndroidProject.prototype.getInstaller = function (type) {
return pluginHandlers.getInstaller(type);
};
AndroidProject.prototype.getUninstaller = function (type) {
return pluginHandlers.getUninstaller(type);
};
/*
* This checks if an Android project is clean or has old build artifacts
*/
AndroidProject.prototype.isClean = function () {
var build_path = path.join(this.projectDir, 'build');
// If the build directory doesn't exist, it's clean
return !(fs.existsSync(build_path));
};
module.exports = AndroidProject;

View File

@@ -1,42 +0,0 @@
/*
* This is a simple routine that checks if project is an Android Studio Project
*
* @param {String} root Root folder of the project
*/
/* jshint esnext: false */
var path = require('path');
var fs = require('fs');
var CordovaError = require('cordova-common').CordovaError;
module.exports.isAndroidStudioProject = function isAndroidStudioProject (root) {
var eclipseFiles = ['AndroidManifest.xml', 'libs', 'res'];
var androidStudioFiles = ['app', 'app/src/main'];
// assume it is an AS project and not an Eclipse project
var isEclipse = false;
var isAS = true;
if (!fs.existsSync(root)) {
throw new CordovaError('AndroidStudio.js:inAndroidStudioProject root does not exist: ' + root);
}
// if any of the following exists, then we are not an ASProj
eclipseFiles.forEach(function (file) {
if (fs.existsSync(path.join(root, file))) {
isEclipse = true;
}
});
// if it is NOT an eclipse project, check that all required files exist
if (!isEclipse) {
androidStudioFiles.forEach(function (file) {
if (!fs.existsSync(path.join(root, file))) {
console.log('missing file :: ' + file);
isAS = false;
}
});
}
return (!isEclipse && isAS);
};

View File

@@ -0,0 +1,25 @@
/**
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const PackageType = {
APK: 'apk',
BUNDLE: 'bundle'
};
module.exports = PackageType;

View File

@@ -17,8 +17,7 @@
under the License.
*/
var Q = require('q');
var superspawn = require('cordova-common').superspawn;
const execa = require('execa');
var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort:
@@ -30,17 +29,14 @@ var suffix_number_regex = /(\d+)$/;
// the number at the end, the more recent the target, the closer to the
// start of the array.
function sort_by_largest_numerical_suffix (a, b) {
var suffix_a = a.match(suffix_number_regex);
var suffix_b = b.match(suffix_number_regex);
if (suffix_a && suffix_b) {
// If the two targets being compared have suffixes, return less than
// zero, or greater than zero, based on which suffix is larger.
return (parseInt(suffix_a[1]) > parseInt(suffix_b[1]) ? -1 : 1);
} else {
// If no suffix numbers were detected, leave the order as-is between
// elements a and b.
return 0;
}
let suffix_a = a.match(suffix_number_regex);
let suffix_b = b.match(suffix_number_regex);
// If no number is detected (eg: preview version like android-R),
// designate a suffix of 0 so it gets moved to the end
suffix_a = suffix_a || ['0', '0'];
suffix_b = suffix_b || ['0', '0'];
// Return < zero, or > zero, based on which suffix is larger.
return (parseInt(suffix_a[1]) > parseInt(suffix_b[1]) ? -1 : 1);
}
module.exports.print_newest_available_sdk_target = function () {
@@ -50,6 +46,8 @@ module.exports.print_newest_available_sdk_target = function () {
});
};
// Versions should not be represented as float, so we disable quote-props here
/* eslint-disable quote-props */
module.exports.version_string_to_api_level = {
'4.0': 14,
'4.0.3': 15,
@@ -65,6 +63,7 @@ module.exports.version_string_to_api_level = {
'7.1.1': 25,
'8.0': 26
};
/* eslint-enable quote-props */
function parse_targets (output) {
var target_out = output.split('\n');
@@ -78,11 +77,11 @@ function parse_targets (output) {
}
module.exports.list_targets_with_android = function () {
return superspawn.spawn('android', ['list', 'target']).then(parse_targets);
return execa('android', ['list', 'target']).then(result => parse_targets(result.stdout));
};
module.exports.list_targets_with_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets);
return execa('avdmanager', ['list', 'target']).then(result => parse_targets(result.stdout));
};
module.exports.list_targets = function () {
@@ -95,7 +94,7 @@ module.exports.list_targets = function () {
} else throw err;
}).then(function (targets) {
if (targets.length === 0) {
return Q.reject(new Error('No android targets (SDKs) installed!'));
return Promise.reject(new Error('No android targets (SDKs) installed!'));
}
return targets;
});

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,7 +17,6 @@
under the License.
*/
var Q = require('q');
var path = require('path');
var fs = require('fs');
var nopt = require('nopt');
@@ -28,48 +25,40 @@ var Adb = require('./Adb');
var builders = require('./builders/builders');
var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn;
const execa = require('execa');
var CordovaError = require('cordova-common').CordovaError;
var PackageType = require('./PackageType');
module.exports.parseBuildOptions = parseOpts;
function parseOpts (options, resolvedTarget, projectRoot) {
options = options || {};
options.argv = nopt({
gradle: Boolean,
studio: Boolean,
prepenv: Boolean,
versionCode: String,
minSdkVersion: String,
maxSdkVersion: String,
targetSdkVersion: String,
gradleArg: [String, Array],
keystore: path,
alias: String,
storePassword: String,
password: String,
keystoreType: String
keystoreType: String,
packageType: String
}, {}, options.argv, 0);
// Android Studio Build method is the default
var ret = {
buildType: options.release ? 'release' : 'debug',
buildMethod: process.env.ANDROID_BUILD || 'studio',
prepEnv: options.argv.prepenv,
arch: resolvedTarget && resolvedTarget.arch,
extraArgs: []
};
if (options.argv.gradle || options.argv.studio) {
ret.buildMethod = options.argv.studio ? 'studio' : 'gradle';
}
// This comes from cordova/run
if (options.studio) ret.buildMethod = 'studio';
if (options.gradle) ret.buildMethod = 'gradle';
if (options.nobuild) ret.buildMethod = 'none';
if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); }
if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); }
if (options.argv.maxSdkVersion) { ret.extraArgs.push('-PcdvMaxSdkVersion=' + options.argv.maxSdkVersion); }
if (options.argv.targetSdkVersion) { ret.extraArgs.push('-PcdvTargetSdkVersion=' + options.argv.targetSdkVersion); }
if (options.argv.gradleArg) {
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
}
@@ -78,14 +67,14 @@ function parseOpts (options, resolvedTarget, projectRoot) {
if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); }
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (flagName) {
['alias', 'storePassword', 'password', 'keystoreType', 'packageType'].forEach(function (flagName) {
if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; }
});
var buildConfig = options.buildConfig;
// If some values are not specified as command line arguments - use build config to supplement them.
// Command line arguemnts have precedence over build config.
// Command line arguments have precedence over build config.
if (buildConfig) {
if (!fs.existsSync(buildConfig)) {
throw new Error('Specified build config file does not exist: ' + buildConfig);
@@ -103,7 +92,7 @@ function parseOpts (options, resolvedTarget, projectRoot) {
events.emit('log', 'Reading the keystore from: ' + packageArgs.keystore);
}
['alias', 'storePassword', 'password', 'keystoreType'].forEach(function (key) {
['alias', 'storePassword', 'password', 'keystoreType', 'packageType'].forEach(function (key) {
packageArgs[key] = packageArgs[key] || androidInfo[key];
});
}
@@ -115,11 +104,38 @@ function parseOpts (options, resolvedTarget, projectRoot) {
}
if (!ret.packageInfo) {
if (Object.keys(packageArgs).length > 0) {
// The following loop is to decide whether to print a warning about generating a signed archive
// We only want to produce a warning if they are using a config property that is related to signing, but
// missing the required properties for signing. We don't want to produce a warning if they are simply
// using a build property that isn't related to signing, such as --packageType
let shouldWarn = false;
const signingKeys = ['keystore', 'alias', 'storePassword', 'password', 'keystoreType'];
for (const key in packageArgs) {
if (!shouldWarn && signingKeys.indexOf(key) > -1) {
// If we enter this condition, we have a key used for signing a build,
// but we are missing some required signing properties
shouldWarn = true;
}
}
if (shouldWarn) {
events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.');
}
}
if (packageArgs.packageType) {
const VALID_PACKAGE_TYPES = [PackageType.APK, PackageType.BUNDLE];
if (VALID_PACKAGE_TYPES.indexOf(packageArgs.packageType) === -1) {
events.emit('warn', '"' + packageArgs.packageType + '" is an invalid packageType. Valid values are: ' + VALID_PACKAGE_TYPES.join(', ') + '\nDefaulting packageType to ' + PackageType.APK);
ret.packageType = PackageType.APK;
} else {
ret.packageType = packageArgs.packageType;
}
} else {
ret.packageType = PackageType.APK;
}
return ret;
}
@@ -129,7 +145,8 @@ function parseOpts (options, resolvedTarget, projectRoot) {
*/
module.exports.runClean = function (options) {
var opts = parseOpts(options, null, this.root);
var builder = builders.getBuilder(opts.buildMethod);
var builder = builders.getBuilder();
return builder.prepEnv(opts).then(function () {
return builder.clean(opts);
});
@@ -149,20 +166,26 @@ module.exports.runClean = function (options) {
*/
module.exports.run = function (options, optResolvedTarget) {
var opts = parseOpts(options, optResolvedTarget, this.root);
console.log(opts.buildMethod);
var builder = builders.getBuilder(opts.buildMethod);
var builder = builders.getBuilder();
return builder.prepEnv(opts).then(function () {
if (opts.prepEnv) {
events.emit('verbose', 'Build file successfully prepared.');
return;
}
return builder.build(opts).then(function () {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
var paths;
if (opts.packageType === PackageType.BUNDLE) {
paths = builder.findOutputBundles(opts.buildType);
events.emit('log', 'Built the following bundle(s): \n\t' + paths.join('\n\t'));
} else {
paths = builder.findOutputApks(opts.buildType, opts.arch);
events.emit('log', 'Built the following apk(s): \n\t' + paths.join('\n\t'));
}
return {
apkPaths: apkPaths,
buildType: opts.buildType,
buildMethod: opts.buildMethod
paths: paths,
buildType: opts.buildType
};
});
});
@@ -178,27 +201,37 @@ module.exports.detectArchitecture = function (target) {
return /intel/i.exec(output) ? 'x86' : 'arm';
});
}
function timeout (ms, err) {
return new Promise((resolve, reject) => {
setTimeout(() => reject(err), ms);
});
}
// It sometimes happens (at least on OS X), that this command will hang forever.
// To fix it, either unplug & replug device, or restart adb server.
return helper().timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.')).then(null, function (err) {
return Promise.race([
helper(),
timeout(5000, new CordovaError(
'Device communication timed out. Try unplugging & replugging the device.'
))
]).catch(err => {
if (/timed out/.exec('' + err)) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']).then(function () {
return execa('killall', ['adb']).then(function () {
return helper().then(null, function () {
// The double kill is sadly often necessary, at least on mac.
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']).then(function () {
return execa('killall', ['adb']).then(function () {
return helper().then(null, function () {
return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
return Promise.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
});
});
});
}, function () {
// For non-killall OS's.
return Q.reject(err);
return Promise.reject(err);
});
}
throw err;
@@ -229,45 +262,23 @@ module.exports.findBestApkForArchitecture = function (buildResults, arch) {
};
function PackageInfo (keystore, alias, storePassword, password, keystoreType) {
this.keystore = {
'name': 'key.store',
'value': keystore
};
this.alias = {
'name': 'key.alias',
'value': alias
};
if (storePassword) {
this.storePassword = {
'name': 'key.store.password',
'value': storePassword
};
}
if (password) {
this.password = {
'name': 'key.alias.password',
'value': password
};
}
if (keystoreType) {
this.keystoreType = {
'name': 'key.store.type',
'value': keystoreType
};
}
const createNameKeyObject = (name, value) => ({ name, value: value.replace(/\\/g, '\\\\') });
this.data = [
createNameKeyObject('key.store', keystore),
createNameKeyObject('key.alias', alias)
];
if (storePassword) this.data.push(createNameKeyObject('key.store.password', storePassword));
if (password) this.data.push(createNameKeyObject('key.alias.password', password));
if (keystoreType) this.data.push(createNameKeyObject('key.store.type', keystoreType));
}
PackageInfo.prototype = {
toProperties: function () {
var self = this;
var result = '';
Object.keys(self).forEach(function (key) {
result += self[key].name;
result += '=';
result += self[key].value.replace(/\\/g, '\\\\');
result += '\n';
});
return result;
appendToProperties: function (propertiesParser) {
for (const { name, value } of this.data) propertiesParser.set(name, value);
propertiesParser.save();
}
};
@@ -276,13 +287,14 @@ module.exports.help = function () {
console.log('Flags:');
console.log(' \'--debug\': will build project in debug mode (default)');
console.log(' \'--release\': will build project for release');
console.log(' \'--ant\': will build project with ant');
console.log(' \'--gradle\': will build project with gradle (default)');
console.log(' \'--nobuild\': will skip build process (useful when using run command)');
console.log(' \'--prepenv\': don\'t build, but copy in build scripts where necessary');
console.log(' \'--versionCode=#\': Override versionCode for this build. Useful for uploading multiple APKs. Requires --gradle.');
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build. Useful for uploading multiple APKs. Requires --gradle.');
console.log(' \'--versionCode=#\': Override versionCode for this build. Useful for uploading multiple APKs.');
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build.');
console.log(' \'--maxSdkVersion=#\': Override maxSdkVersion for this build. (Not Recommended)');
console.log(' \'--targetSdkVersion=#\': Override targetSdkVersion for this build.');
console.log(' \'--gradleArg=<gradle command line arg>\': Extra args to pass to the gradle command. Use one flag per arg. Ex. --gradleArg=-PcdvBuildMultipleApks=true');
console.log(' \'--packageType=<apk|bundle>\': Builds an APK or a bundle');
console.log('');
console.log('Signed APK flags (overwrites debug/release-signing.proprties) :');
console.log(' \'--keystore=<path to keystore>\': Key store used to build a signed archive. (Required)');

View File

@@ -1,124 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
/* eslint no-self-assign: 0 */
/* eslint no-unused-vars: 0 */
var Q = require('q');
var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var events = require('cordova-common').events;
function GenericBuilder (projectDir) {
this.root = projectDir || path.resolve(__dirname, '../../..');
this.binDirs = {
studio: path.join(this.root, 'app', 'build', 'outputs', 'apk'),
gradle: path.join(this.root, 'build', 'outputs', 'apk')
};
}
GenericBuilder.prototype.prepEnv = function () {
return Q();
};
GenericBuilder.prototype.build = function () {
events.emit('log', 'Skipping build...');
return Q(null);
};
GenericBuilder.prototype.clean = function () {
return Q();
};
GenericBuilder.prototype.findOutputApks = function (build_type, arch) {
var self = this;
return Object.keys(this.binDirs).reduce(function (result, builderName) {
var binDir = self.binDirs[builderName];
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
}, []).sort(apkSorter);
};
module.exports = GenericBuilder;
function apkSorter (fileA, fileB) {
// De-prioritize arch specific builds
var archSpecificRE = /-x86|-arm/;
if (archSpecificRE.exec(fileA)) {
return 1;
} else if (archSpecificRE.exec(fileB)) {
return -1;
}
// De-prioritize unsigned builds
var unsignedRE = /-unsigned/;
if (unsignedRE.exec(fileA)) {
return 1;
} else if (unsignedRE.exec(fileB)) {
return -1;
}
var timeDiff = fs.statSync(fileB).mtime - fs.statSync(fileA).mtime;
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
}
function findOutputApksHelper (dir, build_type, arch) {
var shellSilent = shell.config.silent;
shell.config.silent = true;
// list directory recursively
var ret = shell.ls('-R', dir).map(function (file) {
// ls does not include base directory
return path.join(dir, file);
}).filter(function (file) {
// find all APKs
return file.match(/\.apk?$/i);
}).filter(function (candidate) {
var apkName = path.basename(candidate);
// Need to choose between release and debug .apk.
if (build_type === 'debug') {
return /-debug/.exec(apkName) && !/-unaligned|-unsigned/.exec(apkName);
}
if (build_type === 'release') {
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
}
return true;
}).sort(apkSorter);
shellSilent = shellSilent;
if (ret.length === 0) {
return ret;
}
// Assume arch-specific build if newest apk has -x86 or -arm.
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
// And show only arch-specific ones (or non-arch-specific)
ret = ret.filter(function (p) {
/* jshint -W018 */
return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific;
/* jshint +W018 */
});
if (archSpecific && ret.length > 1 && arch) {
ret = ret.filter(function (p) {
return path.basename(p).indexOf('-' + arch) !== -1;
});
}
return ret;
}

View File

@@ -1,330 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var Q = require('q');
var fs = require('fs');
var util = require('util');
var path = require('path');
var shell = require('shelljs');
var superspawn = require('cordova-common').superspawn;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
var GenericBuilder = require('./GenericBuilder');
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
var SIGNING_PROPERTIES = '-signing.properties';
var TEMPLATE =
'# This file is automatically generated.\n' +
'# Do not modify this file -- ' + MARKER + '\n';
function GradleBuilder (projectRoot) {
GenericBuilder.call(this, projectRoot);
this.binDirs = { gradle: this.binDirs.gradle };
}
util.inherits(GradleBuilder, GenericBuilder);
GradleBuilder.prototype.getArgs = function (cmd, opts) {
if (cmd === 'release') {
cmd = 'cdvBuildRelease';
} else if (cmd === 'debug') {
cmd = 'cdvBuildDebug';
}
var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
if (opts.arch) {
args.push('-PcdvBuildArch=' + opts.arch);
}
// 10 seconds -> 6 seconds
args.push('-Dorg.gradle.daemon=true');
// to allow dex in process
args.push('-Dorg.gradle.jvmargs=-Xmx2048m');
// allow NDK to be used - required by Gradle 1.5 plugin
args.push('-Pandroid.useDeprecatedNdk=true');
args.push.apply(args, opts.extraArgs);
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// args.push('-Dorg.gradle.parallel=true');
return args;
};
/*
* This returns a promise
*/
GradleBuilder.prototype.runGradleWrapper = function (gradle_cmd, gradle_file) {
var gradlePath = path.join(this.root, 'gradlew');
gradle_file = path.join(this.root, (gradle_file || 'wrapper.gradle'));
if (fs.existsSync(gradlePath)) {
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
} else {
return superspawn.spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', gradle_file], { stdio: 'pipe' })
.progress(function (stdio) {
suppressJavaOptionsInfo(stdio);
});
}
};
/*
* We need to kill this in a fire.
*/
GradleBuilder.prototype.readProjectProperties = function () {
function findAllUniq (data, r) {
var s = {};
var m;
while ((m = r.exec(data))) {
s[m[1]] = 1;
}
return Object.keys(s);
}
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
return {
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
};
};
GradleBuilder.prototype.extractRealProjectNameFromManifest = function () {
var manifestPath = path.join(this.root, 'AndroidManifest.xml');
var manifestData = fs.readFileSync(manifestPath, 'utf8');
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
if (!m) {
throw new CordovaError('Could not find package name in ' + manifestPath);
}
var packageName = m[1];
var lastDotIndex = packageName.lastIndexOf('.');
return packageName.substring(lastDotIndex + 1);
};
// Makes the project buildable, minus the gradle wrapper.
GradleBuilder.prototype.prepBuildFiles = function () {
// Update the version of build.gradle in each dependent library.
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
var propertiesObj = this.readProjectProperties();
var subProjects = propertiesObj.libs;
// Check and copy the gradle file into the subproject.
// Called by the loop below this function def.
var checkAndCopy = function (subProject, root) {
var subProjectGradle = path.join(root, subProject, 'build.gradle');
// This is the future-proof way of checking if a file exists
// This must be synchronous to satisfy a Travis test
try {
fs.accessSync(subProjectGradle, fs.F_OK);
} catch (e) {
shell.cp('-f', pluginBuildGradle, subProjectGradle);
}
};
// Some dependencies on Android don't use gradle, or don't have default
// gradle files. This copies a dummy gradle file into them
for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib' && subProjects[i] !== 'app') {
checkAndCopy(subProjects[i], this.root);
}
}
var name = this.extractRealProjectNameFromManifest();
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function (p) {
var realDir = p.replace(/[/\\]/g, ':');
var libName = realDir.replace(name + '-', '');
var str = 'include ":' + libName + '"\n';
if (realDir.indexOf(name + '-') !== -1) { str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n'; }
return str;
});
// Write the settings.gradle file.
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
'// GENERATED FILE - DO NOT EDIT\n' +
'include ":"\n' + settingsGradlePaths.join(''));
// Update dependencies within build.gradle.
var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
var depsList = '';
var root = this.root;
// Cordova Plugins can be written as library modules that would use Cordova as a
// dependency. Because we need to make sure that Cordova is compiled only once for
// dexing, we make sure to exclude CordovaLib from these modules
var insertExclude = function (p) {
var gradlePath = path.join(root, p, 'build.gradle');
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
depsList += '{\n exclude module:("CordovaLib")\n }\n';
} else {
depsList += '\n';
}
};
subProjects.forEach(function (p) {
console.log('Subproject Path: ' + p);
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
depsList += ' implementation(project(path: "' + libName + '"))';
insertExclude(p);
});
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
var SYSTEM_LIBRARY_MAPPINGS = [
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
];
propertiesObj.systemLibs.forEach(function (p) {
var mavenRef;
// It's already in gradle form if it has two ':'s
if (/:.*:/.exec(p)) {
mavenRef = p;
} else {
for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
var pair = SYSTEM_LIBRARY_MAPPINGS[i];
if (pair[0].exec(p)) {
mavenRef = p.replace(pair[0], pair[1]);
break;
}
}
if (!mavenRef) {
throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
}
}
depsList += ' compile "' + mavenRef + '"\n';
});
// This code is dangerous and actually writes gradle declarations directly into the build.gradle
// Try not to mess with this if possible
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
var includeList = '';
propertiesObj.gradleIncludes.forEach(function (includePath) {
includeList += 'apply from: "' + includePath + '"\n';
});
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
};
GradleBuilder.prototype.prepEnv = function (opts) {
var self = this;
return check_reqs.check_gradle().then(function (gradlePath) {
return self.runGradleWrapper(gradlePath);
}).then(function () {
return self.prepBuildFiles();
}).then(function () {
// We now copy the gradle out of the framework
// This is a dirty patch to get the build working
/*
var wrapperDir = path.join(self.root, 'CordovaLib');
if (process.platform == 'win32') {
shell.rm('-f', path.join(self.root, 'gradlew.bat'));
shell.cp(path.join(wrapperDir, 'gradlew.bat'), self.root);
} else {
shell.rm('-f', path.join(self.root, 'gradlew'));
shell.cp(path.join(wrapperDir, 'gradlew'), self.root);
}
shell.rm('-rf', path.join(self.root, 'gradle', 'wrapper'));
shell.mkdir('-p', path.join(self.root, 'gradle'));
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(self.root, 'gradle'));
*/
// If the gradle distribution URL is set, make sure it points to version we want.
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
// For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/;
/* jshint -W069 */
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip';
/* jshint +W069 */
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.chmod('u+w', gradleWrapperPropertiesPath);
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
var propertiesFilePath = path.join(self.root, propertiesFile);
if (opts.packageInfo) {
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
} else if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
};
/*
* Builds the project with gradle.
* Returns a promise.
*/
GradleBuilder.prototype.build = function (opts) {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return superspawn.spawn(wrapper, args, { stdio: 'pipe' })
.progress(function (stdio) {
suppressJavaOptionsInfo(stdio);
}).catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function () {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
});
}
return Q.reject(error);
});
};
GradleBuilder.prototype.clean = function (opts) {
var builder = this;
var wrapper = path.join(this.root, 'gradlew');
var args = builder.getArgs('clean', opts);
return Q().then(function () {
return superspawn.spawn(wrapper, args, { stdio: 'inherit' });
}).then(function () {
shell.rm('-rf', path.join(builder.root, 'out'));
['debug', 'release'].forEach(function (config) {
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
});
};
module.exports = GradleBuilder;
function suppressJavaOptionsInfo (stdio) {
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}
function isAutoGenerated (file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}

View File

@@ -0,0 +1,380 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var fs = require('fs-extra');
var path = require('path');
const execa = require('execa');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
var PackageType = require('../PackageType');
const { createEditor } = require('properties-parser');
const MARKER = 'YOUR CHANGES WILL BE ERASED!';
const SIGNING_PROPERTIES = '-signing.properties';
const TEMPLATE =
'# This file is automatically generated.\n' +
'# Do not modify this file -- ' + MARKER + '\n';
const archSpecificRegex = /-x86|-arm/;
const unsignedBuildRegex = /-unsigned/;
const fileSorter = (filePathA, filePathB) => {
const archSpecificA = archSpecificRegex.test(filePathA);
const archSpecificB = archSpecificRegex.test(filePathB);
// If they are not equal, then sort by specific archs after generic ones
if (archSpecificA !== archSpecificB) {
return archSpecificA < archSpecificB ? -1 : 1;
}
// Otherwise, move onto the next sort item, which is by sorting unsigned bulds after signed ones
const unsignedA = unsignedBuildRegex.test(filePathA);
const unsignedB = unsignedBuildRegex.test(filePathB);
if (unsignedA !== unsignedB) {
return unsignedA < unsignedB ? -1 : 1;
}
// Then, sort by modification time, latest first
const modTimeA = fs.statSync(filePathA).mtime.getTime();
const modTimeB = fs.statSync(filePathB).mtime.getTime();
if (modTimeA !== modTimeB) {
return modTimeA < modTimeB ? 1 : -1;
}
// Finally, if all above is the same, sort by file name length, ascending
return filePathB.length < filePathA.length ? -1 : 1;
};
/**
* If the provided directory does not exist or extension is missing, return an empty array.
* If the director exists, loop the directories and collect list of files matching the extension.
*
* @param {String} dir Directory to scan
* @param {String} extension
*/
function recursivelyFindFiles (dir, extension) {
if (!fs.existsSync(dir) || !extension) return [];
const files = fs.readdirSync(dir, { withFileTypes: true })
.map(entry => {
const item = path.resolve(dir, entry.name);
if (entry.isDirectory()) return recursivelyFindFiles(item, extension);
if (path.extname(entry.name) === `.${extension}`) return item;
return false;
});
return Array.prototype.concat(...files)
.filter(file => file !== false);
}
/**
* @param {String} dir
* @param {String} build_type
* @param {String} arch
* @param {String} extension
*/
function findOutputFilesHelper (dir, build_type, arch, extension) {
let files = recursivelyFindFiles(path.resolve(dir, build_type), extension);
if (files.length === 0) return files;
// Assume arch-specific build if newest apk has -x86 or -arm.
const archSpecific = !!/-x86|-arm/.exec(path.basename(files[0]));
// And show only arch-specific ones (or non-arch-specific)
files = files.filter(p => !!/-x86|-arm/.exec(path.basename(p)) === archSpecific);
if (archSpecific && files.length > 1 && arch) {
files = files.filter(p => path.basename(p).indexOf('-' + arch) !== -1);
}
return files;
}
class ProjectBuilder {
constructor (rootDirectory) {
this.root = rootDirectory || path.resolve(__dirname, '../../..');
this.apkDir = path.join(this.root, 'app', 'build', 'outputs', 'apk');
this.aabDir = path.join(this.root, 'app', 'build', 'outputs', 'bundle');
}
getArgs (cmd, opts) {
let args;
let buildCmd = cmd;
if (opts.packageType === PackageType.BUNDLE) {
if (cmd === 'release') {
buildCmd = ':app:bundleRelease';
} else if (cmd === 'debug') {
buildCmd = ':app:bundleDebug';
}
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
} else {
if (cmd === 'release') {
buildCmd = 'cdvBuildRelease';
} else if (cmd === 'debug') {
buildCmd = 'cdvBuildDebug';
}
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
if (opts.arch) {
args.push('-PcdvBuildArch=' + opts.arch);
}
}
args.push.apply(args, opts.extraArgs);
return args;
}
/*
* This returns a promise
*/
runGradleWrapper (gradle_cmd) {
var gradlePath = path.join(this.root, 'gradlew');
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
if (fs.existsSync(gradlePath)) {
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
} else {
return execa(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], { stdio: 'inherit' });
}
}
readProjectProperties () {
function findAllUniq (data, r) {
var s = {};
var m;
while ((m = r.exec(data))) {
s[m[1]] = 1;
}
return Object.keys(s);
}
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
return {
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
};
}
extractRealProjectNameFromManifest () {
var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
var manifestData = fs.readFileSync(manifestPath, 'utf8');
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
if (!m) {
throw new CordovaError('Could not find package name in ' + manifestPath);
}
var packageName = m[1];
var lastDotIndex = packageName.lastIndexOf('.');
return packageName.substring(lastDotIndex + 1);
}
// Makes the project buildable, minus the gradle wrapper.
prepBuildFiles () {
// Update the version of build.gradle in each dependent library.
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
var propertiesObj = this.readProjectProperties();
var subProjects = propertiesObj.libs;
// Check and copy the gradle file into the subproject
// Called by the loop before this function def
var checkAndCopy = function (subProject, root) {
var subProjectGradle = path.join(root, subProject, 'build.gradle');
// This is the future-proof way of checking if a file exists
// This must be synchronous to satisfy a Travis test
try {
fs.accessSync(subProjectGradle, fs.F_OK);
} catch (e) {
fs.copySync(pluginBuildGradle, subProjectGradle);
}
};
for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') {
checkAndCopy(subProjects[i], this.root);
}
}
var name = this.extractRealProjectNameFromManifest();
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function (p) {
var realDir = p.replace(/[/\\]/g, ':');
var libName = realDir.replace(name + '-', '');
var str = 'include ":' + libName + '"\n';
if (realDir.indexOf(name + '-') !== -1) {
str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n';
}
return str;
});
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
'// GENERATED FILE - DO NOT EDIT\n' +
'include ":"\n' + settingsGradlePaths.join(''));
// Update dependencies within build.gradle.
var buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
var depsList = '';
var root = this.root;
var insertExclude = function (p) {
var gradlePath = path.join(root, p, 'build.gradle');
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
depsList += '{\n exclude module:("CordovaLib")\n }\n';
} else {
depsList += '\n';
}
};
subProjects.forEach(function (p) {
events.emit('log', 'Subproject Path: ' + p);
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
if (libName !== 'app') {
depsList += ' implementation(project(path: ":' + libName + '"))';
insertExclude(p);
}
});
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
var SYSTEM_LIBRARY_MAPPINGS = [
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
];
propertiesObj.systemLibs.forEach(function (p) {
var mavenRef;
// It's already in gradle form if it has two ':'s
if (/:.*:/.exec(p)) {
mavenRef = p;
} else {
for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
var pair = SYSTEM_LIBRARY_MAPPINGS[i];
if (pair[0].exec(p)) {
mavenRef = p.replace(pair[0], pair[1]);
break;
}
}
if (!mavenRef) {
throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
}
}
depsList += ' implementation "' + mavenRef + '"\n';
});
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
var includeList = '';
propertiesObj.gradleIncludes.forEach(function (includePath) {
includeList += 'apply from: "../' + includePath + '"\n';
});
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
// This needs to be stored in the app gradle, not the root grade
fs.writeFileSync(path.join(this.root, 'app', 'build.gradle'), buildGradle);
}
prepEnv (opts) {
var self = this;
return check_reqs.check_gradle()
.then(function (gradlePath) {
return self.runGradleWrapper(gradlePath);
}).then(function () {
return self.prepBuildFiles();
}).then(() => {
// update/set the distributionUrl in the gradle-wrapper.properties
const gradleWrapperPropertiesPath = path.join(self.root, 'gradle/wrapper/gradle-wrapper.properties');
const gradleWrapperProperties = createEditor(gradleWrapperPropertiesPath);
const distributionUrl = process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL || 'https://services.gradle.org/distributions/gradle-6.5-all.zip';
gradleWrapperProperties.set('distributionUrl', distributionUrl);
gradleWrapperProperties.save();
events.emit('verbose', `Gradle Distribution URL: ${distributionUrl}`);
})
.then(() => {
const signingPropertiesPath = path.join(self.root, `${opts.buildType}${SIGNING_PROPERTIES}`);
if (fs.existsSync(signingPropertiesPath)) fs.removeSync(signingPropertiesPath);
if (opts.packageInfo) {
fs.ensureFileSync(signingPropertiesPath);
const signingProperties = createEditor(signingPropertiesPath);
signingProperties.addHeadComment(TEMPLATE);
opts.packageInfo.appendToProperties(signingProperties);
}
});
}
/*
* Builds the project with gradle.
* Returns a promise.
*/
build (opts) {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return execa(wrapper, args, { stdio: 'inherit' })
.catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function () {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
throw error;
});
}
throw error;
});
}
clean (opts) {
const wrapper = path.join(this.root, 'gradlew');
const args = this.getArgs('clean', opts);
return execa(wrapper, args, { stdio: 'inherit' })
.then(() => {
fs.removeSync(path.join(this.root, 'out'));
['debug', 'release'].map(config => path.join(this.root, `${config}${SIGNING_PROPERTIES}`))
.forEach(file => {
const hasFile = fs.existsSync(file);
const hasMarker = hasFile && fs.readFileSync(file, 'utf8')
.includes(MARKER);
if (hasFile && hasMarker) fs.removeSync(file);
});
});
}
findOutputApks (build_type, arch) {
return findOutputFilesHelper(this.apkDir, build_type, arch, 'apk').sort(fileSorter);
}
findOutputBundles (build_type) {
return findOutputFilesHelper(this.aabDir, build_type, false, 'aab').sort(fileSorter);
}
fetchBuildResults (build_type, arch) {
return {
apkPaths: this.findOutputApks(build_type, arch),
buildType: build_type
};
}
}
module.exports = ProjectBuilder;

View File

@@ -1,302 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var Q = require('q');
var fs = require('fs');
var util = require('util');
var path = require('path');
var shell = require('shelljs');
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
var GenericBuilder = require('./GenericBuilder');
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
var SIGNING_PROPERTIES = '-signing.properties';
var TEMPLATE =
'# This file is automatically generated.\n' +
'# Do not modify this file -- ' + MARKER + '\n';
function StudioBuilder (projectRoot) {
GenericBuilder.call(this, projectRoot);
this.binDirs = {gradle: this.binDirs.studio};
}
util.inherits(StudioBuilder, GenericBuilder);
StudioBuilder.prototype.getArgs = function (cmd, opts) {
if (cmd === 'release') {
cmd = 'cdvBuildRelease';
} else if (cmd === 'debug') {
cmd = 'cdvBuildDebug';
}
var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
if (opts.arch) {
args.push('-PcdvBuildArch=' + opts.arch);
}
// 10 seconds -> 6 seconds
args.push('-Dorg.gradle.daemon=true');
// to allow dex in process
args.push('-Dorg.gradle.jvmargs=-Xmx2048m');
// allow NDK to be used - required by Gradle 1.5 plugin
// args.push('-Pandroid.useDeprecatedNdk=true');
args.push.apply(args, opts.extraArgs);
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// args.push('-Dorg.gradle.parallel=true');
return args;
};
/*
* This returns a promise
*/
StudioBuilder.prototype.runGradleWrapper = function (gradle_cmd) {
var gradlePath = path.join(this.root, 'gradlew');
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
if (fs.existsSync(gradlePath)) {
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
} else {
return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
}
};
StudioBuilder.prototype.readProjectProperties = function () {
function findAllUniq (data, r) {
var s = {};
var m;
while ((m = r.exec(data))) {
s[m[1]] = 1;
}
return Object.keys(s);
}
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
return {
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
};
};
StudioBuilder.prototype.extractRealProjectNameFromManifest = function () {
var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
var manifestData = fs.readFileSync(manifestPath, 'utf8');
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
if (!m) {
throw new CordovaError('Could not find package name in ' + manifestPath);
}
var packageName = m[1];
var lastDotIndex = packageName.lastIndexOf('.');
return packageName.substring(lastDotIndex + 1);
};
// Makes the project buildable, minus the gradle wrapper.
StudioBuilder.prototype.prepBuildFiles = function () {
// Update the version of build.gradle in each dependent library.
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
var propertiesObj = this.readProjectProperties();
var subProjects = propertiesObj.libs;
// Check and copy the gradle file into the subproject
// Called by the loop before this function def
var checkAndCopy = function (subProject, root) {
var subProjectGradle = path.join(root, subProject, 'build.gradle');
// This is the future-proof way of checking if a file exists
// This must be synchronous to satisfy a Travis test
try {
fs.accessSync(subProjectGradle, fs.F_OK);
} catch (e) {
shell.cp('-f', pluginBuildGradle, subProjectGradle);
}
};
for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') {
checkAndCopy(subProjects[i], this.root);
}
}
var name = this.extractRealProjectNameFromManifest();
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function (p) {
var realDir = p.replace(/[/\\]/g, ':');
var libName = realDir.replace(name + '-', '');
var str = 'include ":' + libName + '"\n';
if (realDir.indexOf(name + '-') !== -1) {
str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n';
}
return str;
});
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
'// GENERATED FILE - DO NOT EDIT\n' +
'include ":"\n' + settingsGradlePaths.join(''));
// Update dependencies within build.gradle.
var buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
var depsList = '';
var root = this.root;
var insertExclude = function (p) {
var gradlePath = path.join(root, p, 'build.gradle');
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
depsList += '{\n exclude module:("CordovaLib")\n }\n';
} else {
depsList += '\n';
}
};
subProjects.forEach(function (p) {
console.log('Subproject Path: ' + p);
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
if (libName !== 'app') {
depsList += ' implementation(project(path: ":' + libName + '"))';
insertExclude(p);
}
});
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
var SYSTEM_LIBRARY_MAPPINGS = [
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
];
propertiesObj.systemLibs.forEach(function (p) {
var mavenRef;
// It's already in gradle form if it has two ':'s
if (/:.*:/.exec(p)) {
mavenRef = p;
} else {
for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
var pair = SYSTEM_LIBRARY_MAPPINGS[i];
if (pair[0].exec(p)) {
mavenRef = p.replace(pair[0], pair[1]);
break;
}
}
if (!mavenRef) {
throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
}
}
depsList += ' compile "' + mavenRef + '"\n';
});
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
var includeList = '';
propertiesObj.gradleIncludes.forEach(function (includePath) {
includeList += 'apply from: "../' + includePath + '"\n';
});
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
// This needs to be stored in the app gradle, not the root grade
fs.writeFileSync(path.join(this.root, 'app', 'build.gradle'), buildGradle);
};
StudioBuilder.prototype.prepEnv = function (opts) {
var self = this;
return check_reqs.check_gradle()
.then(function (gradlePath) {
return self.runGradleWrapper(gradlePath);
}).then(function () {
return self.prepBuildFiles();
}).then(function () {
// If the gradle distribution URL is set, make sure it points to version we want.
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
// For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/;
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip';
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.chmod('u+w', gradleWrapperPropertiesPath);
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
var propertiesFilePath = path.join(self.root, propertiesFile);
if (opts.packageInfo) {
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
} else if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
};
/*
* Builds the project with gradle.
* Returns a promise.
*/
StudioBuilder.prototype.build = function (opts) {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return spawn(wrapper, args, {stdio: 'pipe'})
.progress(function (stdio) {
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}).catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function () {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
});
}
return Q.reject(error);
});
};
StudioBuilder.prototype.clean = function (opts) {
var builder = this;
var wrapper = path.join(this.root, 'gradlew');
var args = builder.getArgs('clean', opts);
return Q().then(function () {
return spawn(wrapper, args, {stdio: 'inherit'});
})
.then(function () {
shell.rm('-rf', path.join(builder.root, 'out'));
['debug', 'release'].forEach(function (config) {
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
});
};
module.exports = StudioBuilder;
function isAutoGenerated (file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}

View File

@@ -1,46 +1,34 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
var CordovaError = require('cordova-common').CordovaError;
var knownBuilders = {
gradle: 'GradleBuilder',
studio: 'StudioBuilder',
none: 'GenericBuilder'
};
const CordovaError = require('cordova-common').CordovaError;
/**
* Helper method that instantiates and returns a builder for specified build
* type.
* Helper method that instantiates and returns a builder for specified build type.
*
* @param {String} builderType Builder name to construct and return. Must
* be one of 'ant', 'gradle' or 'none'
*
* @return {Builder} A builder instance for specified build type.
* @return {Builder} A builder instance for specified build type.
*/
module.exports.getBuilder = function (builderType, projectRoot) {
if (!knownBuilders[builderType]) { throw new CordovaError('Builder ' + builderType + ' is not supported.'); }
module.exports.getBuilder = function (projectPath) {
try {
var Builder = require('./' + knownBuilders[builderType]);
return new Builder(projectRoot);
const Builder = require('./ProjectBuilder');
return new Builder(projectPath);
} catch (err) {
throw new CordovaError('Failed to instantiate ' + knownBuilders[builderType] + ' builder: ' + err);
throw new CordovaError('Failed to instantiate ProjectBuilder builder: ' + err);
}
};

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,37 +17,37 @@
under the License.
*/
/* jshint sub:true */
var shelljs = require('shelljs');
var child_process = require('child_process');
var Q = require('q');
const execa = require('execa');
var path = require('path');
var fs = require('fs');
var fs = require('fs-extra');
var os = require('os');
var which = require('which');
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
var PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn;
const { CordovaError, ConfigParser, events } = require('cordova-common');
var android_sdk = require('./android_sdk');
const { createEditor } = require('properties-parser');
function forgivingWhichSync (cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
return '';
}
const whichResult = which.sync(cmd, { nothrow: true });
// On null, returns empty string to maintain backwards compatibility
// realpathSync follows symlinks
return whichResult === null ? '' : fs.realpathSync(whichResult);
}
function tryCommand (cmd, errMsg, catchStderr) {
var d = Q.defer();
child_process.exec(cmd, function (err, stdout, stderr) {
if (err) d.reject(new CordovaError(errMsg));
// Sometimes it is necessary to return an stderr instead of stdout in case of success, since
// some commands prints theirs output to stderr instead of stdout. 'javac' is the example
else d.resolve((catchStderr ? stderr : stdout).trim());
});
return d.promise;
function getJDKDirectory (directory) {
const p = path.resolve(directory, 'java');
if (fs.existsSync(p)) {
const directories = fs.readdirSync(p);
for (let i = 0; i < directories.length; i++) {
const dir = directories[i];
if (/^(jdk)+./.test(dir)) {
return path.resolve(directory, 'java', dir);
}
}
}
return null;
}
module.exports.isWindows = function () {
@@ -60,31 +58,49 @@ module.exports.isDarwin = function () {
return (os.platform() === 'darwin');
};
// Get valid target from framework/project.properties if run from this repo
// Otherwise get target from project.properties file within a generated cordova-android project
/**
* @description Get valid target from framework/project.properties if run from this repo
* Otherwise get target from project.properties file within a generated cordova-android project
* @returns {string} The android target in format "android-${target}"
*/
module.exports.get_target = function () {
function extractFromFile (filePath) {
var target = shelljs.grep(/\btarget=/, filePath);
if (!target) {
throw new Error('Could not find android target within: ' + filePath);
const projectPropertiesPaths = [
path.join(REPO_ROOT, 'framework', 'project.properties'),
path.join(PROJECT_ROOT, 'project.properties')
];
// Get the minimum required target API from the framework.
let target = projectPropertiesPaths.filter(filePath => fs.existsSync(filePath))
.map(filePath => createEditor(filePath).get('target'))
.pop();
if (!target) {
throw new Error(`We could not locate the target from the "project.properties" at either "${projectPropertiesPaths.join('", "')}".`);
}
// If the repo config.xml file exists, find the desired targetSdkVersion.
const configFile = path.join(REPO_ROOT, 'config.xml');
if (!fs.existsSync(configFile)) return target;
const configParser = new ConfigParser(configFile);
const desiredAPI = parseInt(configParser.getPreference('android-targetSdkVersion', 'android'), 10);
if (!isNaN(desiredAPI)) {
const minimumAPI = parseInt(target.split('-').pop(), 10);
if (desiredAPI >= minimumAPI) {
target = `android-${desiredAPI}`;
} else {
events.emit('warn', `android-targetSdkVersion should be greater than or equal to ${minimumAPI}.`);
}
return target.split('=')[1].trim();
}
var repo_file = path.join(REPO_ROOT, 'framework', 'project.properties');
if (fs.existsSync(repo_file)) {
return extractFromFile(repo_file);
}
var project_file = path.join(PROJECT_ROOT, 'project.properties');
if (fs.existsSync(project_file)) {
// if no target found, we're probably in a project and project.properties is in PROJECT_ROOT.
return extractFromFile(project_file);
}
throw new Error('Could not find android target in either ' + repo_file + ' nor ' + project_file);
return target;
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function () {
return superspawn.spawn('ant', ['-version']).then(function (output) {
return execa('ant', ['-version']).then(({ stdout: output }) => {
// Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
}).catch(function (err) {
@@ -101,19 +117,18 @@ module.exports.get_gradle_wrapper = function () {
var program_dir;
// OK, This hack only works on Windows, not on Mac OS or Linux. We will be deleting this eventually!
if (module.exports.isWindows()) {
var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat'));
var result = execa.sync(path.join(__dirname, 'getASPath.bat'));
// console.log('result.stdout =' + result.stdout.toString());
// console.log('result.stderr =' + result.stderr.toString());
if (result.stderr.toString().length > 0) {
var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
var androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
if (fs.existsSync(androidPath)) {
program_dir = fs.readdirSync(androidPath);
while (i < program_dir.length && !foundStudio) {
if (program_dir[i].startsWith('Android Studio')) {
foundStudio = true;
androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle');
androidStudioPath = path.join(process.env.ProgramFiles, 'Android', program_dir[i], 'gradle');
} else { ++i; }
}
}
@@ -138,31 +153,30 @@ module.exports.get_gradle_wrapper = function () {
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function () {
var sdkDir = process.env['ANDROID_HOME'];
var d = Q.defer();
var sdkDir = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
if (!sdkDir) {
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
return Promise.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
'Might need to install Android SDK or set up \'ANDROID_SDK_ROOT\' env variable.'));
}
var gradlePath = module.exports.get_gradle_wrapper();
if (gradlePath.length !== 0) { d.resolve(gradlePath); } else {
d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio'));
}
return d.promise;
if (gradlePath.length !== 0) return Promise.resolve(gradlePath);
return Promise.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio'));
};
// Returns a promise.
module.exports.check_java = function () {
var javacPath = forgivingWhichSync('javac');
var hasJavaHome = !!process.env['JAVA_HOME'];
return Q().then(function () {
var hasJavaHome = !!process.env.JAVA_HOME;
return Promise.resolve().then(function () {
if (hasJavaHome) {
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
if (!javacPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
process.env.PATH += path.delimiter + path.join(process.env.JAVA_HOME, 'bin');
}
} else {
if (javacPath) {
@@ -170,8 +184,8 @@ module.exports.check_java = function () {
var find_java = '/usr/libexec/java_home';
var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting it manually.';
if (fs.existsSync(find_java)) {
return superspawn.spawn(find_java).then(function (stdout) {
process.env['JAVA_HOME'] = stdout.trim();
return execa(find_java).then(({ stdout }) => {
process.env.JAVA_HOME = stdout;
}).catch(function (err) {
if (err) {
throw new CordovaError(default_java_error_msg);
@@ -179,93 +193,122 @@ module.exports.check_java = function () {
});
} else {
// See if we can derive it from javac's location.
// fs.realpathSync is require on Ubuntu, which symplinks from /usr/bin -> JDK
var maybeJavaHome = path.dirname(path.dirname(javacPath));
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
process.env['JAVA_HOME'] = maybeJavaHome;
process.env.JAVA_HOME = maybeJavaHome;
} else {
throw new CordovaError(default_java_error_msg);
}
}
} else if (module.exports.isWindows()) {
// Try to auto-detect java in the default install paths.
var oldSilent = shelljs.config.silent;
shelljs.config.silent = true;
var firstJdkDir =
shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] ||
shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
shelljs.config.silent = oldSilent;
const programFilesEnv = path.resolve(process.env.ProgramFiles);
const programFiles = 'C:\\Program Files\\';
const programFilesx86 = 'C:\\Program Files (x86)\\';
let firstJdkDir =
getJDKDirectory(programFilesEnv) ||
getJDKDirectory(programFiles) ||
getJDKDirectory(programFilesx86);
if (firstJdkDir) {
// shelljs always uses / in paths.
firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
if (!javacPath) {
process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin');
process.env.PATH += path.delimiter + path.join(firstJdkDir, 'bin');
}
process.env['JAVA_HOME'] = firstJdkDir;
process.env.JAVA_HOME = firstJdkDir;
}
}
}
}).then(function () {
var msg =
'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
if (process.env['JAVA_HOME']) {
msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
}
// We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true).then(function (output) {
// Let's check for at least Java 8, and keep it future proof so we can support Java 10
var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
return match && match[1];
});
return execa('javac', ['-version'], { all: true })
.then(({ all: output }) => {
// Java <= 8 writes version info to stderr, Java >= 9 to stdout
const match = /javac\s+([\d.]+)/i.exec(output);
return match && match[1];
}, () => {
var msg =
'Failed to run "javac -version", make sure that you have a JDK version 8 installed.\n' +
'You can get it from the following location:\n' +
'https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html';
if (process.env.JAVA_HOME) {
msg += '\n\n';
msg += 'Your JAVA_HOME is invalid: ' + process.env.JAVA_HOME;
}
throw new CordovaError(msg);
});
});
};
// Returns a promise.
module.exports.check_android = function () {
return Q().then(function () {
var androidCmdPath = forgivingWhichSync('android');
var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
return Promise.resolve().then(function () {
function maybeSetAndroidHome (value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
process.env['ANDROID_HOME'] = value;
process.env.ANDROID_SDK_ROOT = value;
}
}
var androidCmdPath = forgivingWhichSync('android');
var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = false;
if (process.env.ANDROID_SDK_ROOT) {
maybeSetAndroidHome(path.resolve(process.env.ANDROID_SDK_ROOT));
}
// First ensure ANDROID_HOME is set
// If we have no hints (nothing in PATH), try a few default locations
if (!hasAndroidHome && !androidCmdPath && !adbInPath && !avdmanagerInPath) {
if (process.env.ANDROID_HOME) {
// Fallback to deprecated `ANDROID_HOME` variable
maybeSetAndroidHome(path.join(process.env.ANDROID_HOME));
}
if (module.exports.isWindows()) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'sdk'));
if (process.env.LOCALAPPDATA) {
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'sdk'));
}
if (process.env.ProgramFiles) {
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'sdk'));
}
// Android Studio pre-1.0 installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-studio', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-studio', 'sdk'));
if (process.env.LOCALAPPDATA) {
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'android-studio', 'sdk'));
}
if (process.env.ProgramFiles) {
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'android-studio', 'sdk'));
}
// Stand-alone installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk'));
if (process.env.LOCALAPPDATA) {
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'android-sdk'));
}
if (process.env.ProgramFiles) {
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'android-sdk'));
}
} else if (module.exports.isDarwin()) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['HOME'], 'Library', 'Android', 'sdk'));
if (process.env.HOME) {
maybeSetAndroidHome(path.join(process.env.HOME, 'Library', 'Android', 'sdk'));
}
// Android Studio pre-1.0 installer
maybeSetAndroidHome('/Applications/Android Studio.app/sdk');
// Stand-alone zip file that user might think to put under /Applications
maybeSetAndroidHome('/Applications/android-sdk-macosx');
maybeSetAndroidHome('/Applications/android-sdk');
}
if (process.env['HOME']) {
if (process.env.HOME) {
// Stand-alone zip file that user might think to put under their home directory
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk-macosx'));
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk'));
maybeSetAndroidHome(path.join(process.env.HOME, 'android-sdk-macosx'));
maybeSetAndroidHome(path.join(process.env.HOME, 'android-sdk'));
}
}
if (!hasAndroidHome) {
// If we dont have ANDROID_HOME, but we do have some tools on the PATH, try to infer from the tooling PATH.
// If we dont have ANDROID_SDK_ROOT, but we do have some tools on the PATH, try to infer from the tooling PATH.
var parentDir, grandParentDir;
if (androidCmdPath) {
parentDir = path.dirname(androidCmdPath);
@@ -273,7 +316,7 @@ module.exports.check_android = function () {
if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools directory.');
}
@@ -284,7 +327,7 @@ module.exports.check_android = function () {
if (path.basename(parentDir) === 'platform-tools') {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.');
}
@@ -295,29 +338,29 @@ module.exports.check_android = function () {
if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') {
maybeSetAndroidHome(path.dirname(grandParentDir));
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
}
}
}
if (!process.env['ANDROID_HOME']) {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
if (!process.env.ANDROID_SDK_ROOT) {
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
}
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] +
if (!fs.existsSync(process.env.ANDROID_SDK_ROOT)) {
throw new CordovaError('\'ANDROID_SDK_ROOT\' environment variable is set to non-existent path: ' + process.env.ANDROID_SDK_ROOT +
'\nTry update it manually to point to valid SDK directory.');
}
// Next let's make sure relevant parts of the SDK tooling is in our PATH
if (hasAndroidHome && !androidCmdPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools');
}
if (hasAndroidHome && !adbInPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'platform-tools');
}
if (hasAndroidHome && !avdmanagerInPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools', 'bin');
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools', 'bin');
}
return hasAndroidHome;
});
@@ -362,16 +405,22 @@ module.exports.check_android_target = function (originalError) {
// Returns a promise.
module.exports.run = function () {
return Q.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
console.log('Checking Java JDK and Android SDK versions');
console.log('ANDROID_SDK_ROOT=' + process.env.ANDROID_SDK_ROOT + ' (recommended setting)');
console.log('ANDROID_HOME=' + process.env.ANDROID_HOME + ' (DEPRECATED)');
if (!values[0]) {
throw new CordovaError('Requirements check failed for JDK 1.8 or greater');
return Promise.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('Using Android SDK: ' + process.env.ANDROID_SDK_ROOT);
if (!String(values[0]).startsWith('1.8.')) {
throw new CordovaError(
'Requirements check failed for JDK 8 (\'1.8.*\')! Detected version: ' + values[0] + '\n' +
'Check your ANDROID_SDK_ROOT / JAVA_HOME / PATH environment variables.'
);
}
if (!values[1]) {
throw new CordovaError('Requirements check failed for Android SDK');
throw new CordovaError('Requirements check failed for Android SDK! Android SDK was not detected.');
}
});
};
@@ -400,7 +449,6 @@ var Requirement = function (id, name, version, installed) {
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
*/
module.exports.check_all = function () {
var requirements = [
new Requirement('java', 'Java JDK'),
new Requirement('androidSdk', 'Android SDK'),
@@ -425,7 +473,7 @@ module.exports.check_all = function () {
}, function (err) {
requirement.metadata.reason = err instanceof Error ? err.message : err;
});
}, Q()).then(function () {
}, Promise.resolve()).then(function () {
// When chain is completed, return requirements array to upstream API
return requirements;
});

View File

@@ -0,0 +1,150 @@
/**
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
const fs = require('fs');
const path = require('path');
const propertiesParser = require('properties-parser');
const events = require('cordova-common').events;
class GradlePropertiesParser {
/**
* Loads and Edits Gradle Properties File.
*
* @param {String} platformDir is the path of the Android platform directory
*/
constructor (platformDir) {
this._defaults = {
// 10 seconds -> 6 seconds
'org.gradle.daemon': 'true',
// to allow dex in process
'org.gradle.jvmargs': '-Xmx2048m',
// Android X
'android.useAndroidX': 'false',
'android.enableJetifier': 'false'
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// 'org.gradle.parallel': 'true'
};
this.gradleFilePath = path.join(platformDir, 'gradle.properties');
}
configure (userConfigs) {
events.emit('verbose', '[Gradle Properties] Preparing Configuration');
this._initializeEditor();
events.emit('verbose', '[Gradle Properties] Appending default configuration properties');
this._configureProperties(this._defaults);
events.emit('verbose', '[Gradle Properties] Appending custom configuration properties');
this._configureProperties(userConfigs);
this._save();
}
/**
* Initialize the properties editor for parsing, setting, etc.
*/
_initializeEditor () {
// Touch empty gradle.properties file if missing.
if (!fs.existsSync(this.gradleFilePath)) {
events.emit('verbose', '[Gradle Properties] File missing, creating file with Cordova defaults.');
fs.writeFileSync(this.gradleFilePath, '', 'utf-8');
}
// Create an editor for parsing, getting, and setting configurations.
this.gradleFile = propertiesParser.createEditor(this.gradleFilePath);
}
/**
* Validate that defaults or user configuration properties are set and
* set the missing items.
*/
_configureProperties (properties) {
// Iterate though the properties and set only if missing.
Object.keys(properties).forEach(key => {
const value = this.gradleFile.get(key);
if (!value) {
// Handles the case of adding missing defaults or new properties that are missing.
events.emit('verbose', `[Gradle Properties] Appending configuration item: ${key}=${properties[key]}`);
this.gradleFile.set(key, properties[key]);
} else if (value !== properties[key]) {
if (this._defaults[key] && this._defaults[key] !== properties[key]) {
let shouldEmit = true;
if (key === 'org.gradle.jvmargs') {
shouldEmit = this._isJVMMemoryLessThanRecommended(properties[key], this._defaults[key]);
}
if (shouldEmit) {
// Since the value does not match default, we will notify the discrepancy with Cordova's recommended value.
events.emit('info', `[Gradle Properties] Detected Gradle property "${key}" with the value of "${properties[key]}", Cordova's recommended value is "${this._defaults[key]}"`);
}
} else {
// When the current value exists but does not match the new value or does matches the default key value, the new value it set.
events.emit('verbose', `[Gradle Properties] Updating Gradle property "${key}" with the value of "${properties[key]}"`);
}
// We will set the new value in either case.
this.gradleFile.set(key, properties[key]);
}
});
}
_isJVMMemoryLessThanRecommended (memoryValue, recommendedMemoryValue) {
const UNIT = 2;
const SIZE = 1;
const regex = /-Xmx+([0-9]+)+([mMgGkK])/;
const recommendedCapture = regex.exec(recommendedMemoryValue);
const recommendedBase = this._getBaseJVMSize(recommendedCapture[SIZE], recommendedCapture[UNIT]);
const memoryCapture = regex.exec(memoryValue);
const memoryBase = this._getBaseJVMSize(memoryCapture[SIZE], memoryCapture[UNIT]);
return memoryBase < recommendedBase;
}
_getBaseJVMSize (size, unit) {
const KILOBYTE = 1024;
const MEGABYTE = 1048576;
const GIGABYTE = 1073741824;
switch (unit.toLowerCase()) {
case 'k': return size * KILOBYTE;
case 'm': return size * MEGABYTE;
case 'g': return size * GIGABYTE;
}
events.emit('warn', `[Gradle Properties] Unknown memory size unit (${unit})`);
return null;
}
/**
* Saves any changes that has been made to the properties file.
*/
_save () {
events.emit('verbose', '[Gradle Properties] Updating and Saving File');
this.gradleFile.save();
}
}
module.exports = GradlePropertiesParser;

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,12 +17,11 @@
under the License.
*/
var Q = require('q');
const execa = require('execa');
var build = require('./build');
var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var events = require('cordova-common').events;
@@ -38,7 +35,7 @@ module.exports.list = function (lookHarder) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
return spawn('killall', ['adb']).then(function () {
return execa('killall', ['adb']).then(function () {
events.emit('verbose', 'Restarting adb to see if more devices are detected.');
return Adb.devices();
}, function () {
@@ -53,13 +50,13 @@ module.exports.list = function (lookHarder) {
module.exports.resolveTarget = function (target) {
return this.list(true).then(function (device_list) {
if (!device_list || !device_list.length) {
return Q.reject(new CordovaError('Failed to deploy to device, no devices found.'));
return Promise.reject(new CordovaError('Failed to deploy to device, no devices found.'));
}
// default device
target = target || device_list[0];
if (device_list.indexOf(target) < 0) {
return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
return Promise.reject(new CordovaError('ERROR: Unable to find target \'' + target + '\'.'));
}
return build.detectArchitecture(target).then(function (arch) {
@@ -74,7 +71,7 @@ module.exports.resolveTarget = function (target) {
* Returns a promise.
*/
module.exports.install = function (target, buildResults) {
return Q().then(function () {
return Promise.resolve().then(function () {
if (target && typeof target === 'object') {
return target;
}
@@ -87,7 +84,7 @@ module.exports.install = function (target, buildResults) {
events.emit('log', 'Using apk: ' + apk_path);
events.emit('log', 'Package name: ' + pkgName);
return Adb.install(resolvedTarget.target, apk_path, {replace: true}).catch(function (error) {
return Adb.install(resolvedTarget.target, apk_path, { replace: true }).catch(function (error) {
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
// is already installed on device was signed w/different certificate
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
@@ -98,7 +95,7 @@ module.exports.install = function (target, buildResults) {
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed.
return Adb.uninstall(resolvedTarget.target, pkgName).then(function () {
return Adb.install(resolvedTarget.target, apk_path, {replace: true});
return Adb.install(resolvedTarget.target, apk_path, { replace: true });
});
}).then(function () {
// unlock screen

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,8 +17,8 @@
under the License.
*/
/* jshint sub:true */
const execa = require('execa');
const fs = require('fs-extra');
var android_versions = require('android-versions');
var retry = require('./retry');
var build = require('./build');
@@ -28,54 +26,49 @@ var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
var events = require('cordova-common').events;
var superspawn = require('cordova-common').superspawn;
var CordovaError = require('cordova-common').CordovaError;
var shelljs = require('shelljs');
var android_sdk = require('./android_sdk');
var check_reqs = require('./check_reqs');
var Q = require('q');
var which = require('which');
var os = require('os');
var fs = require('fs');
var child_process = require('child_process');
// constants
var ONE_SECOND = 1000; // in milliseconds
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
var NUM_INSTALL_RETRIES = 3;
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
var EXEC_KILL_SIGNAL = 'SIGKILL';
const ONE_SECOND = 1000; // in milliseconds
const ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
const INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
const NUM_INSTALL_RETRIES = 3;
const CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
const EXEC_KILL_SIGNAL = 'SIGKILL';
function forgivingWhichSync (cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
return '';
}
const whichResult = which.sync(cmd, { nothrow: true });
// On null, returns empty string to maintain backwards compatibility
// realpathSync follows symlinks
return whichResult === null ? '' : fs.realpathSync(whichResult);
}
module.exports.list_images_using_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) {
return execa('avdmanager', ['list', 'avd']).then(({ stdout: output }) => {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
// To return more detailed information use img_obj
var img_obj = {};
if (response[i].match(/Name:\s/)) {
img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
img_obj.name = response[i].split('Name: ')[1].replace('\r', '');
if (response[i + 1].match(/Device:\s/)) {
i++;
img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
img_obj.device = response[i].split('Device: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Path:\s/)) {
i++;
img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
img_obj.path = response[i].split('Path: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Target:\s/)) {
i++;
if (response[i + 1].match(/ABI:\s/)) {
img_obj['abi'] = response[i + 1].split('ABI: ')[1].replace('\r', '');
img_obj.abi = response[i + 1].split('ABI: ')[1].replace('\r', '');
}
// This next conditional just aims to match the old output of `android list avd`
// We do so so that we don't have to change the logic when parsing for the
@@ -83,24 +76,24 @@ module.exports.list_images_using_avdmanager = function () {
// This allows us to transitionally support both `android` and `avdmanager` binaries,
// depending on what SDK version the user has
if (response[i + 1].match(/Based\son:\s/)) {
img_obj['target'] = response[i + 1].split('Based on:')[1];
if (img_obj['target'].match(/Tag\/ABI:\s/)) {
img_obj['target'] = img_obj['target'].split('Tag/ABI:')[0].replace('\r', '').trim();
if (img_obj['target'].indexOf('(') > -1) {
img_obj['target'] = img_obj['target'].substr(0, img_obj['target'].indexOf('(') - 1).trim();
img_obj.target = response[i + 1].split('Based on:')[1];
if (img_obj.target.match(/Tag\/ABI:\s/)) {
img_obj.target = img_obj.target.split('Tag/ABI:')[0].replace('\r', '').trim();
if (img_obj.target.indexOf('(') > -1) {
img_obj.target = img_obj.target.substr(0, img_obj.target.indexOf('(') - 1).trim();
}
}
var version_string = img_obj['target'].replace(/Android\s+/, '');
var version_string = img_obj.target.replace(/Android\s+/, '');
var api_level = android_sdk.version_string_to_api_level[version_string];
if (api_level) {
img_obj['target'] += ' (API level ' + api_level + ')';
img_obj.target += ' (API level ' + api_level + ')';
}
}
}
if (response[i + 1].match(/Skin:\s/)) {
i++;
img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
img_obj.skin = response[i].split('Skin: ')[1].replace('\r', '');
}
emulator_list.push(img_obj);
@@ -109,41 +102,40 @@ module.exports.list_images_using_avdmanager = function () {
if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
} */
}
return emulator_list;
});
};
module.exports.list_images_using_android = function () {
return superspawn.spawn('android', ['list', 'avd']).then(function (output) {
return execa('android', ['list', 'avd']).then(({ stdout: output }) => {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
// To return more detailed information use img_obj
var img_obj = {};
if (response[i].match(/Name:\s/)) {
img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
img_obj.name = response[i].split('Name: ')[1].replace('\r', '');
if (response[i + 1].match(/Device:\s/)) {
i++;
img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
img_obj.device = response[i].split('Device: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Path:\s/)) {
i++;
img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
img_obj.path = response[i].split('Path: ')[1].replace('\r', '');
}
if (response[i + 1].match(/\(API\slevel\s/) || (response[i + 2] && response[i + 2].match(/\(API\slevel\s/))) {
i++;
var secondLine = response[i + 1].match(/\(API\slevel\s/) ? response[i + 1] : '';
img_obj['target'] = (response[i] + secondLine).split('Target: ')[1].replace('\r', '');
img_obj.target = (response[i] + secondLine).split('Target: ')[1].replace('\r', '');
}
if (response[i + 1].match(/ABI:\s/)) {
i++;
img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', '');
img_obj.abi = response[i].split('ABI: ')[1].replace('\r', '');
}
if (response[i + 1].match(/Skin:\s/)) {
i++;
img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
img_obj.skin = response[i].split('Skin: ')[1].replace('\r', '');
}
emulator_list.push(img_obj);
@@ -152,7 +144,6 @@ module.exports.list_images_using_android = function () {
if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
} */
}
return emulator_list;
});
@@ -170,15 +161,13 @@ module.exports.list_images_using_android = function () {
}
*/
module.exports.list_images = function () {
return Q.fcall(function () {
return Promise.resolve().then(function () {
if (forgivingWhichSync('avdmanager')) {
return module.exports.list_images_using_avdmanager();
} else if (forgivingWhichSync('android')) {
return module.exports.list_images_using_android();
} else {
return Q().then(function () {
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
});
return Promise.reject(new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?'));
}
}).then(function (avds) {
// In case we're missing the Android OS version string from the target description, add it.
@@ -228,13 +217,13 @@ module.exports.best_image = function () {
// Returns a promise.
module.exports.list_started = function () {
return Adb.devices({emulators: true});
return Adb.devices({ emulators: true });
};
// Returns a promise.
// TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function () {
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}).then(function (output) {
return execa('android', ['list', 'targets'], { cwd: os.tmpdir() }).then(({ stdout: output }) => {
var target_out = output.split('\n');
var targets = [];
for (var i = target_out.length; i >= 0; i--) {
@@ -277,8 +266,8 @@ module.exports.get_available_port = function () {
module.exports.start = function (emulator_ID, boot_timeout) {
var self = this;
return Q().then(function () {
if (emulator_ID) return Q(emulator_ID);
return Promise.resolve().then(function () {
if (emulator_ID) return Promise.resolve(emulator_ID);
return self.best_image().then(function (best) {
if (best && best.name) {
@@ -287,7 +276,7 @@ module.exports.start = function (emulator_ID, boot_timeout) {
}
var androidCmd = check_reqs.getAbsoluteAndroidCmd();
return Q.reject(new CordovaError('No emulator images (avds) found.\n' +
return Promise.reject(new CordovaError('No emulator images (avds) found.\n' +
'1. Download desired System Image by running: ' + androidCmd + ' sdk\n' +
'2. Create an AVD by running: ' + androidCmd + ' avd\n' +
'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n'));
@@ -296,11 +285,10 @@ module.exports.start = function (emulator_ID, boot_timeout) {
return self.get_available_port().then(function (port) {
// Figure out the directory the emulator binary runs in, and set the cwd to that directory.
// Workaround for https://code.google.com/p/android/issues/detail?id=235461
var emulator_dir = path.dirname(shelljs.which('emulator'));
var emulator_dir = path.dirname(which.sync('emulator'));
var args = ['-avd', emulatorId, '-port', port];
// Don't wait for it to finish, since the emulator will probably keep running for a long time.
child_process
.spawn('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
execa('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
.unref();
// wait for emulator to start
@@ -308,7 +296,7 @@ module.exports.start = function (emulator_ID, boot_timeout) {
return self.wait_for_emulator(port);
});
}).then(function (emulatorId) {
if (!emulatorId) { return Q.reject(new CordovaError('Failed to start emulator')); }
if (!emulatorId) { return Promise.reject(new CordovaError('Failed to start emulator')); }
// wait for emulator to boot up
process.stdout.write('Waiting for emulator to boot (this may take a while)...');
@@ -334,7 +322,7 @@ module.exports.start = function (emulator_ID, boot_timeout) {
*/
module.exports.wait_for_emulator = function (port) {
var self = this;
return Q().then(function () {
return Promise.resolve().then(function () {
var emulator_id = 'emulator-' + port;
return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) {
if (output.indexOf('1') >= 0) {
@@ -344,7 +332,9 @@ module.exports.wait_for_emulator = function (port) {
}, function (error) {
if ((error && error.message &&
(error.message.indexOf('not found') > -1)) ||
(error.message.indexOf('device offline') > -1)) {
(error.message.indexOf('device offline') > -1) ||
(error.message.indexOf('device still connecting') > -1) ||
(error.message.indexOf('device still authorizing') > -1)) {
// emulator not yet started, continue waiting
return self.wait_for_emulator(port);
} else {
@@ -362,18 +352,21 @@ module.exports.wait_for_emulator = function (port) {
*/
module.exports.wait_for_boot = function (emulator_id, time_remaining) {
var self = this;
return Adb.shell(emulator_id, 'ps').then(function (output) {
if (output.match(/android\.process\.acore/)) {
return Adb.shell(emulator_id, 'getprop sys.boot_completed').then(function (output) {
if (output.match(/1/)) {
return true;
} else if (time_remaining === 0) {
return false;
} else {
process.stdout.write('.');
// Check at regular intervals
return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function () {
var updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining;
return self.wait_for_boot(emulator_id, updated_time);
return new Promise(resolve => {
const delay = time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL;
setTimeout(() => {
const updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining;
resolve(self.wait_for_boot(emulator_id, updated_time));
}, delay);
});
}
});
@@ -387,22 +380,22 @@ module.exports.wait_for_boot = function (emulator_id, time_remaining) {
module.exports.create_image = function (name, target) {
console.log('Creating new avd named ' + name);
if (target) {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
return execa('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(' Do you have the latest android targets including ' + target + '?');
console.error(error);
console.error(error.message);
});
} else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
// TODO: there's a more robust method for finding targets in android_sdk.js
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
return execa('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
// TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Ensure you have targets available by running the "android" command');
return Q.reject();
return Promise.reject(new CordovaError());
}, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(error);
console.error(error.message);
});
}
};
@@ -410,17 +403,17 @@ module.exports.create_image = function (name, target) {
module.exports.resolveTarget = function (target) {
return this.list_started().then(function (emulator_list) {
if (emulator_list.length < 1) {
return Q.reject('No running Android emulators found, please start an emulator before deploying your project.');
return Promise.reject(new CordovaError('No running Android emulators found, please start an emulator before deploying your project.'));
}
// default emulator
target = target || emulator_list[0];
if (emulator_list.indexOf(target) < 0) {
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
return Promise.reject(new CordovaError('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'));
}
return build.detectArchitecture(target).then(function (arch) {
return {target: target, arch: arch, isEmulator: true};
return { target: target, arch: arch, isEmulator: true };
});
});
};
@@ -432,18 +425,14 @@ module.exports.resolveTarget = function (target) {
* Returns a promise.
*/
module.exports.install = function (givenTarget, buildResults) {
var target;
// We need to find the proper path to the Android Manifest
var manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml');
if (buildResults.buildMethod === 'gradle') {
manifestPath = path.join(__dirname, '../../AndroidManifest.xml');
}
var manifest = new AndroidManifest(manifestPath);
var pkgName = manifest.getPackageId();
const manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml');
const manifest = new AndroidManifest(manifestPath);
const pkgName = manifest.getPackageId();
// resolve the target emulator
return Q().then(function () {
return Promise.resolve().then(function () {
if (givenTarget && typeof givenTarget === 'object') {
return givenTarget;
} else {
@@ -458,8 +447,7 @@ module.exports.install = function (givenTarget, buildResults) {
}).then(function () {
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed.
return Q.when().then(function () {
return Promise.resolve().then(function () {
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
var execOptions = {
cwd: os.tmpdir(),
@@ -477,24 +465,21 @@ module.exports.install = function (givenTarget, buildResults) {
function adbInstallWithOptions (target, apk, opts) {
events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
var command = 'adb -s ' + target + ' install -r "' + apk + '"';
return Q.promise(function (resolve, reject) {
child_process.exec(command, opts, function (err, stdout, stderr) {
if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr));
// adb does not return an error code even if installation fails. Instead it puts a specific
// message to stdout, so we have to use RegExp matching to detect installation failure.
else if (/Failure/.test(stdout)) {
if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
' or sign and deploy the unsigned apk manually using Android tools.';
} else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
'\nEither uninstall an app or increment the versionCode.';
}
const args = ['-s', target, 'install', '-r', apk];
return execa('adb', args, opts).then(({ stdout }) => {
// adb does not return an error code even if installation fails. Instead it puts a specific
// message to stdout, so we have to use RegExp matching to detect installation failure.
if (/Failure/.test(stdout)) {
if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
' or sign and deploy the unsigned apk manually using Android tools.';
} else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
'\nEither uninstall an app or increment the versionCode.';
}
reject(new CordovaError('Failed to install apk to emulator: ' + stdout));
} else resolve(stdout);
});
throw new CordovaError('Failed to install apk to emulator: ' + stdout);
}
});
}
@@ -521,7 +506,6 @@ module.exports.install = function (givenTarget, buildResults) {
});
// unlock screen
}).then(function () {
events.emit('verbose', 'Unlocking screen...');
return Adb.shell(target.target, 'input keyevent 82');
}).then(function () {

View File

@@ -19,23 +19,23 @@
under the License.
*/
var device = require('./device'),
args = process.argv;
var device = require('./device');
var args = process.argv;
if(args.length > 2) {
if (args.length > 2) {
var install_target;
if (args[2].substring(0, 9) == '--target=') {
if (args[2].substring(0, 9) === '--target=') {
install_target = args[2].substring(9, args[2].length);
device.install(install_target).done(null, function(err) {
device.install(install_target).catch(function (err) {
console.error('ERROR: ' + err);
process.exit(2);
});
} else {
} else {
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
process.exit(2);
}
}
} else {
device.install().done(null, function(err) {
device.install().catch(function (err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

View File

@@ -19,20 +19,20 @@
under the License.
*/
var emulator = require('./emulator'),
args = process.argv;
var emulator = require('./emulator');
var args = process.argv;
var install_target;
if(args.length > 2) {
if (args[2].substring(0, 9) == '--target=') {
if (args.length > 2) {
if (args[2].substring(0, 9) === '--target=') {
install_target = args[2].substring(9, args[2].length);
} else {
} else {
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
process.exit(2);
}
}
}
emulator.install(install_target).done(null, function(err) {
emulator.install(install_target).catch(function (err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

View File

@@ -22,12 +22,12 @@
var devices = require('./device');
// Usage support for when args are given
require('./check_reqs').check_android().then(function() {
devices.list().done(function(device_list) {
device_list && device_list.forEach(function(dev) {
require('./check_reqs').check_android().then(function () {
devices.list().then(function (device_list) {
device_list && device_list.forEach(function (dev) {
console.log(dev);
});
}, function(err) {
}, function (err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

View File

@@ -22,12 +22,12 @@
var emulators = require('./emulator');
// Usage support for when args are given
require('./check_reqs').check_android().then(function() {
emulators.list_images().done(function(emulator_list) {
emulator_list && emulator_list.forEach(function(emu) {
require('./check_reqs').check_android().then(function () {
emulators.list_images().then(function (emulator_list) {
emulator_list && emulator_list.forEach(function (emu) {
console.log(emu.name);
});
}, function(err) {
}, function (err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -20,7 +20,7 @@ SET script_path="%~dp0list-emulator-images"
IF EXIST %script_path% (
node %script_path% %*
) ELSE (
ECHO.
ECHO.
ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2
EXIT /B 1
)

View File

@@ -22,12 +22,12 @@
var emulators = require('./emulator');
// Usage support for when args are given
require('./check_reqs').check_android().then(function() {
emulators.list_started().done(function(emulator_list) {
emulator_list && emulator_list.forEach(function(emu) {
require('./check_reqs').check_android().then(function () {
emulators.list_started().then(function (emulator_list) {
emulator_list && emulator_list.forEach(function (emu) {
console.log(emu);
});
}, function(err) {
}, function (err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -21,8 +19,7 @@
var path = require('path');
var os = require('os');
var Q = require('q');
var child_process = require('child_process');
var execa = require('execa');
var ROOT = path.join(__dirname, '..', '..');
/*
@@ -30,8 +27,7 @@ var ROOT = path.join(__dirname, '..', '..');
* Returns a promise.
*/
module.exports.run = function () {
var d = Q.defer();
var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
var adb = execa('adb', ['logcat'], { cwd: os.tmpdir(), stderr: 'inherit' });
adb.stdout.on('data', function (data) {
var lines = data ? data.toString().split('\n') : [];
@@ -39,14 +35,7 @@ module.exports.run = function () {
console.log(out.join('\n'));
});
adb.stderr.on('data', console.error);
adb.on('close', function (code) {
if (code > 0) {
d.reject('Failed to run logcat command.');
} else d.resolve();
});
return d.promise;
return adb;
};
module.exports.help = function () {

View File

@@ -20,10 +20,8 @@
buildscript {
repositories {
google()
jcenter()
maven {
url "https://maven.google.com"
}
}
// Switch the Android Gradle plugin version requirement depending on the
@@ -38,7 +36,7 @@ buildscript {
apply plugin: 'com.android.library'
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
implementation fileTree(dir: 'libs', include: '*.jar')
debugCompile project(path: ":CordovaLib", configuration: "debug")
releaseCompile project(path: ":CordovaLib", configuration: "release")
}
@@ -46,7 +44,6 @@ dependencies {
android {
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
publishNonDefault true
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6

View File

@@ -14,11 +14,8 @@
*
*/
/* jshint unused: vars */
var fs = require('fs');
var fs = require('fs-extra');
var path = require('path');
var shell = require('shelljs');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
@@ -28,22 +25,7 @@ var handlers = {
if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));
var dest = path.join(obj.targetDir, path.basename(obj.src));
// TODO: This code needs to be replaced, since the core plugins need to be re-mapped to a different location in
// a later plugins release. This is for legacy plugins to work with Cordova.
if (options && options.android_studio === true) {
// If a Java file is using the new directory structure, don't penalize it
if (!obj.targetDir.includes('app/src/main')) {
if (obj.src.endsWith('.java')) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
} else if (obj.src.endsWith('.xml')) {
// We are making a huge assumption here that XML files will be going to res/xml or values/xml
dest = path.join('app/src/main', obj.targetDir, path.basename(obj.src));
}
}
}
var dest = getInstallDestination(obj);
if (options && options.force) {
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
@@ -52,48 +34,38 @@ var handlers = {
}
},
uninstall: function (obj, plugin, project, options) {
var dest = path.join(obj.targetDir, path.basename(obj.src));
var dest = getInstallDestination(obj);
if (options && options.android_studio === true) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
// TODO: Add Koltin extension to uninstall, since they are handled like Java files
if (obj.src.endsWith('java')) {
deleteJava(project.projectDir, dest);
} else {
// Just remove the file, not the whole parent directory
removeFile(path.resolve(project.projectDir, dest));
}
deleteJava(project.projectDir, dest);
}
},
'lib-file': {
install: function (obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src));
if (options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src));
}
var dest = path.join('app/libs', path.basename(obj.src));
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
},
uninstall: function (obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src));
if (options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src));
}
removeFile(project.projectDir, dest);
var dest = path.join('app/libs', path.basename(obj.src));
removeFile(path.resolve(project.projectDir, dest));
}
},
'resource-file': {
install: function (obj, plugin, project, options) {
var dest = path.normalize(obj.target);
if (options && options.android_studio === true) {
dest = path.join('app/src/main', dest);
}
var dest = path.join('app', 'src', 'main', obj.target);
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
},
uninstall: function (obj, plugin, project, options) {
var dest = path.normalize(obj.target);
if (options && options.android_studio === true) {
dest = path.join('app/src/main', dest);
}
removeFile(project.projectDir, dest);
var dest = path.join('app', 'src', 'main', obj.target);
removeFile(path.resolve(project.projectDir, dest));
}
},
'framework': {
framework: {
install: function (obj, plugin, project, options) {
var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
@@ -129,7 +101,7 @@ var handlers = {
if (obj.custom) {
var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
removeFile(project.projectDir, subRelativeDir);
removeFile(path.resolve(project.projectDir, subRelativeDir));
subDir = path.resolve(project.projectDir, subRelativeDir);
// If it's the last framework in the plugin, remove the parent directory.
var parDir = path.dirname(subDir);
@@ -170,12 +142,12 @@ var handlers = {
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
removeFileF(path.resolve(project.www, target));
removeFileF(path.resolve(project.www, 'plugins', plugin.id));
removeFile(path.resolve(project.www, target));
removeFile(path.resolve(project.www, 'plugins', plugin.id));
if (options && options.usePlatformWww) {
// CB-11022 remove file from both directories if usePlatformWww is specified
removeFileF(path.resolve(project.platformWww, target));
removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id));
removeFile(path.resolve(project.platformWww, target));
removeFile(path.resolve(project.platformWww, 'plugins', plugin.id));
}
}
},
@@ -193,13 +165,13 @@ var handlers = {
scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';
var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
shell.mkdir('-p', path.dirname(wwwDest));
fs.ensureDirSync(path.dirname(wwwDest));
fs.writeFileSync(wwwDest, scriptContent, 'utf-8');
if (options && options.usePlatformWww) {
// CB-11022 copy file to both directories if usePlatformWww is specified
var platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
shell.mkdir('-p', path.dirname(platformWwwDest));
fs.ensureDirSync(path.dirname(platformWwwDest));
fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
}
},
@@ -244,14 +216,11 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// check that dest path is located in project directory
if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
shell.mkdir('-p', path.dirname(dest));
fs.ensureDirSync(path.dirname(dest));
if (link) {
symlinkFileOrDirTree(src, dest);
} else if (fs.statSync(src).isDirectory()) {
// XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
shell.cp('-Rf', src + '/*', dest);
} else {
shell.cp('-f', src, dest);
fs.copySync(src, dest);
}
}
@@ -265,11 +234,11 @@ function copyNewFile (plugin_dir, src, project_dir, dest, link) {
function symlinkFileOrDirTree (src, dest) {
if (fs.existsSync(dest)) {
shell.rm('-Rf', dest);
fs.removeSync(dest);
}
if (fs.statSync(src).isDirectory()) {
shell.mkdir('-p', dest);
fs.ensureDirSync(path.dirname(dest));
fs.readdirSync(src).forEach(function (entry) {
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
});
@@ -278,15 +247,8 @@ function symlinkFileOrDirTree (src, dest) {
}
}
// checks if file exists and then deletes. Error if doesn't exist
function removeFile (project_dir, src) {
var file = path.resolve(project_dir, src);
shell.rm('-Rf', file);
}
// deletes file/directory without checking
function removeFileF (file) {
shell.rm('-Rf', file);
function removeFile (file) {
fs.removeSync(file);
}
// Sometimes we want to remove some java, and prune any unnecessary empty directories
@@ -299,7 +261,7 @@ function removeFileAndParents (baseDir, destFile, stopper) {
var file = path.resolve(baseDir, destFile);
if (!fs.existsSync(file)) return;
removeFileF(file);
removeFile(file);
// check if directory is empty
var curDir = path.dirname(file);
@@ -318,3 +280,44 @@ function removeFileAndParents (baseDir, destFile, stopper) {
function generateAttributeError (attribute, element, id) {
return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
}
function getInstallDestination (obj) {
var APP_MAIN_PREFIX = 'app/src/main';
var PATH_SEPARATOR = '/';
var PATH_SEP_MATCH = '\\' + PATH_SEPARATOR;
var PATH_SEP_OR_EOL_MATCH = '(\\' + PATH_SEPARATOR + '|$)';
var appReg = new RegExp('^app' + PATH_SEP_OR_EOL_MATCH);
var libsReg = new RegExp('^libs' + PATH_SEP_OR_EOL_MATCH);
var srcReg = new RegExp('^src' + PATH_SEP_OR_EOL_MATCH);
var srcMainReg = new RegExp('^src' + PATH_SEP_MATCH + 'main' + PATH_SEP_OR_EOL_MATCH);
if (appReg.test(obj.targetDir)) {
// If any source file is using the new app directory structure,
// don't penalize it
return path.join(obj.targetDir, path.basename(obj.src));
} else {
// Plugin using deprecated target directory structure (GH-580)
if (obj.src.endsWith('.java')) {
return path.join(APP_MAIN_PREFIX, 'java', obj.targetDir.replace(srcReg, ''),
path.basename(obj.src));
} else if (obj.src.endsWith('.aidl')) {
return path.join(APP_MAIN_PREFIX, 'aidl', obj.targetDir.replace(srcReg, ''),
path.basename(obj.src));
} else if (libsReg.test(obj.targetDir)) {
if (obj.src.endsWith('.so')) {
return path.join(APP_MAIN_PREFIX, 'jniLibs', obj.targetDir.replace(libsReg, ''),
path.basename(obj.src));
} else {
return path.join('app', obj.targetDir, path.basename(obj.src));
}
} else if (srcMainReg.test(obj.targetDir)) {
return path.join('app', obj.targetDir, path.basename(obj.src));
}
// For all other source files not using the new app directory structure,
// add 'app/src/main' to the targetDir
return path.join(APP_MAIN_PREFIX, obj.targetDir, path.basename(obj.src));
}
}

View File

@@ -16,12 +16,10 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-useless-escape: 0 */
var Q = require('q');
var fs = require('fs');
var fs = require('fs-extra');
var path = require('path');
var shell = require('shelljs');
const nopt = require('nopt');
var events = require('cordova-common').events;
var AndroidManifest = require('./AndroidManifest');
var checkReqs = require('./check_reqs');
@@ -32,17 +30,58 @@ var FileUpdater = require('cordova-common').FileUpdater;
var PlatformJson = require('cordova-common').PlatformJson;
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
const utils = require('./utils');
const GradlePropertiesParser = require('./config/GradlePropertiesParser');
function parseArguments (argv) {
return nopt({
// `jvmargs` is a valid option however, we don't actually want to parse it because we want the entire string as is.
// jvmargs: String
}, {}, argv || [], 0);
}
module.exports.prepare = function (cordovaProject, options) {
var self = this;
let args = {};
if (options && options.options) {
args = parseArguments(options.options.argv);
}
var platformJson = PlatformJson.load(this.locations.root, this.platform);
var munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider());
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
// Get the min SDK version from config.xml
const minSdkVersion = this._config.getPreference('android-minSdkVersion', 'android');
const maxSdkVersion = this._config.getPreference('android-maxSdkVersion', 'android');
const targetSdkVersion = this._config.getPreference('android-targetSdkVersion', 'android');
const androidXEnabled = this._config.getPreference('AndroidXEnabled', 'android');
const isGradlePluginKotlinEnabled = this._config.getPreference('GradlePluginKotlinEnabled', 'android');
const gradlePluginKotlinCodeStyle = this._config.getPreference('GradlePluginKotlinCodeStyle', 'android');
const gradlePropertiesUserConfig = {};
if (minSdkVersion) gradlePropertiesUserConfig.cdvMinSdkVersion = minSdkVersion;
if (maxSdkVersion) gradlePropertiesUserConfig.cdvMaxSdkVersion = maxSdkVersion;
if (targetSdkVersion) gradlePropertiesUserConfig.cdvTargetSdkVersion = targetSdkVersion;
if (args.jvmargs) gradlePropertiesUserConfig['org.gradle.jvmargs'] = args.jvmargs;
if (isGradlePluginKotlinEnabled) {
gradlePropertiesUserConfig['kotlin.code.style'] = gradlePluginKotlinCodeStyle || 'official';
}
// Both 'useAndroidX' and 'enableJetifier' are linked together.
if (androidXEnabled) {
gradlePropertiesUserConfig['android.useAndroidX'] = androidXEnabled;
gradlePropertiesUserConfig['android.enableJetifier'] = androidXEnabled;
}
const gradlePropertiesParser = new GradlePropertiesParser(this.locations.root);
gradlePropertiesParser.configure(gradlePropertiesUserConfig);
// Update own www dir with project's www assets and plugins' assets and js-files
return Q.when(updateWww(cordovaProject, this.locations)).then(function () {
return Promise.resolve(updateWww(cordovaProject, this.locations)).then(function () {
// update project according to config.xml changes.
return updateProjectAccordingTo(self._config, self.locations);
}).then(function () {
@@ -62,13 +101,13 @@ module.exports.clean = function (options) {
var projectRoot = path.resolve(this.root, '../..');
if ((options && options.noPrepare) || !fs.existsSync(this.locations.configXml) ||
!fs.existsSync(this.locations.configXml)) {
return Q();
return Promise.resolve();
}
var projectConfig = new ConfigParser(this.locations.configXml);
var self = this;
return Q().then(function () {
return Promise.resolve().then(function () {
cleanWww(projectRoot, self.locations);
cleanIcons(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
cleanSplashes(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
@@ -95,7 +134,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
// First cleanup current config and merge project's one into own
// Overwrite platform config.xml with defaults.xml.
shell.cp('-f', locations.defaultConfigXml, locations.configXml);
fs.copySync(locations.defaultConfigXml, locations.configXml);
// Then apply config changes from global munge to all config files
// in project (including project's config)
@@ -171,14 +210,14 @@ function updateProjectAccordingTo (platformConfig, locations) {
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
var name = platformConfig.name();
strings.find('string[@name="app_name"]').text = name.replace(/\'/g, '\\\'');
strings.find('string[@name="app_name"]').text = name.replace(/'/g, '\\\'');
var shortName = platformConfig.shortName && platformConfig.shortName();
if (shortName && shortName !== name) {
strings.find('string[@name="launcher_name"]').text = shortName.replace(/\'/g, '\\\'');
strings.find('string[@name="launcher_name"]').text = shortName.replace(/'/g, '\\\'');
}
fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8');
fs.writeFileSync(locations.strings, strings.write({ indent: 4 }), 'utf-8');
events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);
// Java packages cannot support dashes
@@ -194,15 +233,13 @@ function updateProjectAccordingTo (platformConfig, locations) {
manifest.setVersionName(platformConfig.version())
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
.setPackageId(androidPkgName)
.setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
.setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
.setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
.write();
// Java file paths shouldn't be hard coded
var javaPattern = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'), '*.java');
var java_files = shell.ls(javaPattern).filter(function (f) {
return shell.grep(/extends\s+CordovaActivity/g, f);
const javaDirectory = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'));
const javaPattern = /\.java$/;
const java_files = utils.scanDirectory(javaDirectory, javaPattern, true).filter(function (f) {
return utils.grep(f, /extends\s+CordovaActivity/g) !== null;
});
if (java_files.length === 0) {
@@ -211,18 +248,24 @@ function updateProjectAccordingTo (platformConfig, locations) {
events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
}
var destFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0]));
shell.mkdir('-p', path.dirname(destFile));
shell.sed(/package [\w\.]*;/, 'package ' + androidPkgName + ';', java_files[0]).to(destFile);
const destFile = java_files[0];
// var destFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0]));
// fs.ensureDirSync(path.dirname(destFile));
// events.emit('verbose', java_files[0]);
// events.emit('verbose', destFile);
// console.log(locations);
// fs.copySync(java_files[0], destFile);
utils.replaceFileContents(destFile, /package [\w.]*;/, 'package ' + androidPkgName + ';');
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile);
var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin() ?
manifestId.toUpperCase() !== androidPkgName.toUpperCase() :
manifestId !== androidPkgName;
var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin()
? manifestId.toUpperCase() !== androidPkgName.toUpperCase()
: manifestId !== androidPkgName;
if (removeOrigPkg) {
// If package was name changed we need to remove old java with main activity
shell.rm('-Rf', java_files[0]);
fs.removeSync(java_files[0]);
// remove any empty directories
var currentDir = path.dirname(java_files[0]);
var sourcesRoot = path.resolve(locations.root, 'src');
@@ -265,6 +308,14 @@ function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
return resourcePath;
}
function getAdaptiveImageResourcePath (resourcesDir, type, density, name, sourceName) {
if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png');
}
var resourcePath = path.join(resourcesDir, (density ? type + '-' + density + '-v26' : type), name);
return resourcePath;
}
function updateSplashes (cordovaProject, platformResourcesDir) {
var resources = cordovaProject.projectConfig.getSplashScreens('android');
@@ -314,20 +365,202 @@ function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
}
function updateIcons (cordovaProject, platformResourcesDir) {
var icons = cordovaProject.projectConfig.getIcons('android');
const icons = cordovaProject.projectConfig.getIcons('android');
// if there are icon elements in config.xml
// Skip if there are no app defined icons in config.xml
if (icons.length === 0) {
events.emit('verbose', 'This app does not have launcher icons defined');
return;
}
var resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'icon.png');
// 1. loop icons determin if there is an error in the setup.
// 2. during initial loop, also setup for legacy support.
const errorMissingAttributes = [];
const errorLegacyIconNeeded = [];
let hasAdaptive = false;
icons.forEach((icon, key) => {
if (
(icon.background && !icon.foreground) ||
(!icon.background && icon.foreground) ||
(!icon.background && !icon.foreground && !icon.src)
) {
errorMissingAttributes.push(icon.density ? icon.density : 'size=' + (icon.height || icon.width));
}
var android_icons = {};
var default_icon;
if (icon.foreground) {
hasAdaptive = true;
if (
!icon.src &&
(
icon.foreground.startsWith('@color') ||
path.extname(path.basename(icon.foreground)) === '.xml'
)
) {
errorLegacyIconNeeded.push(icon.density ? icon.density : 'size=' + (icon.height || icon.width));
} else if (!icon.src) {
icons[key].src = icon.foreground;
}
}
});
const errorMessage = [];
if (errorMissingAttributes.length > 0) {
errorMessage.push('One of the following attributes are set but missing the other for the density type: ' + errorMissingAttributes.join(', ') + '. Please ensure that all require attributes are defined.');
}
if (errorLegacyIconNeeded.length > 0) {
errorMessage.push('For the following icons with the density of: ' + errorLegacyIconNeeded.join(', ') + ', adaptive foreground with a defined color or vector can not be used as a standard fallback icon for older Android devices. To support older Android environments, please provide a value for the src attribute.');
}
if (errorMessage.length > 0) {
throw new CordovaError(errorMessage.join(' '));
}
let resourceMap = Object.assign(
{},
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_background.png'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.xml'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_background.xml'),
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
);
const preparedIcons = prepareIcons(icons);
if (hasAdaptive) {
resourceMap = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
}
resourceMap = updateIconResourceForLegacy(preparedIcons, resourceMap, platformResourcesDir);
events.emit('verbose', 'Updating icons at ' + platformResourcesDir);
FileUpdater.updatePaths(resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformResourcesDir) {
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
let background;
let foreground;
let targetPathBackground;
let targetPathForeground;
for (const density in android_icons) {
let backgroundVal = '@mipmap/ic_launcher_background';
let foregroundVal = '@mipmap/ic_launcher_foreground';
background = android_icons[density].background;
foreground = android_icons[density].foreground;
if (!background || !foreground) {
// This icon isn't an adaptive icon, so skip it
continue;
}
if (background.startsWith('@color')) {
// Colors Use Case
backgroundVal = background; // Example: @color/background_foobar_1
} else if (path.extname(path.basename(background)) === '.xml') {
// Vector Use Case
targetPathBackground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_background.xml', path.basename(android_icons[density].background));
resourceMap[targetPathBackground] = android_icons[density].background;
} else if (path.extname(path.basename(background)) === '.png') {
// Images Use Case
targetPathBackground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_background.png', path.basename(android_icons[density].background));
resourceMap[targetPathBackground] = android_icons[density].background;
}
if (foreground.startsWith('@color')) {
// Colors Use Case
foregroundVal = foreground;
} else if (path.extname(path.basename(foreground)) === '.xml') {
// Vector Use Case
targetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_foreground.xml', path.basename(android_icons[density].foreground));
resourceMap[targetPathForeground] = android_icons[density].foreground;
} else if (path.extname(path.basename(foreground)) === '.png') {
// Images Use Case
targetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_foreground.png', path.basename(android_icons[density].foreground));
resourceMap[targetPathForeground] = android_icons[density].foreground;
}
// create an XML for DPI and set color
const icLauncherTemplate = `<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="` + backgroundVal + `" />
<foreground android:drawable="` + foregroundVal + `" />
</adaptive-icon>`;
const launcherXmlPath = path.join(platformResourcesDir, 'mipmap-' + density + '-v26', 'ic_launcher.xml');
// Remove the XML from the resourceMap so the file does not get removed.
delete resourceMap[launcherXmlPath];
fs.writeFileSync(path.resolve(launcherXmlPath), icLauncherTemplate);
}
// There's no "default" drawable, so assume default == mdpi.
if (default_icon && !android_icons.mdpi) {
let defaultTargetPathBackground;
let defaultTargetPathForeground;
if (background.startsWith('@color')) {
// Colors Use Case
targetPathBackground = default_icon.background;
} else if (path.extname(path.basename(background)) === '.xml') {
// Vector Use Case
defaultTargetPathBackground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_background.xml', path.basename(default_icon.background));
resourceMap[defaultTargetPathBackground] = default_icon.background;
} else if (path.extname(path.basename(background)) === '.png') {
// Images Use Case
defaultTargetPathBackground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_background.png', path.basename(default_icon.background));
resourceMap[defaultTargetPathBackground] = default_icon.background;
}
if (foreground.startsWith('@color')) {
// Colors Use Case
targetPathForeground = default_icon.foreground;
} else if (path.extname(path.basename(foreground)) === '.xml') {
// Vector Use Case
defaultTargetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_foreground.xml', path.basename(default_icon.foreground));
resourceMap[defaultTargetPathForeground] = default_icon.foreground;
} else if (path.extname(path.basename(foreground)) === '.png') {
// Images Use Case
defaultTargetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_foreground.png', path.basename(default_icon.foreground));
resourceMap[defaultTargetPathForeground] = default_icon.foreground;
}
}
return resourceMap;
}
function updateIconResourceForLegacy (preparedIcons, resourceMap, platformResourcesDir) {
const android_icons = preparedIcons.android_icons;
const default_icon = preparedIcons.default_icon;
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
for (var density in android_icons) {
var targetPath = getImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher.png', path.basename(android_icons[density].src));
resourceMap[targetPath] = android_icons[density].src;
}
// There's no "default" drawable, so assume default == mdpi.
if (default_icon && !android_icons.mdpi) {
var defaultTargetPath = getImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher.png', path.basename(default_icon.src));
resourceMap[defaultTargetPath] = default_icon.src;
}
return resourceMap;
}
function prepareIcons (icons) {
// http://developer.android.com/design/style/iconography.html
var sizeToDensityMap = {
const SIZE_TO_DENSITY_MAP = {
36: 'ldpi',
48: 'mdpi',
72: 'hdpi',
@@ -335,11 +568,15 @@ function updateIcons (cordovaProject, platformResourcesDir) {
144: 'xxhdpi',
192: 'xxxhdpi'
};
const android_icons = {};
let default_icon;
// find the best matching icon for a given density or size
// @output android_icons
var parseIcon = function (icon, icon_size) {
// do I have a platform icon for that density already
var density = icon.density || sizeToDensityMap[icon_size];
var density = icon.density || SIZE_TO_DENSITY_MAP[icon_size];
if (!density) {
// invalid icon defition ( or unsupported size)
return;
@@ -355,12 +592,34 @@ function updateIcons (cordovaProject, platformResourcesDir) {
for (var i = 0; i < icons.length; i++) {
var icon = icons[i];
var size = icon.width;
if (!size) {
size = icon.height;
}
if (!size && !icon.density) {
if (default_icon) {
events.emit('verbose', 'Found extra default icon: ' + icon.src + ' (ignoring in favor of ' + default_icon.src + ')');
const found = {};
const favor = {};
// populating found icon.
if (icon.background && icon.foreground) {
found.background = icon.background;
found.foreground = icon.foreground;
}
if (icon.src) {
found.src = icon.src;
}
if (default_icon.background && default_icon.foreground) {
favor.background = default_icon.background;
favor.foreground = default_icon.foreground;
}
if (default_icon.src) {
favor.src = default_icon.src;
}
events.emit('verbose', 'Found extra default icon: ' + JSON.stringify(found) + ' and ignoring in favor of ' + JSON.stringify(favor) + '.');
} else {
default_icon = icon;
}
@@ -369,45 +628,45 @@ function updateIcons (cordovaProject, platformResourcesDir) {
}
}
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
for (var density in android_icons) {
var targetPath = getImageResourcePath(
platformResourcesDir, 'mipmap', density, 'icon.png', path.basename(android_icons[density].src));
resourceMap[targetPath] = android_icons[density].src;
}
// There's no "default" drawable, so assume default == mdpi.
if (default_icon && !android_icons.mdpi) {
var defaultTargetPath = getImageResourcePath(
platformResourcesDir, 'mipmap', 'mdpi', 'icon.png', path.basename(default_icon.src));
resourceMap[defaultTargetPath] = default_icon.src;
}
events.emit('verbose', 'Updating icons at ' + platformResourcesDir);
FileUpdater.updatePaths(
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
return {
android_icons: android_icons,
default_icon: default_icon
};
}
function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
var icons = projectConfig.getIcons('android');
if (icons.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'icon.png');
events.emit('verbose', 'Cleaning icons at ' + platformResourcesDir);
// No source paths are specified in the map, so updatePaths() will delete the target files.
FileUpdater.updatePaths(
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
// Skip if there are no app defined icons in config.xml
if (icons.length === 0) {
events.emit('verbose', 'This app does not have launcher icons defined');
return;
}
const resourceMap = Object.assign(
{},
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_background.png'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.xml'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_background.xml'),
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
);
events.emit('verbose', 'Cleaning icons at ' + platformResourcesDir);
// No source paths are specified in the map, so updatePaths() will delete the target files.
FileUpdater.updatePaths(resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
}
/**
* Gets a map containing resources of a specified name from all drawable folders in a directory.
*/
function mapImageResources (rootDir, subDir, type, resourceName) {
var pathMap = {};
shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) {
var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
const pathMap = {};
const pattern = new RegExp(type + '+-.+');
utils.scanDirectory(path.join(rootDir, subDir), pattern).forEach(function (drawableFolder) {
const imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
pathMap[imagePath] = null;
});
return pathMap;
@@ -445,8 +704,7 @@ function cleanFileResources (projectRoot, projectConfig, platformDir) {
});
FileUpdater.updatePaths(
resourceMap, {
rootDir: projectRoot, all: true}, logFileOp);
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
}
}

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,8 +17,6 @@
under the License.
*/
/* jshint node: true */
'use strict';
var events = require('cordova-common').events;
@@ -29,21 +25,19 @@ var events = require('cordova-common').events;
* Retry a promise-returning function a number of times, propagating its
* results on success or throwing its error on a failed final attempt.
*
* @arg {Number} attemts_left - The number of times to retry the passed call.
* @arg {Number} attemptsLeft - The number of times to retry the passed call.
* @arg {Function} promiseFunction - A function that returns a promise.
* @arg {...} - Arguments to pass to promiseFunction.
*
* @returns {Promise}
*/
module.exports.retryPromise = function (attemts_left, promiseFunction) {
module.exports.retryPromise = function (attemptsLeft, promiseFunction) {
// NOTE:
// get all trailing arguments, by skipping the first two (attemts_left and
// get all trailing arguments, by skipping the first two (attemptsLeft and
// promiseFunction) because they shouldn't get passed to promiseFunction
var promiseFunctionArguments = Array.prototype.slice.call(arguments, 2);
return promiseFunction.apply(undefined, promiseFunctionArguments).then(
// on success pass results through
function onFulfilled (value) {
return value;
@@ -51,17 +45,16 @@ module.exports.retryPromise = function (attemts_left, promiseFunction) {
// on rejection either retry, or throw the error
function onRejected (error) {
attemptsLeft -= 1;
attemts_left -= 1;
if (attemts_left < 1) {
if (attemptsLeft < 1) {
throw error;
}
events.emit('verbose', 'A retried call failed. Retrying ' + attemts_left + ' more time(s).');
events.emit('verbose', 'A retried call failed. Retrying ' + attemptsLeft + ' more time(s).');
// retry call self again with the same arguments, except attemts_left is now lower
var fullArguments = [attemts_left, promiseFunction].concat(promiseFunctionArguments);
// retry call self again with the same arguments, except attemptsLeft is now lower
var fullArguments = [attemptsLeft, promiseFunction].concat(promiseFunctionArguments);
return module.exports.retryPromise.apply(undefined, fullArguments);
}
);

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
@@ -19,14 +17,11 @@
under the License.
*/
/* jshint loopfunc:true */
var path = require('path');
var build = require('./build');
var emulator = require('./emulator');
var device = require('./device');
var Q = require('q');
var events = require('cordova-common').events;
var PackageType = require('./PackageType');
const { CordovaError, events } = require('cordova-common');
function getInstallTarget (runOptions) {
var install_target;
@@ -52,11 +47,12 @@ function getInstallTarget (runOptions) {
* @return {Promise}
*/
module.exports.run = function (runOptions) {
runOptions = runOptions || {};
var self = this;
var install_target = getInstallTarget(runOptions);
return Q().then(function () {
return Promise.resolve().then(function () {
if (!install_target) {
// no target given, deploy to device if available, otherwise use the emulator.
return device.list().then(function (device_list) {
@@ -98,21 +94,30 @@ module.exports.run = function (runOptions) {
});
}
}
return Q.reject('Target \'' + install_target + '\' not found, unable to run project');
return Promise.reject(new CordovaError(`Target '${install_target}' not found, unable to run project`));
});
});
});
}).then(function (resolvedTarget) {
// Better just call self.build, but we're doing some processing of
// build results (according to platformApi spec) so they are in different
// format than emulator.install expects.
// TODO: Update emulator/device.install to handle this change
return build.run.call(self, runOptions, resolvedTarget).then(function (buildResults) {
if (resolvedTarget.isEmulator) {
return new Promise((resolve) => {
const builder = require('./builders/builders').getBuilder();
const buildOptions = require('./build').parseBuildOptions(runOptions, null, self.root);
// Android app bundles cannot be deployed directly to the device
if (buildOptions.packageType === PackageType.BUNDLE) {
const packageTypeErrorMessage = 'Package type "bundle" is not supported during cordova run.';
events.emit('error', packageTypeErrorMessage);
throw packageTypeErrorMessage;
}
resolve(builder.fetchBuildResults(buildOptions.buildType, buildOptions.arch));
}).then(function (buildResults) {
if (resolvedTarget && resolvedTarget.isEmulator) {
return emulator.wait_for_boot(resolvedTarget.target).then(function () {
return emulator.install(resolvedTarget, buildResults);
});
}
return device.install(resolvedTarget, buildResults);
});
});

View File

@@ -19,21 +19,20 @@
under the License.
*/
var emulator = require('./emulator'),
args = process.argv;
var emulator = require('./emulator');
var args = process.argv;
var install_target;
if(args.length > 2) {
if (args[2].substring(0, 9) == '--target=') {
if (args.length > 2) {
if (args[2].substring(0, 9) === '--target=') {
install_target = args[2].substring(9, args[2].length);
} else {
} else {
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
process.exit(2);
}
}
}
emulator.start(install_target).done(null, function(err) {
emulator.start(install_target).catch(function (err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -5,9 +5,9 @@
:: to you under the Apache License, Version 2.0 (the
:: "License"); you may not use this file except in compliance
:: with the License. You may obtain a copy of the License at
::
::
:: http://www.apache.org/licenses/LICENSE-2.0
::
::
:: Unless required by applicable law or agreed to in writing,
:: software distributed under the License is distributed on an
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

97
bin/templates/cordova/lib/utils.js vendored Normal file
View File

@@ -0,0 +1,97 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
/*
Provides a set of utility methods, which can also be spied on during unit tests.
*/
// TODO: Perhaps this should live in cordova-common?
const fs = require('fs-extra');
const path = require('path');
/**
* Reads, searches, and replaces the found occurences with replacementString and then writes the file back out.
* A backup is not made.
*
* @param {string} file A file path to a readable & writable file
* @param {RegExp} searchRegex The search regex
* @param {string} replacementString The string to replace the found occurences
* @returns void
*/
exports.replaceFileContents = function (file, searchRegex, replacementString) {
let contents = fs.readFileSync(file).toString();
contents = contents.replace(searchRegex, replacementString);
fs.writeFileSync(file, contents);
};
/**
* Reads a file and scans for regex. Returns the line of the first occurence or null if no occurences are found.
*
* @param {string} file A file path
* @param {RegExp} regex A search regex
* @returns string|null
*/
exports.grep = function (file, regex) {
const contents = fs.readFileSync(file).toString().replace(/\\r/g, '').split('\n');
for (let i = 0; i < contents.length; i++) {
const line = contents[i];
if (regex.test(line)) {
return line;
}
}
return null;
};
/**
* Scans directories and outputs a list of found paths that matches the regex
*
* @param {string} directory The starting directory
* @param {RegExp} regex The search regex
* @param {boolean} recursive Enables recursion
* @returns Array<string>
*/
exports.scanDirectory = function (directory, regex, recursive) {
let output = [];
if (fs.existsSync(directory)) {
const items = fs.readdirSync(directory);
for (let i = 0; i < items.length; i++) {
const item = items[i];
const itemPath = path.join(directory, item);
const stats = fs.statSync(itemPath);
if (regex.test(itemPath)) {
output.push(itemPath);
}
if (stats.isDirectory()) {
if (recursive) {
output = output.concat(exports.scanDirectory(itemPath, regex, recursive));
} else {
// Move onto the next item
continue;
}
}
}
}
return output;
};

View File

@@ -19,17 +19,17 @@
under the License.
*/
var log = require('./lib/log'),
reqs = require('./lib/check_reqs'),
args = process.argv;
var log = require('./lib/log');
var reqs = require('./lib/check_reqs');
var args = process.argv;
// Usage support for when args are given
if(args.length > 2) {
if (args.length > 2) {
log.help();
} else {
reqs.run().done(function() {
reqs.run().then(function () {
return log.run();
}, function(err) {
}, function (err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -24,22 +24,23 @@ var nopt = require('nopt');
var path = require('path');
// Support basic help commands
if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0)
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
require('./lib/run').help();
}
// Do some basic argument parsing
var runOpts = nopt({
'verbose' : Boolean,
'silent' : Boolean,
'debug' : Boolean,
'release' : Boolean,
'nobuild': Boolean,
'buildConfig' : path,
'archs' : String,
'device' : Boolean,
'emulator': Boolean,
'target' : String
}, { 'd' : '--verbose' });
verbose: Boolean,
silent: Boolean,
debug: Boolean,
release: Boolean,
nobuild: Boolean,
buildConfig: path,
archs: String,
device: Boolean,
emulator: Boolean,
target: String
}, { d: '--verbose' });
// Make runOptions compatible with PlatformApi run method spec
runOpts.argv = runOpts.argv.remain;
@@ -47,7 +48,7 @@ runOpts.argv = runOpts.argv.remain;
require('./loggingHelper').adjustLoggerLevel(runOpts);
new Api().run(runOpts)
.catch(function(err) {
console.error(err, err.stack);
process.exit(2);
});
.catch(function (err) {
console.error(err, err.stack);
process.exit(2);
});

View File

@@ -19,11 +19,6 @@
under the License.
*/
// Coho updates this line:
var VERSION = "7.1.0-dev";
const Api = require('./Api');
module.exports.version = VERSION;
if (!module.parent) {
console.log(VERSION);
}
console.log(Api.version());

View File

@@ -17,7 +17,7 @@
specific language governing permissions and limitations
under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
<supports-screens
android:largeScreens="true"
@@ -30,20 +30,18 @@
<uses-permission android:name="android.permission.INTERNET" />
<application android:icon="@mipmap/icon" android:label="@string/app_name"
<application android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
android:hardwareAccelerated="true" android:supportsRtl="true">
<activity android:name="__ACTIVITY__"
android:label="@string/activity_name"
android:launchMode="singleTop"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode">
<intent-filter android:label="@string/launcher_name">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="__APILEVEL__"/>
</manifest>

View File

@@ -19,36 +19,84 @@
apply plugin: 'com.android.application'
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
}
buildscript {
apply from: '../CordovaLib/cordova.gradle'
if(cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
String defaultGradlePluginKotlinVersion = kotlin_version
/**
* Fetches the user's defined Kotlin Version from config.xml.
* If the version is not set or invalid, it will default to the ${defaultGradlePluginKotlinVersion}
*/
String gradlePluginKotlinVersion = cdvHelpers.getConfigPreference('GradlePluginKotlinVersion', defaultGradlePluginKotlinVersion)
if(!cdvHelpers.isVersionValid(gradlePluginKotlinVersion)) {
println("The defined Kotlin version (${gradlePluginKotlinVersion}) does not appear to be a valid version. Falling back to version: ${defaultGradlePluginKotlinVersion}.")
gradlePluginKotlinVersion = defaultGradlePluginKotlinVersion
}
// Change the version to be used.
ext.kotlin_version = gradlePluginKotlinVersion
}
repositories {
mavenCentral()
google()
jcenter()
maven {
url "https://maven.google.com"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
apply from: '../CordovaLib/cordova.gradle'
classpath 'com.android.tools.build:gradle:4.0.0'
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
if(cdvHelpers.getConfigPreference('GradlePluginGoogleServicesEnabled', 'false').toBoolean()) {
String defaultGradlePluginGoogleServicesVersion = '4.2.0'
/**
* Fetches the user's defined Google Services Plugin Version from config.xml.
* If the version is not set or invalid, it will default to the ${defaultGradlePluginGoogleServicesVersion}
*/
String gradlePluginGoogleServicesVersion = cdvHelpers.getConfigPreference('GradlePluginGoogleServicesVersion', defaultGradlePluginGoogleServicesVersion)
if(!cdvHelpers.isVersionValid(gradlePluginGoogleServicesVersion)) {
println("The defined Google Services plugin version (${gradlePluginGoogleServicesVersion}) does not appear to be a valid version. Falling back to version: ${defaultGradlePluginGoogleServicesVersion}.")
gradlePluginGoogleServicesVersion = defaultGradlePluginGoogleServicesVersion
}
// Create the Google Services classpath and set it.
String gradlePluginGoogleServicesClassPath = "com.google.gms:google-services:${gradlePluginGoogleServicesVersion}"
println "Adding classpath: ${gradlePluginGoogleServicesClassPath}"
classpath gradlePluginGoogleServicesClassPath
}
}
}
// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
repositories {
mavenCentral();
mavenCentral()
jcenter()
}
}
task wrapper(type: Wrapper) {
gradleVersion = '4.1.0'
gradleVersion = '6.5'
}
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
// Refer to: http://www.gradle.org/docs/current/userguide/tutorial_this_and_that.html
ext {
apply from: '../CordovaLib/cordova.gradle'
// The value for android.compileSdkVersion.
if (!project.hasProperty('cdvCompileSdkVersion')) {
cdvCompileSdkVersion = null;
@@ -65,11 +113,19 @@ ext {
if (!project.hasProperty('cdvMinSdkVersion')) {
cdvMinSdkVersion = null
}
// Sets the maxSdkVersion to the given value.
if (!project.hasProperty('cdvMaxSdkVersion')) {
cdvMaxSdkVersion = null
}
// The value for android.targetSdkVersion.
if (!project.hasProperty('cdvTargetSdkVersion')) {
cdvTargetSdkVersion = null;
}
// Whether to build architecture-specific APKs.
if (!project.hasProperty('cdvBuildMultipleApks')) {
cdvBuildMultipleApks = null
}
// Whether to append a 0 "abi digit" to versionCode when only a single APK is build
// Whether to append a 0 "abi digit" to versionCode when only a single APK is build
if (!project.hasProperty('cdvVersionCodeForceAbiDigit')) {
cdvVersionCodeForceAbiDigit = null
}
@@ -93,16 +149,23 @@ ext {
// PLUGIN GRADLE EXTENSIONS START
// PLUGIN GRADLE EXTENSIONS END
def hasBuildExtras = file('build-extras.gradle').exists()
if (hasBuildExtras) {
def hasBuildExtras1 = file('build-extras.gradle').exists()
if (hasBuildExtras1) {
apply from: 'build-extras.gradle'
}
// Set property defaults after extension .gradle files.
if (ext.cdvCompileSdkVersion == null) {
ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
//ext.cdvCompileSdkVersion = project.ext.defaultCompileSdkVersion
def hasBuildExtras2 = file('../build-extras.gradle').exists()
if (hasBuildExtras2) {
apply from: '../build-extras.gradle'
}
// Set property defaults after extension .gradle files.
ext.cdvCompileSdkVersion = cdvCompileSdkVersion == null ? (
defaultCompileSdkVersion == null
? privateHelpers.getProjectTarget()
: defaultCompileSdkVersion
) : Integer.parseInt('' + cdvCompileSdkVersion);
if (ext.cdvBuildToolsVersion == null) {
ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
//ext.cdvBuildToolsVersion = project.ext.defaultBuildToolsVersion
@@ -117,7 +180,14 @@ if (ext.cdvReleaseSigningPropertiesFile == null && file('../release-signing.prop
// Cast to appropriate types.
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
ext.cdvVersionCodeForceAbiDigit = cdvVersionCodeForceAbiDigit == null ? false : cdvVersionCodeForceAbiDigit.toBoolean();
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : defaultMinSdkVersion
// minSdkVersion, maxSdkVersion and targetSdkVersion
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? defaultMinSdkVersion : Integer.parseInt('' + cdvMinSdkVersion)
if (cdvMaxSdkVersion != null) {
ext.cdvMaxSdkVersion = Integer.parseInt('' + cdvMaxSdkVersion)
}
ext.cdvTargetSdkVersion = cdvTargetSdkVersion == null ? defaultTargetSdkVersion : Integer.parseInt('' + cdvTargetSdkVersion)
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
def computeBuildTargetName(debugBuild) {
@@ -140,24 +210,27 @@ cdvBuildRelease.dependsOn {
return computeBuildTargetName(false)
}
task cdvPrintProps << {
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
println('cdvVersionCode=' + cdvVersionCode)
println('cdvVersionCodeForceAbiDigit=' + cdvVersionCodeForceAbiDigit)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
println('cdvBuildArch=' + cdvBuildArch)
println('computedVersionCode=' + android.defaultConfig.versionCode)
android.productFlavors.each { flavor ->
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
task cdvPrintProps {
doLast {
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
println('cdvVersionCode=' + cdvVersionCode)
println('cdvVersionCodeForceAbiDigit=' + cdvVersionCodeForceAbiDigit)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvMaxSdkVersion=' + cdvMaxSdkVersion)
println('cdvTargetSdkVersion=' + cdvTargetSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
println('cdvBuildArch=' + cdvBuildArch)
println('computedVersionCode=' + android.defaultConfig.versionCode)
android.productFlavors.each { flavor ->
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
}
}
}
android {
defaultConfig {
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId privateHelpers.extractStringFromManifest("package")
@@ -165,6 +238,14 @@ android {
if (cdvMinSdkVersion != null) {
minSdkVersion cdvMinSdkVersion
}
if (cdvMaxSdkVersion != null) {
maxSdkVersion cdvMaxSdkVersion
}
if(cdvTargetSdkVersion != null) {
targetSdkVersion cdvTargetSdkVersion
}
}
lintOptions {
@@ -174,10 +255,11 @@ android {
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
//This code exists for Crosswalk and other Native APIs.
//By default, we multiply the existing version code in the Android Manifest by 10 and
//add a number for each architecture. If you are not using Crosswalk or SQLite, you can
//ignore this chunk of code, and your version codes will be respected.
// This code exists for Crosswalk and other Native APIs.
// By default, we multiply the existing version code in the
// Android Manifest by 10 and add a number for each architecture.
// If you are not using Crosswalk or SQLite, you can
// ignore this chunk of code, and your version codes will be respected.
if (Boolean.valueOf(cdvBuildMultipleApks)) {
flavorDimensions "default"
@@ -243,9 +325,14 @@ android {
}
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
}
if (cdvDebugSigningPropertiesFile) {
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
}
/*
@@ -257,6 +344,11 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: '*.jar')
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
// SUB-PROJECT DEPENDENCIES START
debugCompile(project(path: ":CordovaLib", configuration: "debug"))
releaseCompile(project(path: ":CordovaLib", configuration: "release"))
@@ -322,3 +414,7 @@ for (def func : cdvPluginPostBuildExtras) {
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}
if (cdvHelpers.getConfigPreference('GradlePluginGoogleServicesEnabled', 'false').toBoolean()) {
apply plugin: 'com.google.gms.google-services'
}

File diff suppressed because it is too large Load Diff

View File

@@ -102,13 +102,13 @@ h1 {
50% { opacity: 0.4; }
to { opacity: 1.0; }
}
@-webkit-keyframes fade {
from { opacity: 1.0; }
50% { opacity: 0.4; }
to { opacity: 1.0; }
}
.blink {
animation:fade 3000ms infinite;
-webkit-animation:fade 3000ms infinite;

View File

@@ -19,33 +19,31 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
maven {
url "https://maven.google.com"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url "https://maven.google.com"
}
}
//This replaces project.properties w.r.t. build settings
project.ext {
defaultBuildToolsVersion="27.0.1" //String
defaultMinSdkVersion=19 //Integer - Minimum requirement is Android 4.4
defaultTargetSdkVersion=27 //Integer - We ALWAYS target the latest by default
defaultCompileSdkVersion=27 //Integer - We ALWAYS compile with the latest by default
defaultBuildToolsVersion="29.0.2" //String
defaultMinSdkVersion=22 //Integer - Minimum requirement is Android 5.1
defaultTargetSdkVersion=29 //Integer - We ALWAYS target the latest by default
defaultCompileSdkVersion=29 //Integer - We ALWAYS compile with the latest by default
}
}

View File

@@ -10,5 +10,12 @@ ant-gen
# Eclipse builds
gen
out
# Gradle builds
# Gradle build artifacts
.gradle
.gradletasknamecache
/build
/CordovaLib/build
/app/build
gradle-app.setting
# Android Studio
.idea

View File

@@ -1,311 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
apply plugin: 'com.android.application'
buildscript {
repositories {
mavenCentral()
jcenter()
}
// Switch the Android Gradle plugin version requirement depending on the
// installed version of Gradle. This dependency is documented at
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
// and https://issues.apache.org/jira/browse/CB-8143
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
}
}
// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
repositories {
mavenCentral();
jcenter()
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.14.1'
}
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
// Refer to: http://www.gradle.org/docs/current/userguide/tutorial_this_and_that.html
ext {
apply from: 'CordovaLib/cordova.gradle'
// The value for android.compileSdkVersion.
if (!project.hasProperty('cdvCompileSdkVersion')) {
cdvCompileSdkVersion = null;
}
// The value for android.buildToolsVersion.
if (!project.hasProperty('cdvBuildToolsVersion')) {
cdvBuildToolsVersion = null;
}
// Sets the versionCode to the given value.
if (!project.hasProperty('cdvVersionCode')) {
cdvVersionCode = null
}
// Sets the minSdkVersion to the given value.
if (!project.hasProperty('cdvMinSdkVersion')) {
cdvMinSdkVersion = null
}
// Whether to build architecture-specific APKs.
if (!project.hasProperty('cdvBuildMultipleApks')) {
cdvBuildMultipleApks = null
}
// .properties files to use for release signing.
if (!project.hasProperty('cdvReleaseSigningPropertiesFile')) {
cdvReleaseSigningPropertiesFile = null
}
// .properties files to use for debug signing.
if (!project.hasProperty('cdvDebugSigningPropertiesFile')) {
cdvDebugSigningPropertiesFile = null
}
// Set by build.js script.
if (!project.hasProperty('cdvBuildArch')) {
cdvBuildArch = null
}
// Plugin gradle extensions can append to this to have code run at the end.
cdvPluginPostBuildExtras = []
}
// PLUGIN GRADLE EXTENSIONS START
// PLUGIN GRADLE EXTENSIONS END
def hasBuildExtras = file('build-extras.gradle').exists()
if (hasBuildExtras) {
apply from: 'build-extras.gradle'
}
// Set property defaults after extension .gradle files.
if (ext.cdvCompileSdkVersion == null) {
ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
}
if (ext.cdvBuildToolsVersion == null) {
ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
}
if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) {
ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties'
}
if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) {
ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties'
}
// Cast to appropriate types.
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : Integer.parseInt('' + cdvMinSdkVersion)
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
def computeBuildTargetName(debugBuild) {
def ret = 'assemble'
if (cdvBuildMultipleApks && cdvBuildArch) {
def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
}
return ret + (debugBuild ? 'Debug' : 'Release')
}
// Make cdvBuild a task that depends on the debug/arch-sepecific task.
task cdvBuildDebug
cdvBuildDebug.dependsOn {
return computeBuildTargetName(true)
}
task cdvBuildRelease
cdvBuildRelease.dependsOn {
return computeBuildTargetName(false)
}
task cdvPrintProps << {
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
println('cdvVersionCode=' + cdvVersionCode)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
println('cdvBuildArch=' + cdvBuildArch)
println('computedVersionCode=' + android.defaultConfig.versionCode)
android.productFlavors.each { flavor ->
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
}
}
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
defaultConfig {
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId privateHelpers.extractStringFromManifest("package")
if (cdvMinSdkVersion != null) {
minSdkVersion cdvMinSdkVersion
}
}
lintOptions {
abortOnError false;
}
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
if (Boolean.valueOf(cdvBuildMultipleApks)) {
productFlavors {
armv7 {
versionCode defaultConfig.versionCode*10 + 2
ndk {
abiFilters "armeabi-v7a", ""
}
}
x86 {
versionCode defaultConfig.versionCode*10 + 4
ndk {
abiFilters "x86", ""
}
}
all {
ndk {
abiFilters "all", ""
}
}
}
}
/*
ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO!
else if (!cdvVersionCode) {
def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion")
// Vary versionCode by the two most common API levels:
// 14 is ICS, which is the lowest API level for many apps.
// 20 is Lollipop, which is the lowest API level for the updatable system webview.
if (minSdkVersion >= 20) {
defaultConfig.versionCode += 9
} else if (minSdkVersion >= 14) {
defaultConfig.versionCode += 8
}
}
*/
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
if (cdvReleaseSigningPropertiesFile) {
signingConfigs {
release {
// These must be set or Gradle will complain (even if they are overridden).
keyAlias = ""
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
storeFile = null
storePassword = "__unset"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
}
if (cdvDebugSigningPropertiesFile) {
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
}
}
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
// SUB-PROJECT DEPENDENCIES START
// SUB-PROJECT DEPENDENCIES END
}
def promptForReleaseKeyPassword() {
if (!cdvReleaseSigningPropertiesFile) {
return;
}
if ('__unset'.equals(android.signingConfigs.release.storePassword)) {
android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ')
}
if ('__unset'.equals(android.signingConfigs.release.keyPassword)) {
android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: ');
}
}
gradle.taskGraph.whenReady { taskGraph ->
taskGraph.getAllTasks().each() { task ->
if (task.name == 'validateReleaseSigning' || task.name == 'validateSigningRelease') {
promptForReleaseKeyPassword()
}
}
}
def addSigningProps(propsFilePath, signingConfig) {
def propsFile = file(propsFilePath)
def props = new Properties()
propsFile.withReader { reader ->
props.load(reader)
}
def storeFile = new File(props.get('key.store') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'storeFile'))
if (!storeFile.isAbsolute()) {
storeFile = RelativePath.parse(true, storeFile.toString()).getFile(propsFile.getParentFile())
}
if (!storeFile.exists()) {
throw new FileNotFoundException('Keystore file does not exist: ' + storeFile.getAbsolutePath())
}
signingConfig.keyAlias = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias')
signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword))
signingConfig.storeFile = storeFile
signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword))
def storeType = props.get('storeType', props.get('key.store.type', ''))
if (!storeType) {
def filename = storeFile.getName().toLowerCase();
if (filename.endsWith('.p12') || filename.endsWith('.pfx')) {
storeType = 'pkcs12'
} else {
storeType = signingConfig.storeType // "jks"
}
}
signingConfig.storeType = storeType
}
for (def func : cdvPluginPostBuildExtras) {
func()
}
// This can be defined within build-extras.gradle as:
// ext.postBuildExtras = { ... code here ... }
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 192 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/ic_launcher_background" />
<foreground android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Some files were not shown because too many files have changed in this diff Show More