Compare commits

...

647 Commits
5.1.1 ... 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
Steve Gill
8d9a1b82dd CB-13912 Updated RELEASENOTES and Version for release 7.1.0 2018-02-21 10:50:50 -08:00
Steve Gill
f1b57dd9b4 CB-13912: updated .ratignore to include build diectory 2018-02-20 11:36:55 -08:00
Steve Gill
d2a0323ae4 CB-13912 Updated checked-in node_modules 2018-02-20 11:17:05 -08:00
Simon MacDonald
3a339ba37f ⬆️ Bump gradle tools dependency to 3.0.1 for project template 2018-02-15 17:42:10 -05:00
Steve Gill
e9603b0738 CB-13879 updated gradle plugin 2018-02-14 16:54:06 -08:00
Joe Bowser
3d83fd784b Merge pull request #431 from sunday18/patch-1
CB-13818: (android) Update android_sdk.js to support Android Oreo 8.0 (API 26) emulator
2018-01-26 12:32:53 -08:00
Joe Bowser
38a5f891a4 Merge pull request #430 from abernix/abernix/bump-android-versions
CB-13831: (android) Update `android-versions` to 1.3.0 to support SDK 27.
2018-01-26 08:02:35 -08:00
sunday18
8563c8beef Update android_sdk.js
Update `android_sdk.js` to support Android Oreo (API 26) emulator
2018-01-26 06:21:53 +00:00
Jesse Rosenberger
f91102ee07 CB-13831: (android) Update android-versions to 1.3.0 to support SDK 27.
The `android-versions@1.3.0` version now has support for Android SDK level
27 thanks to 230957205e.

As the `android-versions` npm is a "bundled dependency" this will only
take effect when a new version of `cordova-android` is published, since
bundled dependencies are packed within the npm at publish time, not
automatically fetched as dependencies at publish time (which would have
normally been covered for new installations of cordova-android@7.0.0
thanks to semver caret notation).
2018-01-25 20:08:10 +02:00
Joe Bowser
cc08e9d84a Merge pull request #429 from BBosman/cb13800
CB-13800: Drop pre-KitKat specific code
2018-01-19 11:09:40 -08:00
Bas Bosman
075d38117a CB-13800: (android) Drop pre-KitKat specific code 2018-01-19 13:58:24 +01:00
Joe Bowser
6cc4896690 Merge pull request #426 from infil00p/api_level_bump
CB-13724: Bump Target SDK to API 27
2018-01-17 11:48:51 -08:00
Joe Bowser
673a6773b2 Merge pull request #427 from infil00p/deleteDeprecatedNdk
CB-13646: End of an era.  Using the deprecated NDK by default breaks …
2018-01-10 12:08:46 -08:00
Joe Bowser
3a6923988d CB-13646: End of an era. Using the deprecated NDK by default breaks the build. Crosswalk users need to specify the Gradle parameters to keep it working. 2018-01-08 14:52:46 -08:00
Joe Bowser
0d1692681f CB-13724: Updated the Android Tooling required for the latest version on both the test project, and the template 2018-01-04 15:35:04 -08:00
Joe Bowser
fcf705e007 Merge pull request #425 from deton/fix-nullresult-fromthread
CB-12218: (android) Fix consistency of null result message
2018-01-03 11:24:24 -08:00
Joe Bowser
5c93c214b9 CB-13724: Bump Target SDK to API 27 2018-01-02 14:38:03 -08:00
Joe Bowser
7cbb8401a2 Merge pull request #422 from themightychris/patch-2
Prevent crash when unrecognized android version encounterd
2018-01-02 14:34:17 -08:00
Joe Bowser
b3c262cd47 Merge pull request #424 from jcesarmobile/CB-13721
CB-13721 (Android): fix build apps that use cdvHelpers.getConfigPreference
2018-01-02 13:41:45 -08:00
KIHARA Hideto
45a7b90c6c CB-12218: (android) Fix consistency of null result message
Fix problem that JavaScript receives ""(empty string) instead of null
if plugin sends null result from new thread.
2018-01-02 12:41:17 +09:00
Julio César
e1c3b4fd94 CB-13721 (Android): fix build apps that use cdvHelpers.getConfigPreference 2017-12-30 01:07:45 +01:00
Chris Alfano
109112ae75 CB-13571: (android) Prevent crash with unrecognized android version
Fixes issue where commands that list images will crash when an image with a version not present in `android-versions` is encountered, as is presently the case with API 27:

```
cordova run android --list
Available android devices:
Available android virtual devices:
LEVEL: null
ERROR: TypeError: Cannot read property 'semver' of null
An unexpected error has occured while running list-emulator-images with code 2: Error: /home/chris/Repositories/acp-guidelines/cordova/platforms/android/cordova/lib/list-emulator-images: Command failed with exit code 2
```

Signed-off-by: Chris Alfano <chris@jarv.us>
2017-12-22 15:56:26 -05:00
Joe Bowser
a24ba41eda Merge pull request #421 from infil00p/test-sdk-bump
CB-12914: Test needs SDK bumps
2017-12-18 12:19:24 -08:00
Joe Bowser
be3f8ef80d CB-12914: Test needs SDK bumps 2017-12-14 13:48:19 -08:00
Joe Bowser
d50dedb5d0 Merge pull request #420 from infil00p/CB12914
CB-12914: Forgot to bump AndroidManifest.xml for play store reasons
2017-12-14 09:58:18 -08:00
Joe Bowser
74e9e213d6 Forgot to bump AndroidManifest.xml for play store reasons 2017-12-12 12:49:54 -08:00
Joe Bowser
d8d92cae61 CB-13621: eslint error 2017-11-30 11:08:39 -08:00
Joe Bowser
99d5b4d982 CB-13622: Testing commit 2017-11-30 10:56:28 -08:00
Joe Bowser
52fd32c837 CB-13621: Wrote similar warning to CB-12948 on iOS. We no longer support update. 2017-11-30 10:40:07 -08:00
Joe Bowser
061f08bc03 Update JS snapshot to version 7.1.0-dev (via coho) 2017-11-30 10:01:30 -08:00
Joe Bowser
735bc98243 Set VERSION to 7.1.0-dev (via coho) 2017-11-30 10:01:28 -08:00
Joe Bowser
e53a65ef07 CB-13620 Updated RELEASENOTES and Version for release 7.0.0 2017-11-30 09:42:11 -08:00
Joe Bowser
3642ffb57a CB-13741: Updating checked-in node_modules, otherwise plugin installation fails 2017-11-29 13:32:44 -08:00
Joe Bowser
3dcc319cd2 Merge pull request #419 from dpogue/signing-fix
CB-13615: Fix paths to signing properties files
2017-11-29 12:21:53 -08:00
Darryl Pogue
8484f7b906 CB-13615: Fix paths to signing properties files 2017-11-29 10:56:05 -08:00
Joe Bowser
59018ab632 Merge pull request #389 from infil00p/StudioProjectCompat
CB-11244: Studio Project Compatibility: Now with merge commit
2017-11-29 09:55:37 -08:00
Joe Bowser
c6cfeb15f4 Fixed typo in the gradle file 2017-11-28 14:59:50 -08:00
Joe Bowser
adc7dab377 CB-13612: eslint fix 2017-11-28 13:09:07 -08:00
Joe Bowser
a9e01f4309 CB-13612: Fix the remapper so that XML files copy over and the Camera works again. 2017-11-28 12:57:00 -08:00
Joe Bowser
0269e532df CB-13741: Bump package.json so we can install plugins 2017-11-28 11:31:04 -08:00
Joe Bowser
4863320e45 Merge branch 'master' into StudioProjectCompat 2017-11-28 10:35:45 -08:00
Joe Bowser
3ad1ed7dbc Merge pull request #418 from dpogue/splash-compress
CB-13610: Compress the default app assets
2017-11-28 10:35:04 -08:00
Joe Bowser
ae823e6c4e Merge pull request #385 from dreifachstein/master
CB-12835: add a Context getter in CordovaInterface
2017-11-28 10:34:19 -08:00
Darryl Pogue
9056e5a2dc CB-13610: Compress the default app assets 2017-11-27 22:46:02 -08:00
Joe Bowser
2b20802dbb Merge branch 'master' into StudioProjectCompat
Merge by hand CB-8976
2017-11-27 14:10:59 -08:00
Joe Bowser
a972d1ef62 Merge pull request #401 from fabulant/CB-8976
CB-8976: Added the `cdvVersionCodeForceAbiDigit` property
2017-11-27 14:02:20 -08:00
Joe Bowser
30c3713dca Merge branch 'master' into StudioProjectCompat 2017-11-27 11:42:25 -08:00
Joe Bowser
6f58d4c474 Merge pull request #352 from Icenium/include-armeabi-dir
CB-12291: (android) Add x86_64, arm64 and armeabi architecture flavors
2017-11-27 11:35:47 -08:00
Joe Bowser
9d9bac397b CB-13602: We were setting the path wrong, this is hacky but it works 2017-11-22 13:18:38 -08:00
Joe Bowser
d8f10c33dc CB-13601: Fixing lint error and mis-spelling of variable 2017-11-22 11:47:05 -08:00
Joe Bowser
5d57eff612 CB-13601: Fixing the standalone run scripts to make sure this works without using the CLI 2017-11-22 11:39:40 -08:00
Joe Bowser
43956c1bc8 Merge branch 'master' into StudioProjectCompat
Fixing conflict caused by merging #417 into master
2017-11-20 18:21:49 -08:00
Joe Bowser
2930900963 Merge branch 'master' into StudioProjectCompat 2017-11-20 13:53:00 -08:00
Joe Bowser
6404780186 Merge pull request #417 from DavidStrausz/master
CB-13580: (android) fix build for multiple apks (different product flavors)
2017-11-16 10:13:50 -08:00
David Strauß
5d99e50c4a CB-13580: (android) fix lint errors 2017-11-16 01:54:04 +01:00
David Strauß
aaeb630eb1 CB-13580: fix build for multiple apks (different product flavors) 2017-11-16 01:27:20 +01:00
Joe Bowser
3acba59494 CB-13297: This just works once you bump the project structure. Java 1.8 compatibility baked-in 2017-11-15 11:39:32 -08:00
Joe Bowser
3760616639 Merge pull request #416 from infil00p/bintray_fix
CB-13558: Upgrading the gradle so we can upload the AAR
2017-11-15 10:42:00 -08:00
Joe Bowser
026dce563b CB-13558: Upgrading the gradle so we can upload the AAR 2017-11-09 11:14:47 -08:00
Joe Bowser
b77febc7a7 Merge branch 'master' into StudioProjectCompat 2017-11-07 10:52:37 -08:00
Joe Bowser
83601dca2f Update JS snapshot to version 6.5.0-dev (via coho) 2017-11-06 11:44:23 -08:00
Joe Bowser
05aeaf1bd2 Set VERSION to 6.5.0-dev (via coho) 2017-11-06 11:44:20 -08:00
Joe Bowser
b138867b78 CB-13528 Updated RELEASENOTES and Version for release 6.4.0 2017-11-06 11:43:47 -08:00
Joe Bowser
a1195cefd5 Merge pull request #415 from infil00p/CB-13530
CB-13530: Fix the utility method for finding the built APK for generic APKs
2017-11-03 16:50:32 -07:00
Joe Bowser
b83c3b3684 CB-13530: Removing debug console.logs 2017-11-02 14:45:51 -07:00
Joe Bowser
5502ddaf0d CB-13530: This fixes the basic APK installation 2017-11-02 14:42:39 -07:00
Joe Bowser
f54336eb61 eslint fix 2017-11-01 17:18:41 -07:00
Joe Bowser
18d6884522 CB-11244: Android Studio 3 work, things have changed with how the platform is built 2017-11-01 17:09:00 -07:00
Joe Bowser
8ba0109e55 Completing merge which includes the fix for Android Studio 3.0 2017-11-01 13:46:23 -07:00
Joe Bowser
f50ca85a95 CB-11244: Found bug where the gradle subproject changes weren't actually getting written to the correct gradle file 2017-11-01 13:22:22 -07:00
Joe Bowser
39e6765e64 Merge pull request #413 from infil00p/StudioThreeFix
CB-13289: This fixes the Cordova-Android build on cordova-android 6.x
2017-11-01 10:26:53 -07:00
Joe Bowser
47e20da631 CB-13289: Updating Travis and appveyor 2017-10-31 10:48:19 -07:00
Audrey
bb7b47b063 Merge pull request #414 from audreyso/CB-13501
CB-13501 : update appveyor to support node 8
2017-10-30 16:11:50 -07:00
Joe Bowser
a2618dcde5 CB-13289: eslint fix 2017-10-30 13:17:18 -07:00
Joe Bowser
9fdb126715 CB-13289: Fixing build problems with Studio Three, but keeping Windows Gradle fix for now, will be deprecated 2017-10-30 10:39:59 -07:00
Audrey So
b2a81c09ec :CB-13501 : update appveyor node versions to support node 8 2017-10-30 10:01:50 -07:00
Jan Piotrowski
04fa5d3feb CB-13499: Remove duplicate "setting" in error strings 2017-10-26 22:28:38 +02:00
Joe Bowser
d73108cc13 CB-13289: Fix test to work with new Google Android Gradle DSL 2017-10-25 11:38:07 -07:00
Joe Bowser
3ba00f91bb eslint errors 2017-10-19 13:37:43 -07:00
Joe Bowser
cbee8580d0 CB-13470: Fix Clean so that it cleans the Android Studio structure 2017-10-19 13:31:16 -07:00
Joe Bowser
6ccd6b009b Redoing PR #411 over here because the changes were obliterated from earlier 2017-10-18 11:09:08 -07:00
Joe Bowser
6cf6e20d2e Merge branch 'master' into StudioProjectCompat, fixing conflicts, need
to re-add previous PR fix on the branch
2017-10-18 11:04:44 -07:00
Joe Bowser
93efe71080 Merge pull request #411 from Keyclic/master
CB-12802: (android) Fix build.gradle to sign when building with multiple APK is enable.
2017-10-16 11:23:29 -07:00
Joe Bowser
697c8f129c Merge pull request #410 from filmaj/emu-fixes
CB-13404, CB-13406: `best_image` function fixes when choosing emulator images to deploy to
2017-10-16 10:37:30 -07:00
Joe Bowser
c12f7fef76 Close #379 2017-10-13 11:08:01 -07:00
Kevin
cde238de94 Include missing values for task.name when 'cdvBuildMultipleApks' option is true, 'task.name' can have 'validateSigningArmv7Release' or 'validateSigningX86Release' values too. 2017-10-09 09:17:40 +02:00
filmaj
ec83d481fa CB-13406: Fixed AVD API level comparison when choosing sub-par API level match. Added tests for the best_image method. 2017-10-05 15:18:42 -05:00
filmaj
1f12fdbeea CB-13404: add android-versions to bundledDependencies. Ignore best emulator selection when parsed AVD information does not include API level in the target 2017-10-05 15:17:31 -05:00
Joe Bowser
8743e88550 CB-11244: Fixing eslint errors 2017-10-03 15:06:54 -07:00
Joe Bowser
f3c238db1c CB-11244: Adding specs for resource files inside an Android Studio Project 2017-10-03 14:16:27 -07:00
Joe Bowser
2534a3c767 CB-11244: Added remapping for drawables 2017-10-02 19:04:40 -07:00
Joe Bowser
b1f527e682 Merge branch 'master' into StudioProjectCompat 2017-09-28 10:21:49 -07:00
Audrey
6fa16b615b Merge pull request #408 from audreyso/CB-12895-eslint
CB-12895 : eslint ignoring cordova.js
2017-09-28 08:47:50 -07:00
Audrey So
e5c90badba CB-12895 : eslint ignoring cordova.js 2017-09-26 17:15:30 -07:00
Joe Bowser
be6c6ba976 CB-12895: Temporarily disabling eslint since cordova-js does not have eslint yet. 2017-09-25 14:00:38 -07:00
Joe Bowser
7f3be98199 Update JS snapshot to version 6.4.0-dev (via coho) 2017-09-25 11:37:52 -07:00
Joe Bowser
0e498db735 Set VERSION to 6.4.0-dev (via coho) 2017-09-25 11:37:51 -07:00
Joe Bowser
7203b740fd CB-13323 Updated RELEASENOTES and Version for release 6.3.0 2017-09-25 11:35:59 -07:00
Joe Bowser
97aab900da CB-13323 Updated checked-in node_modules 2017-09-25 11:17:24 -07:00
Joe Bowser
dddb2837dd CB-6936: Merge pull request #304 from uareurapid/master
CB-6936: fix crash when calling methods on a destroyed webview
2017-09-15 13:26:21 -07:00
Joe Bowser
7d282426c4 Merge branch 'master' into StudioProjectCompat
Another merge commit to track master changes
2017-09-14 11:45:57 -07:00
filmaj
1637937664 CB-12981: handle SDK 26.0.2 slightly different AVD list output for Android 8+ AVDs. Would cause "cannot read property replace of undefined" errors when trying to deploy an Android 8 emulator. 2017-09-14 11:26:18 -07:00
filmaj
03144eb160 Add a node and npm package.json script for running java unit tests. Include them in the top-level npm test script. Run java unit tests in travis. Small refactor in gradlebuilder to support building gradle wrapper more easily. Don't explicitly build gradlew on appveyor CI as now npm test will do it for you. 2017-09-14 09:57:37 -07:00
Joe Bowser
53210710ba Merge pull request #404 from mediathand/maven-repo-fix
Update maven repo to include most recent lib versions
2017-09-11 10:04:48 -07:00
Joe Bowser
0d98e09fb1 Merge branch 'master' into StudioProjectCompat 2017-09-05 11:05:42 -07:00
Steve Gill
e7a972df77 updated bundled node_modules 2017-09-05 11:04:12 -07:00
Gil Pedersen
cb2f396e33 Update maven repo to include most recent lib versions 2017-08-29 17:27:02 +02:00
Martin Bektchiev
2940d05209 CB-12291: (android) Add x86_64, arm64 and armeabi architecture flavors 2017-08-16 09:35:33 +03:00
Joe Bowser
2377aa7ac2 Merge pull request #403 from infil00p/api26
CB-13177: Updating to API Level 26
2017-08-15 14:17:09 -07:00
Joe Bowser
f18086f83a Merge branch 'master' into StudioProjectCompat 2017-08-15 13:29:44 -07:00
Joe Bowser
dce3b7ed6c Merge pull request #402 from macdonst/master
Revert CB-12015: initial-scale values less than 1.0 are ignored on Android
2017-08-15 13:22:22 -07:00
Simon MacDonald
5bda4df7fa Revert CB-12015: initial-scale values less than 1.0 are ignored on Android 2017-08-14 20:57:44 -04:00
Joe Bowser
1a8e36ccd3 CB-13177: Updating to API Level 26 2017-08-14 11:41:46 -07:00
Simon MacDonald
14816c7c81 Merge pull request #399 from macdonst/CB-12730
CB-12730: Compat - INTEGRATE
2017-08-11 10:38:06 -04:00
Joe Bowser
8fdb16c555 Fixing cordova-cli related bug 2017-08-04 12:00:39 -07:00
Fabian Eichinger
940439866e CB-8976: Added the cdvVersionCodeForceAbiDigit flag to the template build.gradle that appends 0 to the versionCode when cdvBuildMultipleApks is not set 2017-08-04 18:28:06 +02:00
Joe Bowser
e91b19d006 Fixing error caused by merge commit 2017-07-31 14:10:14 -07:00
Joe Bowser
46905ebe99 Merge branch 'master' into StudioProjectCompat. Dealing with latest
commits merged into master.
2017-07-31 13:43:21 -07:00
Simon MacDonald
d364f46baa CB-12730: Compat - INTEGRATE 2017-07-28 08:56:19 -04:00
Nikita Matrosov
2b53c98cf5 CB-12453: Remove unnecessary double quotes from .bat files which are the causes of crash if project path contains spaces
this closes #362
2017-07-28 14:23:53 +03:00
Nikita Matrosov
36d07d7a15 CB-13031: Fix bug with case-sensitivity of android-packageName
this closes #397
2017-07-28 14:05:53 +03:00
filmaj
458e479681 Trying AppVeyor-supplied workaround for current appveyor time out issues. See http://help.appveyor.com/discussions/problems/7159-builds-timing-out-after-an-hour 2017-07-27 14:33:16 -07:00
Joe Bowser
5fe95570d2 Merge branch 'master' into StudioProjectCompat 2017-07-25 15:07:56 -07:00
filmaj
c3ce2f8a07 Closes #398 2017-07-24 10:11:21 -07:00
Joe Bowser
d0dab4bb09 Merge branch 'master' into StudioProjectCompat
I fixed the master so it passes eslint.
2017-07-18 14:25:56 -07:00
Joe Bowser
893356abcd CB-13034: Fixing eslint error 2017-07-18 14:13:53 -07:00
Joe Bowser
00c879e27d It passed then failed. I'll change the test for now, because perfect is the enemy of good. 2017-07-18 11:53:44 -07:00
Joe Bowser
b65af106ff Make the tests pass 2017-07-17 13:07:32 -07:00
Joe Bowser
fdd7eb3446 Linting fix 2017-07-14 16:51:10 -07:00
Darryl Pogue
22645d9158 [CB-10916] Support display name for Android 2017-07-14 17:37:15 -02:30
filmaj
76dd8613ca CB-12423: make explicit JDK 1.8 or greater is needed in the README. 2017-07-14 11:13:52 -05:00
Joe Bowser
99f15c507d Updating paths in the unit tests, leaving the failing test and coding against that 2017-07-12 11:13:20 -07:00
Joe Bowser
849b887e20 Merge branch 'master' into StudioProjectCompat 2017-07-12 09:39:55 -07:00
Joe Bowser
245d9a1e46 Merge branch 'master' into StudioProjectCompat 2017-07-12 09:33:37 -07:00
filmaj
5917d4ef0b CB-13006: removed create and update end-to-end tests, and instead added more unit test coverage. tweaked code coverage invocation so that we get coverage details on the create.js module. slight changes to the create.js module so that it is slightly easier to test. 2017-07-11 12:54:15 -05:00
filmaj
90053eb9df CB-12950: lots of tweaks for end-to-end test runs, especially on CI:
- rename npm tasks to reflect what they do (npm run unit-tests, npm run e2e-tests). main `npm test` runs linter, unit tests and e2e tests now.
- locked jasmine down to ~2.6.0.
- consolidate gitignores.
- updated travis to run `npm test`. add android sdk installation to appveyor ci run.align android dpendencies across travis and appveyor. have appveyor install gradle. force gradle to version 3.4.1 in appveyor, as that seems to be the only version choco has. explicitly invoke sdkmanager to move license accepting process along.
2017-06-27 15:59:16 -05:00
Joe Bowser
a7304b9a19 Finishing the linting 2017-06-27 13:15:04 -07:00
Nikita Matrosov
540929c6a0 CB-9971: Suppressed unwanted java stderr output when running gradle wrapper
this closes #388
2017-06-26 21:17:58 +03:00
Joe Bowser
e456175a81 Merge branch 'master' into StudioProjectCompat
This will have to be linted
2017-06-26 10:29:31 -07:00
filmaj
3ff32092a3 CB-12954 🔪 remove jshint leftovers 2017-06-26 12:26:53 -05:00
Audrey So
55d7cf3865 CB-12895 : updated .eslintrc file in spec dir and set jasmine true and removed root is true 2017-06-23 08:44:08 -07:00
Audrey So
ac4ac935f6 CB-12895 : added .eslintrc files to set up jasmine environment 2017-06-23 08:07:08 -07:00
Audrey So
d83d49d83b CB-12895 : fixed eslint errors 2017-06-23 08:07:08 -07:00
Audrey So
e36158a0da CB-12895 : added eslint and removed jshint 2017-06-23 08:07:07 -07:00
Joe Bowser
b20028c42b The prepare step was broken, which breaks the CLI workflow. This was caused by hardcoding the Java directory, which is a very bad idea. 2017-06-22 13:46:18 -07:00
Jesse MacFadyen
5cc14b8031 CB-12605 In Windows get Android studio path from the registry
This closes #387
2017-06-22 10:55:39 -07:00
Joe Bowser
1cda7a9de0 CB-11244: Found bug in Api.js where xml/strings.xml is used instead of values/strings.xml 2017-06-19 14:34:03 -07:00
Joe Bowser
49b76f5c71 Fixing mangled commits that crept into this branch 2017-06-14 11:36:43 -07:00
Joe Bowser
c0474e811d Bump for travis test 2017-06-14 10:16:05 -07:00
Joe Bowser
40c9709445 OK, Going back to the old build.gradle for legacy projects 2017-06-14 09:57:29 -07:00
Joe Bowser
b67e9905bc This is probably a bad idea, but we need to split the gradle files into legacy and new style 2017-06-14 09:57:28 -07:00
Joe Bowser
c74192d578 Adding conditional code into Gradle, this is a bit dirty since we can't explicitly test it but we'll just have to rely on jasmine 2017-06-14 09:57:28 -07:00
Joe Bowser
33feb00e8f Adding the if statement to see if we can support both structures with minimal editing, TODO: actually write tests for this somehow 2017-06-14 09:57:28 -07:00
Joe Bowser
8f16df4c90 Adding logic to upgrade both Classic and Android Studio style project structures 2017-06-14 09:57:28 -07:00
Joe Bowser
fb6cb51e64 Fixing lint errors 2017-06-14 09:57:28 -07:00
Joe Bowser
28ebbb8f02 CB-11244: Setup Api.js to support multiple builders based on project structure 2017-06-14 09:57:28 -07:00
Joe Bowser
bd4ddcdedd Updated AndroidStudio to only look for the app directory to determine studio status 2017-06-14 09:57:28 -07:00
Joe Bowser
e621edfba7 Fixing the Android Studio detection and making it automatically pick the right builder, good for upgrading Cordova 2017-06-14 09:57:27 -07:00
Joe Bowser
304a899114 Fixed the specification of the builders in the run command by getting build to check what was being passed from run 2017-06-14 09:57:27 -07:00
Joe Bowser
8391af2e8f JsHint Fixes, deleting unused methods 2017-06-14 09:57:27 -07:00
Joe Bowser
69ab6a0e0d Changing the project to add the app directory as a dependency 2017-06-14 09:57:27 -07:00
Joe Bowser
a216f0db75 CB-11244: Changing directory creation, will most likely hide this behind a flag for the next release of Cordova-Android, and then make it default in the next major pending feedback 2017-06-14 09:57:27 -07:00
Joe Bowser
69260fb96a Fix the overwriting of Fil's fix, blargh 2017-06-14 09:57:27 -07:00
Joe Bowser
db87e0ae6a Made changes so cordova/build builds with the new project. Need to work on plugin installation. 2017-06-14 09:57:26 -07:00
Joe Bowser
8ead919fae Managed to get the project to mostly compile, still need to re-work the build command to add the app project 2017-06-14 09:57:26 -07:00
Joe Bowser
b73c04f3c8 Updating gradle version in the build file 2017-06-14 09:57:26 -07:00
Joe Bowser
f790aeb8f6 Setting up the create command so we actually have all the directories in the right place, and define default variables in the top level build.gradle 2017-06-14 09:57:26 -07:00
Joe Bowser
7b17abc555 Fixing linting issues 2017-06-14 09:57:26 -07:00
Joe Bowser
ffadf5dd51 Changing this so we pass lint 2017-06-14 09:57:25 -07:00
Joe Bowser
23d8d99925 Moving Android Manifest finding to the Gradle and Studio builders. 2017-06-14 09:57:25 -07:00
Joe Bowser
d88df59c32 Adding the Studio Builder to build a project based on Android Studio,
and deleting Ant, since Google does not support Ant Builds anymore.
Sorry guys!
2017-06-14 09:57:25 -07:00
Xiaolei Yu
17906735df CB-12835: add a Context getter in CordovaInterface
A custom engine may live outside of the Activity's lifecycle and the
Activity instance may not always be available. This getter allows
Context accesses in all cases.
2017-06-11 17:51:19 +08:00
Audrey So
3a6e898b12 CB-12762 : pointed package.json repo items to github mirrors instead of apache repos site
This closes #383
2017-06-07 09:28:22 -07:00
filmaj
0cc3df3747 CB-12859: document how to run the native tests. add a README to the test/ project. 2017-06-06 14:37:57 -05:00
Joe Bowser
2bc842a2b3 This commit should exist on the Crosswalk Plugin, Close #357 2017-05-29 13:04:17 -07:00
Joe Bowser
1c6f640026 Commit already merged, Close #378 2017-05-29 13:02:35 -07:00
Joe Bowser
6daad829cc Close #283 2017-05-29 09:53:49 -07:00
Darryl Pogue
7d926822ed CB-8980: Ensure copied resource-files are cleaned
This closes #377
2017-05-25 16:11:20 -07:00
Audrey So
d4dcbb13fc CB-12617 : removed node0.x support for platforms and added engineStrict
This closes #372
2017-05-25 14:09:55 -07:00
filmaj
f396712f59 CB-12847: added bugs entry to package.json. 2017-05-24 00:54:03 +02:00
filmaj
d97250f968 Update JS snapshot to version 6.3.0-dev (via coho) 2017-05-02 16:19:21 -07:00
filmaj
e7e8e95242 Set VERSION to 6.3.0-dev (via coho) 2017-05-02 16:19:19 -07:00
filmaj
d518a655a8 CB-12746: updated release notes for impending 6.2.3 release, since some of the reported changes did not make it into 6.2.2. 2017-05-02 16:04:20 -07:00
filmaj
b6a5844027 CB-12746: decrement working dev version on master to 6.2.4-dev, to align with impending patch release. 2017-05-02 16:04:20 -07:00
filmaj
9d9abea157 Start of GradleBuilder.js specs - puts it on the code coverage radar. 2017-05-02 15:31:26 -07:00
Simon MacDonald
ee1165ea33 CB-12015: initial-scale values less than 1.0 are ignored on Android
This closes #376
2017-05-01 11:20:25 -07:00
Joe Bowser
2704ee54cf This closes #374 2017-05-01 10:51:50 -07:00
Steve Gill
ad01d28351 Set VERSION to 6.3.0-dev (via coho) 2017-04-24 22:05:43 -07:00
Steve Gill
a215c1cf30 CB-12697 Updated RELEASENOTES and Version for release 6.2.2 2017-04-24 22:04:56 -07:00
Steve Gill
cadea2f6c3 CB-12697 Updated checked-in node_modules 2017-04-24 21:56:28 -07:00
filmaj
e13e15d3e9 CB-12640: better handling of unrecognized commands on windows. removed error checking in emulator image listing when shelling out, as we already defensively dont shell out if the program is not on the PATH / not recognized. added additional test for windows unrecognized command errors for target listing. fixed up spying in a test. 2017-04-10 12:12:46 -07:00
filmaj
6ef2f67ae8 CB-12640: flipped avd parsing logic so that it always tries to use avdmanager to retrieve avds first, then falls back to android command if avdmanager cannot be found (and errors with ENOENT). updated tests - and added explicit tests to ensure to shell out to singular forms of sub-commands when executing android 2017-04-10 12:12:46 -07:00
filmaj
765c4ee9f6 CB-12640: support for android sdk tools 26.0.1. simplified target parsing by using avdmanager instead of sdkmanager. flipped target parsing logic so that it always tries to use avdmanager to retrieve targets first, then falls back to android command if avdmanager cannot be found (and errors with ENOENT). updated tests. 2017-04-10 12:12:46 -07:00
Steve Gill
a4103d8dc8 updated version in build.gradle 2017-04-07 10:56:39 -07:00
Steve Gill
8e0f021cad Set VERSION to 6.3.0-dev (via coho) 2017-04-02 17:43:13 -05:00
Steve Gill
002ab85f76 CB-12627 Updated RELEASENOTES and Version for release 6.2.1 2017-04-02 17:40:35 -05:00
Steve Gill
4a0f69a3f0 CB-12621: reverted elementtree dep to 0.1.6 2017-04-02 17:11:17 -05:00
Steve Gill
8a2e96d995 Update JS snapshot to version 6.3.0-dev (via coho) 2017-03-28 15:23:50 -07:00
Steve Gill
13dbd2f5d4 Set VERSION to 6.3.0-dev (via coho) 2017-03-28 15:18:35 -07:00
Steve Gill
6d084428a8 CB-12609 Updated RELEASENOTES and Version for release 6.2.0 2017-03-28 15:17:52 -07:00
Steve Gill
312efc3878 CB-12609: added missing license headers 2017-03-28 15:03:47 -07:00
Joe Bowser
301faebfcc CB-12614: Adding headers to tests 2017-03-28 14:27:57 -07:00
Steve Gill
a8af103ff4 CB-12609: checking in updated node_modules 2017-03-27 22:23:09 -07:00
Darryl Pogue
0df50c3f33 CB-8978 Prepare copy resource-files from config.xml
This closes #321
2017-03-27 22:13:13 -07:00
Alexander Sorokin
08c80f77a9 CB-12605 Fix a requirements check failure on Windows 2017-03-27 11:47:32 +03:00
Joe Bowser
ea8d65d7fc CB-12595: Remove the console.log stuff
This closes #370
2017-03-24 10:22:11 -07:00
Joe Bowser
e3198b0f6b CB-12595: Ugly code, but this should find an Android Studio installation and use the sweet gradle center found inside 2017-03-24 10:22:07 -07:00
Josh Jackson
19220de388 testMultipartPopAndEncodeAsJs under newer test system
This closes #358
2017-03-22 10:54:48 -07:00
Josh Jackson
52878d6c5b MultipartMessageTest 2017-03-22 10:54:42 -07:00
Josh Jackson
abfece70c1 Shorter method name 2017-03-22 10:54:41 -07:00
Josh Jackson
33da6e9eb8 Fix encoding of multipart messages. 2017-03-22 10:54:41 -07:00
Joe Bowser
4919a2d2a8 Bumping up the test gradle versions 2017-03-22 10:53:58 -07:00
filmaj
2f2e8a560d CB-12546: account for quotes when detecting android command error for Windows. 2017-03-20 10:09:46 -07:00
filmaj
884818934f CB-12546: on create, update the require path for certain helper modules as directory structure differs. big ol TODO dropped as it _is_ kinda weird. 2017-03-20 10:09:45 -07:00
filmaj
1d8e6f53a1 CB-12546: sort detected targets by numerical suffix when reporting most recent version of android sdk target installed. 2017-03-20 10:09:45 -07:00
filmaj
3554267adf CB-12546: more specs for android_sdk and check_reqs. added fixtures for sdk targets. refactored target listing. 2017-03-20 10:09:45 -07:00
filmaj
f7687a2567 CB-12546: use android_sdk list_targets instead of rewriting the same thing again. 2017-03-20 10:09:45 -07:00
filmaj
ebd4a02d2c CB-12546: switch to superspawn for shelling out where possible. rework android sdk module to work with new sdk. 2017-03-20 10:09:45 -07:00
filmaj
4a354bba86 CB-12546: based on android command exit code and stdout, conditionally try to invoke avdmanager to list out AVD images. tweak relevant test to match behaviour. small tweak to use exposed methods for checking platform (for easier future stubbing). 2017-03-20 10:08:36 -07:00
filmaj
d40c22441f CB-12546: move check_reqs to templates directory and update module references. 2017-03-20 10:08:36 -07:00
filmaj
6395eda0c8 CB-12546: emulator specs. 2017-03-20 10:08:36 -07:00
filmaj
eb6ada8091 CB-12546: more robust sdk location detection. ANDROID_HOME now can be set from location of either of adb, android or avdmanager commands. slightly rework logic of infering ANDROID_HOME + setting up PATH to hopefully separate the logic into clearer sections. check_reqs.check_android now validates SDK Tools 25.3.1 binaries/structure. added specs for check_reqs.check_android. move android sdk version script. expose some helper functions as module methods to help with mocking. 2017-03-20 10:08:35 -07:00
filmaj
a7ef686a27 CB-12546: leverage avdmanager if android warns it is no longer
useful, which happens in Android SDK Tools 25.3.1.
explicitly set the CWD of the spawned emulator process to workaround a recent google android sdk bug.
rename android_sdk_version.js to android_sdk.js, to better reflect its contents.
have create.js copy over the android_sdk_version batch file.
2017-03-20 10:08:35 -07:00
Alexander Sorokin
e2af492a63 CB-12587 A (not so) quick fix for get_gradle_wrapper() call 2017-03-20 15:22:37 +03:00
Joe Bowser
84de9ee0da CB-12574 - Bumping Version
This closes #368
2017-03-15 13:31:24 -07:00
Joe Bowser
e182e669f0 CB-12524: Put the script in the wrong spot, oops
This closes #367
2017-03-13 13:51:37 -07:00
Joe Bowser
a9a28d3e77 CB-12524: Workaround for obsolete gradle on integration servers 2017-03-13 13:51:37 -07:00
Joe Bowser
6a77acdbf2 CB-12524: Thwarted by JSHint again, accidentally deleted the wrong message 2017-03-13 13:51:37 -07:00
Joe Bowser
028725468a CB-12524: Removing the downgrade test, because thanks to Google's Tool Change, this will forever fail. 2017-03-13 13:51:37 -07:00
Joe Bowser
9dbffa6ee8 CB-12524: Forgot to check for the android command on the target check 2017-03-13 13:51:37 -07:00
Joe Bowser
2d3f3cae13 CB-12524: Set Gradle to always build in the project directory since our tests don't run scripts from the project directory by default 2017-03-13 13:51:36 -07:00
Joe Bowser
fb9f0f3ee8 CB-12524: I need travis to install a wrapper on this, since travis fails the gradle check 2017-03-13 13:51:36 -07:00
Joe Bowser
ab276504fd CB-12524: Windows functionality for Gradle execution 2017-03-13 13:51:36 -07:00
Joe Bowser
3cc4e5b440 CB-12524: This now fetches the template from inside of the Android Studio directory, and falls back to a locally installed Gradle instance 2017-03-13 13:51:36 -07:00
Joe Bowser
b2664bc469 CB-12524: Test distributing a wrapper in the framework directory 2017-03-13 13:51:36 -07:00
Bharath Hariharan
7d5afdebe1 Updating version
This closes #364
2017-02-27 16:03:44 -08:00
Joe Bowser
3bbc7fb328 CB-12465: Writing new JUnit Test Instrumentation to replace tests and retire problmatic tests
This closes #363
2017-02-27 09:28:02 -08:00
Bharath Hariharan
ac6ad2dea1 Adding Binary plugin to class path
This closes #361
2017-02-22 16:47:59 -08:00
Bharath Hariharan
0b116f1b5c Updating Maven settings 2017-02-14 16:31:27 -08:00
Bharath Hariharan
acdb934ef9 Add the ability to publish to Bintray jCenter repo 2017-02-13 16:37:39 -08:00
Steve Gill
5591a1a4e8 Update JS snapshot to version 6.2.0-dev (via coho) 2017-01-26 16:30:33 -08:00
Steve Gill
124574bb3a Set VERSION to 6.2.0-dev (via coho) 2017-01-26 16:30:31 -08:00
Steve Gill
a2cfaab7f4 CB-12403 Updated RELEASENOTES and Version for release 6.1.2 2017-01-26 15:21:50 -08:00
Joe Bowser
6355425a6f Change to https by default 2017-01-20 09:31:31 -08:00
audreyso
2d96995801 CB:12018 : added dev dependencies to gitignore
This closes #355
2017-01-17 09:38:14 -08:00
audreyso
3fc4daa447 CB-12018 : updated tests to work with jasmine (promise matcher tests commented out for now) 2017-01-11 17:07:17 -08:00
Julio César
c7ff24b983 Closing invalid pull request: close #350 2017-01-10 19:22:44 +01:00
Jesse MacFadyen
3f674faf30 Merge branch 'CB-5968' of https://github.com/sterlingann/cordova-android 2017-01-08 22:42:18 -08:00
Steve Gill
b9ad1b6b26 Update JS snapshot to version 6.2.0-dev (via coho) 2017-01-03 17:45:55 -08:00
Steve Gill
676f0ddc2e CB-12314 updated release notes for 6.1.1 release 2017-01-03 17:45:15 -08:00
daserge
6c60dc5dc8 CB-12159 Android: Keystore password prompt won't show up
This closes #351
2016-12-08 09:51:24 -08:00
Joe Bowser
1af5ade39a Adding isClean to the spec, this really should have failed a few commits ago
This closes #349
2016-12-05 15:32:18 -08:00
Joe Bowser
ad40d33400 Need to pass lint 2016-11-21 16:36:20 -08:00
Joe Bowser
5017e2302b CB-12169: Check for build directory before running a clean 2016-11-21 14:46:09 -08:00
Joe Bowser
3bfeda4a3b Updating the timeout due to known travis issues 2016-11-03 11:35:42 -07:00
Jesse MacFadyen
348b1b4dda Do not test for non-existent build folder
This closes #348
2016-11-03 11:23:05 -07:00
Jesse MacFadyen
0fd7e7f040 Fixed AndroidStudio tests to actually run, removed app/src/main/assets/ as a requirement and added app/src/main/res instead, added placeholder for build/ folder, Removed dupe gitignore 2016-11-02 19:46:33 -07:00
Joe Bowser
cfa0fa7243 Update JS snapshot to version 6.2.0-dev (via coho) 2016-11-02 16:32:16 -07:00
Joe Bowser
6a63d9df0a Set VERSION to 6.2.0-dev (via coho) 2016-11-02 16:32:15 -07:00
Jesse MacFadyen
36646eb527 rewrite to use retro js
This closes #347
2016-11-02 16:30:14 -07:00
Joe Bowser
48940bf4c0 CB-12109 Updated RELEASENOTES and Version for release 6.1.0 2016-11-02 14:29:49 -07:00
Joe Bowser
5346a386f7 CB-12108: Updating gradle files to work with the latest version of Android Studio, since a patch release breaks this, and Gradle complains if you add the plus 2016-11-02 14:19:21 -07:00
Joe Bowser
b3201ad073 CB-12102: Bump travis to build to API 25 2016-11-01 16:39:09 -07:00
Joe Bowser
35b0ba6d07 Bumping up the version 2016-11-01 16:39:09 -07:00
Joe Bowser
3c087c6716 CB-12101: Spot fix to go into next patch release
This closes #346
2016-11-01 16:37:39 -07:00
Darryl Pogue
854946e313 CB-12077: Fix paths for Android icons/splashscreens
This closes #343
2016-10-31 11:31:26 -07:00
Sterling
d6a1d7a913 updated xxhdpi mipmap icon to 144x144 2016-10-28 21:57:54 -07:00
Sterling
700b425774 replaced with unstretched images 2016-10-28 17:20:56 -07:00
Sterling
e31634b0fb created directories and corresponding images for xxhdpi and xxxhdpi, both drawables and mipmaps 2016-10-28 17:03:45 -07:00
Steve Gill
1f06176149 added framework/build to .ratignore 2016-10-24 11:54:00 -07:00
Joe Bowser
e628026cf0 Fix for broken testUrl test 2016-10-20 12:47:33 -07:00
Joe Bowser
3a512c300e Last minute change of test targets 2016-10-20 12:25:47 -07:00
Joe Bowser
7f9e7c73ab Update JS snapshot to version 6.1.0-dev (via coho) 2016-10-20 11:49:04 -07:00
Joe Bowser
3e7be6cc0f Set VERSION to 6.1.0-dev (via coho) 2016-10-20 11:49:03 -07:00
Joe Bowser
81dfe96355 CB-12038 Updated RELEASENOTES and Version for release 6.0.0 2016-10-20 11:48:20 -07:00
Joe Bowser
efa23bd39b Updating the gradle build for test to use the latest 2016-10-20 11:25:06 -07:00
Joe Bowser
6aa4b1b301 CB-11083: Fixing syncronous file check and future-proofing the JS for Travis
This closes #323
2016-10-19 09:49:42 -07:00
Joe Bowser
304cbef5ed CB-11083: Reading files to check for CordovaLib dependency, if so, we exclude CordovaLib to be safe 2016-10-19 09:49:39 -07:00
Joe Bowser
263968e019 CB-11083: Plugin build script for dependencies without a gradle file 2016-10-19 09:49:39 -07:00
Joe Bowser
c05521e55a CB-11083: The GradleBuidler can tell the difference between a Cordova Plugin Framework and a regular framework based on the name 2016-10-19 09:49:39 -07:00
Joe Bowser
2f3c71feaa CB-11083 Fix to deal with custom frameworks with their own Gradle configuration 2016-10-19 09:49:39 -07:00
Steve Gill
0b710a86a9 CB-12003 updated node_modules 2016-10-17 10:50:30 -07:00
Martin Bektchiev
2e37d2c253 CB-11771 Deep symlink directories to target project instead of linking the directory itself
When installing a plugin with custom library using the --link option the whole directory is symlinked and temporary
files leak into the original plugin directory on build. This leads to broken builds if the same plugin is linked in
2 projects targeting different Cordova versions.

 This closes #326
2016-10-13 18:39:27 -07:00
Steve Gill
d7c1dc5517 updated .gitignore to include dev dep 2016-10-13 18:29:22 -07:00
Simon MacDonald
77b9fd62a7 Fix tab/space issue 2016-10-12 22:21:14 -04:00
Thomas Raffray
f838db4e18 CB-11880 android: Fail-safe for cordova.exec()
- added preliminary sanity check on args variable
- replication of iosExec() behaviour
- allows to call cordova.exec() without args variable

This closes #334
2016-10-12 15:20:40 -07:00
Jesse MacFadyen
b2d7124424 CB-11999 add message, catch exception if require fails 2016-10-12 11:48:06 -07:00
Joe Bowser
2532c4a39e This closes #308 2016-10-11 15:50:54 -07:00
Joe Bowser
be19585c68 Merge branch 'fix-appname-apostrophe' of https://github.com/revolunet/cordova-android
Using a merge commit to merge in a PR
This closes #272
2016-10-11 15:49:20 -07:00
Darryl Pogue
ca0cbad1c7 CB-8722 - Move icons from drawable to mipmap
This closes #340
2016-10-11 13:29:15 -07:00
Joe Bowser
fb53452d37 Unused requires cause AppVeyor to not work
This closes #339
2016-10-07 18:14:36 -07:00
Joe Bowser
b5246f3f09 Clean also wipes out the www directory, which was causing the strange errors that were happening when building 2016-10-07 18:14:36 -07:00
Joe Bowser
4be413af79 Removing try/catch now that we have the new tests and committing latest 2016-10-07 18:14:36 -07:00
Alexander Sorokin
5db2de95f5 CB-11964 Call clean after plugin install and mock it in tests 2016-10-07 18:14:36 -07:00
Joe Bowser
6418add83d Did a try/catch to deal with the unit tests vs actual project environment, code duplication is needed because of builderEnv 2016-10-07 18:14:36 -07:00
Joe Bowser
7d26c51da6 CB-11964: Do a clean when installing a plugin to et around the bug 2016-10-07 18:14:36 -07:00
Shazron Abdullah
dcada79e06 CB-11921 - Add github pull request template 2016-10-04 21:49:55 -07:00
Simon Pireyn
37384c583d CB-11935 Does a best-effort attempt to pause any processing that can be paused safely, such as animations and geolocation.
This closes #337
2016-10-03 16:16:52 -07:00
Joe Bowser
9544783b5e CB-11640: Fixing check_reqs.js so it actually works
This closes #338
2016-10-03 14:41:14 -07:00
Joe Bowser
eaf9b319ca Tweaking the regex 2016-10-03 14:41:14 -07:00
Joe Bowser
47abde2f8d CB-11640: Changing requirements check to ask for Java 8 2016-10-03 14:41:14 -07:00
daserge
30325e4f32 CB-11869 Fix cordova-js android exec tests
Useragent Regexp result is null in node environment - add a check for it
Updated cordova-js
Increased e2e create specs timeout
2016-09-29 12:24:52 +03:00
Joe Bowser
9cf38f8705 CB-11907: Bumping Gradle to work with Android Studio 2.2 and the Gradle Plugin, I should have done this last commit
This closes #336
2016-09-26 15:28:02 -07:00
Joe Bowser
7de4803e21 CB-11907: Bumping Gradle to work with Android Studio 2.2 and the Android Gradle Plugin 2016-09-26 15:06:44 -07:00
Joe Bowser
9586a7ddcd CB-11907: Bumping Android Gradle Plugin version to 2.2 so this works with Android Studio 2016-09-26 15:06:44 -07:00
Simon MacDonald
a4f6c8bf7b Enable background start of Cordova Android apps
This closes #322
2016-09-23 09:12:18 -07:00
Anis Kadri
f5ab6fc602 adding tests for Android Studio
This closes #331
2016-09-20 19:54:38 -07:00
Anis Kadri
222fb1c0e7 adding tests for AndroidStudio 2016-09-20 19:54:34 -07:00
Anis Kadri
511fe7c51e fixing jshint issues 2016-09-20 19:54:34 -07:00
Anis Kadri
acb3cc80b7 First attempt at supporting Android Studio 2016-09-20 19:54:34 -07:00
Pierre-Alexis de Solminihac
23fd0982b0 replace Integer.parseInt with BigInteger so that you can use longer Android version codes
I used to have DNS like version codes (YYYYmmmddXX format) for my apps builds, and I can't go back in older apps because Google Play wont allow me to upload inferior version codes, so I thing we should to use BigInteger instead of Integer.parseInt

This closes #298
2016-09-12 10:39:34 -07:00
Joe Bowser
d2a159d2dc Close #280 2016-09-09 14:23:33 -07:00
Joe Bowser
dc0bfeb0cc CB-11828: Adding dirty userAgent checking to see if we're running Jellybean or not for bridge modes 2016-09-09 12:05:19 -07:00
Joe Bowser
deea0f7e4f CB-11828: Switching default bridge back to ONLINE_BRIDGE 2016-09-09 12:05:19 -07:00
Simon MacDonald
3a33f4c0a6 Add gradle build flag to enable dex in process for large projects 2016-09-08 19:00:55 -04:00
Or Arnon
312872adaf added ability for cordova activity to be viewed in a real full screen regardless of android version (as was the case in previous cordova versions)
This closes #279
This closes #218

PR #218 didn't cleanly merge
2016-09-08 11:02:17 -07:00
Joe Bowser
ba5e3a81e0 Updating travis
This closes #328
2016-09-06 10:42:59 -07:00
Joe Bowser
bf5c32d780 Adding Static Method to CoreAndroid Plugin so we can get the BuildConfig data from other plugins 2016-09-06 10:42:51 -07:00
Joe Bowser
d3cb38de29 Bump Target and Min API levels 2016-09-06 10:42:51 -07:00
Simon MacDonald
047ed52d60 Make evaluateJavaScript brige default
This closes #320
2016-09-01 08:50:29 -07:00
Joe Bowser
bb96bb9a88 Creating an evaluateJavascript branch 2016-09-01 08:49:32 -07:00
Joe Bowser
0ee672d2b0 Close #253
I can't reproduce this issue, and the issue has been closed for a while,
closing the PR makes sense
2016-08-31 14:40:10 -07:00
dheeran1
2a8a6b3970 Update CoreAndroid.java
No modifier (package-private) class to public class

This closes #293
2016-08-30 12:55:12 -07:00
Joe Bowser
5dbc51da59 Close #262 2016-08-29 15:51:32 -07:00
Joe Bowser
f1a6d938cd Close #243 2016-08-29 15:50:06 -07:00
Joe Bowser
991a2b1873 Close #242 2016-08-29 15:49:03 -07:00
Joe Bowser
172c01561b Close #227 2016-08-29 15:48:26 -07:00
Joe Bowser
b2f061cbaf Close #219 2016-08-29 15:47:44 -07:00
Joe Bowser
1f1b2977a4 Close #185 2016-08-29 15:46:23 -07:00
Simon MacDonald
4a0a7bc424 Cordova-Android should use org.apache.cordova.LOG for logging
* Read LogLevel preference from config.xml
* Replace Log with LOG
* Add addition warning method to LOG
2016-08-22 13:19:51 -04:00
Shazron Abdullah
8fbb6d7c62 CB-11727 - travis ci setup is still using 0.10.32 node
This closes #325
2016-08-19 15:50:38 -07:00
Shazron Abdullah
0299467831 CB-11726 - Update appveyor node versions to 4 and 6, so they will always use the latest versions
This closes #324
2016-08-19 15:49:56 -07:00
Steve Gill
2efe240617 updated bundled cordova-common to 1.4.1 2016-08-16 13:23:10 -07:00
Vladimir Kotikov
ed543dc648 Close invalid PRs
This closes #317, closes #318
2016-08-11 14:53:22 +03:00
Szymon Drosdzol
db6295c917 CB-11683 Fixed linking to directories during plugin installation.
This closes #322
2016-08-11 14:47:36 +03:00
akilawickey
d54a42f0f0 fixed CB-11078 Empty string for BackgroundColor preference crashes application
This closes #316
2016-08-02 22:38:58 -07:00
Steve Gill
74ae6651c8 Update JS snapshot to version 5.3.0-dev (via coho) 2016-07-26 14:57:29 -07:00
Steve Gill
f33f352501 Set VERSION to 5.3.0-dev (via coho) 2016-07-26 14:57:28 -07:00
Steve Gill
39c17e71ef CB-11626 Updated RELEASENOTES and Version for release 5.2.2 2016-07-26 13:06:08 -07:00
Steve Gill
081637134f updated cordoova-common to 1.4.0 2016-07-22 12:24:27 -07:00
Joe Bowser
39165a8694 This closes #195 2016-07-11 15:30:10 -07:00
Joe Bowser
ad163ce244 Updaing the gradle for the tests to the latest 2016-07-11 14:26:36 -07:00
Vladimir Kotikov
a7176d39f0 CB-11550 Updated RELEASENOTES for release 5.2.1 2016-07-11 14:06:55 +03:00
Alexander Sorokin
f1c185b771 CB-9489 Fixed "endless waiting for emulator" issue 2016-07-06 15:12:36 +03:00
Steve Gill
103e1e8d24 Update JS snapshot to version 5.3.0-dev (via coho) 2016-06-29 15:04:55 -07:00
Steve Gill
30198c4bdd Set VERSION to 5.3.0-dev (via coho) 2016-06-29 15:04:55 -07:00
Steve Gill
841f02f43f CB-11444 Updated RELEASENOTES and Version for release 5.2.0 2016-06-29 15:04:55 -07:00
Joe Bowser
a5e9861440 Close #288 2016-06-29 14:48:50 -07:00
Vivek Kiran
d69698c23f Update plugin-build.gradle. This closes #312 2016-06-29 14:40:20 -07:00
Vivek Kiran
d763f14ffe CB-11481: android-library is deprecated use com.android.library instead 2016-06-29 12:10:51 -07:00
Joe Bowser
22918152cc Close #178 2016-06-28 10:49:49 -07:00
Joe Bowser
1fcee242dc Close #311 2016-06-27 16:00:39 -07:00
Joe Bowser
3f99c15338 Close #290 2016-06-27 15:58:49 -07:00
Joe Bowser
f496f015b6 Close #275 2016-06-27 15:58:27 -07:00
Joe Bowser
5e7e9d5b6c Close #266 2016-06-27 15:57:47 -07:00
Joe Bowser
8bd4ca49a8 Close #225 2016-06-27 15:54:26 -07:00
Joe Bowser
d33f7e08a8 We already implemented this, Close #222 2016-06-27 15:53:36 -07:00
Joe Bowser
d46ad592b5 Close #196 2016-06-27 15:53:00 -07:00
Joe Bowser
eda765453b Close #183 2016-06-27 15:50:51 -07:00
Joe Bowser
b295fc6687 Close #181 2016-06-27 15:49:10 -07:00
Joe Bowser
0959608ab9 Close #177 2016-06-27 15:46:23 -07:00
Joe Bowser
6db94dbb9a Close #172 2016-06-27 15:41:59 -07:00
Joe Bowser
04a3c9d9ee Adding mavenCentral just in case
This closes #309
2016-06-09 10:50:00 -07:00
Joe Bowser
db4d1f5768 CB-11383: Update to gradle for using jcenter and correct Application plugin 2016-06-06 11:21:30 -07:00
Steve Gill
81d6219663 CB-11365 fixed plugin rm issue with emit being undefined 2016-06-02 11:32:21 -07:00
Jason Ginchereau
3a1b4ffcb8 Use platform config.xml for clean
This closes #295
2016-05-31 10:27:40 -07:00
Jason Ginchereau
d6fe1a65eb Bundle updated node modules 2016-05-31 10:27:39 -07:00
Jason Ginchereau
72bbe9fdf0 CB-11117: Use FileUpdater to optimize prepare for android platform 2016-05-31 10:27:37 -07:00
Tony Homer
d125ece9e9 CB-10096: Upgrade test project to Gradle Plugin 2.1.0
This closes #306
2016-05-25 14:49:13 -07:00
Tony Homer
ecb99c963d CB-11292 fix broken MessageChannel after plugins are recreated
This closes #307
2016-05-25 14:49:13 -07:00
Richard Knoll
9738079c42 CB-11259: Improving build output
This closes #305
2016-05-17 13:31:12 -07:00
Joe Bowser
e9e27ca47c CB-10096: Upgrading to Gradle Plugin 2.1.0 2016-05-09 10:59:36 -07:00
Nikhil Khandelwal
5d21fb26e6 CB-11198 Skip android target sdk check. This closes #303. 2016-05-06 10:44:14 -07:00
PC Dreams (Paulo Cristo)
bcf3f8611a fix crash when calling methods on a destroyed webview 2016-05-04 08:42:14 +01:00
Vladimir Kotikov
d351e316bf CB-11138 Reuse PluginManager from common to add/rm plugins
This closes #301
2016-04-29 15:07:05 +03:00
Vladimir Kotikov
dc6384d063 CB-11138 Bump cordova-common to 1.2.0 2016-04-29 15:07:01 +03:00
daserge
914e2fa35f CB-11163 Android Builds are failing in CI
CB-11133 Handle android emulator start failure (reverted from commit abcaee24f9)
2016-04-28 17:25:39 +03:00
Nikhil Khandelwal
8b96081579 CB-11153 Move to gradle version 2.13
This closes #299
2016-04-26 14:56:02 -07:00
Nikhil Khandelwal
f3e0623d1d CB-11153 Use Gradle 2.8 correctly for building Android projects 2016-04-26 14:55:55 -07:00
daserge
abcaee24f9 CB-11133 Handle android emulator start failure 2016-04-26 19:54:34 +03:00
daserge
8951660393 CB-11132 Fix Error: Cannot read property 'match' of undefined in cordova-android emulator.js 2016-04-21 15:40:33 +03:00
Nikhil Khandelwal
e4ecbad3cb Add simple log for package name being deployed 2016-04-20 09:36:02 -07:00
daserge
40a740b45c CB-11015 Error adding plugin with gradle extras 2016-04-18 13:20:25 +03:00
Vladimir Kotikov
9af7f506f0 CB-11095 Fix plugin add/removal when running on Node v.010 2016-04-18 12:26:39 +03:00
Vladimir Kotikov
9b8558dbde CB-11022 Duplicate www files to both destinations on plugin operations
This closes #289
2016-04-14 12:03:44 +03:00
daserge
d28ca67edb CB-10964 Handle build.json file starting with a BOM. This closes #286 2016-04-08 15:32:04 -07:00
Omar Mefire
0deaba0920 Revert "CB-10881: Logging to further investigations"
This reverts commit 1b43bc99a1.
2016-04-06 10:21:40 -07:00
Joe Bowser
bcb10ba6da Modified build.gradle so that the tests run again 2016-04-04 14:14:57 -07:00
Richard Knoll
ef268e2320 CB-10963: Handle overlapping permission requests from plugins
This closes #285
2016-03-29 15:14:41 -07:00
daserge
d9814c5542 CB-8582 Obscure INSTALL_FAILED_VERSION_DOWNGRADE error when installing app 2016-03-28 13:22:22 +03:00
daserge
dd3b9dd01c CB-10862 Cannot set minsdkversion 2016-03-28 11:35:40 +03:00
Omar Mefire
1b43bc99a1 CB-10881: Logging to further investigations 2016-03-25 08:57:09 -07:00
Joe Bowser
eccde6c0c0 CB-10896: We never enabled cookies on the WebView proper 2016-03-22 12:45:23 -07:00
Vladimir Kotikov
69a2a5b551 CB-10837 Support platform-specific orientation on Android
Remove processing of "orientation" preference and let
user to specify any platform-specific value for Android

 This closes #281
2016-03-21 18:30:20 +03:00
daserge
bc9e895e25 CB-10600 'cordova run android --release' does not use signed and zip-aligned version of APK
De-prioritize unsigned builds
Adds an actionable hint for INSTALL_PARSE_FAILED_NO_CERTIFICATES error
Removes filtering by architecture when not specified
2016-03-18 08:59:59 +03:00
Roberto Andrade
457c5b8b3b CB-9710: Fixing issues parsing android avd list output for certain AVDs which resulted in them not being included in the selection process even if they are the best match.
This closes 221
2016-03-16 09:32:24 -07:00
Vladimir Kotikov
9f1aedb56d CB-10888 Enable coverage reports collection via codecov 2016-03-16 17:42:01 +03:00
Julien Bouquillon
2cefb42559 fix issue with app_name containing apostrophes 2016-03-14 14:01:43 +01:00
Vladimir Kotikov
a048aa94ec CB-10846 Add Travis and AppVeyor badges to readme 2016-03-11 13:35:14 +03:00
Vladimir Kotikov
27d359bd43 CB-10846 Add AppVeyor configuration 2016-03-11 13:03:18 +03:00
Julien Bouquillon
456b4801af add JIRA issue tracker link. This closes #213 2016-03-11 11:40:00 +03:00
daserge
82582e5a5b CB-10749 Use cordova-common.CordovaLogger in cordova-android
Added -d shorthand to all platform scripts
2016-03-09 18:54:05 +03:00
Byoungro So
18e81c4b90 CB-10673 fixed conflicting plugin install issue with overlapped <source-file> tag. Add --force flag. This closes #264. 2016-03-07 23:02:19 -08:00
Joe Bowser
bad0034e5d CB-8976: I wish there was an easier way to test gradle stuff then check it in without having to remember to delete imports
This closes #270
2016-03-04 09:53:01 -08:00
Joe Bowser
99d5ff7347 CB-8976: Accidentally left in the crosswalk build from when I was experimenting with this file 2016-03-04 09:53:00 -08:00
Joe Bowser
dc579417fe CB-8976: Removing the auto-version for non-Crosswalk applications 2016-03-04 09:53:00 -08:00
daserge
fe906dabda CB-10768 Use cordova-common.superspawn in GradleBuilder 2016-03-03 21:12:28 +03:00
Vladimir Kotikov
8ab75e7109 CB-10729 Move plugin handlers tests for into platform's repo 2016-03-01 11:31:14 +03:00
daserge
aa2b3d3da1 CB-10669 cordova run --list cannot find 'adb' 2016-02-26 13:29:26 +03:00
Byoungro So
0a1b71f125 CB-10660 fixed the exception when removing a non-existing directory. This closes #261 2016-02-26 10:40:33 +03:00
1149 changed files with 23268 additions and 97585 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

3
.eslintignore Normal file
View File

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

27
.eslintrc.yml Normal file
View File

@@ -0,0 +1,27 @@
# 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)

35
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,35 @@
<!--
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
### 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. -->
### Description
<!-- Describe your changes in detail -->
### Testing
<!-- Please describe in detail how you tested your changes. -->
### Checklist
- [ ] 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

53
.gitignore vendored
View File

@@ -1,11 +1,21 @@
.DS_Store
.gradle
.metadata
Thumbs.db
Desktop.ini
*.tmp
*.bak
*.swp
*.class
*.jar
default.properties
gen
assets/www/cordova.js
local.properties
proguard.cfg
proguard.cfg
proguard-project.txt
example
/coverage
/framework/lib
/framework/build
/framework/bin
@@ -15,31 +25,32 @@ proguard-project.txt
/framework/libs
/framework/javadoc-public
/framework/javadoc-private
/test/libs
example
/test/bin
/test/.externalNativeBuild
/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/gradle
/test/gradlew
/test/gradlew.bat
/test/bin
/test/build
.gradle
/test/captures
/test/libs
tmp/**
.metadata
tmp/**/*
Thumbs.db
Desktop.ini
*.tmp
*.bak
*.swp
*.class
*.jar
!/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar
# IntelliJ IDEA files
**/.idea/**/*
*.iml
.idea
npm-debug.log
/framework/build
node_modules/jshint
node_modules/promise-matchers
node_modules/jasmine-node
node_modules/
coverage/
.nyc_output/
# Eclipse Buildship files
.project
.settings
.classpath

View File

@@ -1,2 +0,0 @@
bin/node_modules/*
bin/templates/project/*

View File

@@ -1,10 +0,0 @@
{
"node": true
, "bitwise": true
, "undef": true
, "trailing": true
, "quotmark": true
, "indent": 4
, "unused": "vars"
, "latedef": "nofunc"
}

5
.npmignore Normal file
View File

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

View File

@@ -2,3 +2,7 @@
bin
gen
proguard-project.txt
spec
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,8 +0,0 @@
language: android
sudo: false
install:
- npm install
- echo y | android update sdk -u --filter android-22,android-23
script:
- npm test
- npm run test-build

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

@@ -18,20 +18,22 @@
# under the License.
#
-->
# 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).
## Requires
- Java JDK 1.6 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
@@ -56,3 +58,6 @@ These commands live in a generated Cordova Android project. Any interactions wit
1. Create a project
2. Import it via "Non-Android Studio Project"
## 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!

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
5.2.0-dev
9.0.0

View File

@@ -19,11 +19,9 @@
under the License.
*/
var android_sdk_version = require('./lib/android_sdk_version');
var android_sdk = require('./templates/cordova/lib/android_sdk');
android_sdk_version.run().done(null, function(err) {
console.log(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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0android_sdk_version"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'android_sdk_version' script in 'bin' folder, aborting...>&2

View File

@@ -19,12 +19,13 @@
under the License.
*/
var check_reqs = require('./lib/check_reqs');
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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0check_reqs"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2

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]
});
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]');
@@ -53,4 +53,9 @@ var options = {
activityName: argv['activity-name']
};
Api.createPlatform(argv.argv.remain[0], config, options).done();
require('./templates/cordova/loggingHelper').adjustLoggerLevel(argv);
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,64 +0,0 @@
#!/usr/bin/env node
/*
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 child_process = require('child_process'),
Q = require('q');
var get_highest_sdk = function(results){
var reg = /\d+/;
var apiLevels = [];
for(var i=0;i<results.length;i++){
apiLevels[i] = parseInt(results[i].match(reg)[0]);
}
apiLevels.sort(function(a,b){return b-a;});
console.log(apiLevels[0]);
};
var get_sdks = function() {
var d = Q.defer();
child_process.exec('android list targets', function(err, stdout, stderr) {
if (err) d.reject(stderr);
else d.resolve(stdout);
});
return d.promise.then(function(output) {
var reg = /android-\d+/gi;
var results = output.match(reg);
if(results.length===0){
return Q.reject(new Error('No android sdks installed.'));
}else{
get_highest_sdk(results);
}
return Q();
}, function(stderr) {
if (stderr.match(/command\snot\sfound/) || stderr.match(/'android' is not recognized/)) {
return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
} else {
return Q.reject(new Error('An error occurred while listing Android targets'));
}
});
};
module.exports.run = function() {
return Q.all([get_sdks()]);
};

View File

@@ -1,330 +0,0 @@
#!/usr/bin/env node
/*
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.
*/
/* jshint sub:true */
var shelljs = require('shelljs'),
child_process = require('child_process'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError;
var isWindows = process.platform == 'win32';
function forgivingWhichSync(cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
return '';
}
}
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;
}
// Get valid target from framework/project.properties
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);
}
return target.split('=')[1].trim();
}
if (fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
return extractFromFile(path.join(ROOT, 'framework', 'project.properties'));
}
if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
// if no target found, we're probably in a project and project.properties is in ROOT.
return extractFromFile(path.join(ROOT, 'project.properties'));
}
throw new Error('Could not find android target. File missing: ' + path.join(ROOT, 'project.properties'));
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function() {
return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.')
.then(function (output) {
// Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
});
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function() {
var sdkDir = 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.'));
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
if (!fs.existsSync(wrapperDir)) {
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Might need to update your Android SDK.\n' +
'Looked here: ' + wrapperDir));
}
return Q.when();
};
// Returns a promise.
module.exports.check_java = function() {
var javacPath = forgivingWhichSync('javac');
var hasJavaHome = !!process.env['JAVA_HOME'];
return Q().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');
}
} else {
if (javacPath) {
var msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
// OS X has a command for finding JAVA_HOME.
if (fs.existsSync('/usr/libexec/java_home')) {
return tryCommand('/usr/libexec/java_home', msg)
.then(function(stdout) {
process.env['JAVA_HOME'] = stdout.trim();
});
} 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;
} else {
throw new CordovaError(msg);
}
}
} else if (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;
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['JAVA_HOME'] = firstJdkDir;
}
}
}
}).then(function() {
var msg =
'Failed to run "java -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';
}
return tryCommand('java -version', msg)
.then(function() {
// 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) {
var match = /javac ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
return match && match[1];
});
});
};
// Returns a promise.
module.exports.check_android = function() {
return Q().then(function() {
var androidCmdPath = forgivingWhichSync('android');
var adbInPath = !!forgivingWhichSync('adb');
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
function maybeSetAndroidHome(value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
process.env['ANDROID_HOME'] = value;
}
}
if (!hasAndroidHome && !androidCmdPath) {
if (isWindows) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
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'));
// Stand-alone installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk'));
} else if (process.platform == 'darwin') {
// Android Studio 1.0 installer
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']) {
// 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'));
}
}
if (hasAndroidHome && !androidCmdPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
}
if (androidCmdPath && !hasAndroidHome) {
var parentDir = path.dirname(androidCmdPath);
var grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) == 'tools') {
process.env['ANDROID_HOME'] = path.dirname(parentDir);
hasAndroidHome = true;
} else if (fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
process.env['ANDROID_HOME'] = grandParentDir;
hasAndroidHome = true;
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting 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 path to valid SDK directory.');
}
}
if (hasAndroidHome && !adbInPath) {
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
}
if (!process.env['ANDROID_HOME']) {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting 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'] +
'\nTry update it manually to point to valid SDK directory.');
}
});
};
module.exports.getAbsoluteAndroidCmd = function () {
var cmd = forgivingWhichSync('android');
if (process.platform === 'win32') {
return '"' + cmd + '"';
}
return cmd.replace(/(\s)/g, '\\$1');
};
module.exports.check_android_target = function(valid_target) {
// valid_target can look like:
// android-19
// android-L
// Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20
if (!valid_target) valid_target = module.exports.get_target();
var msg = 'Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.';
return tryCommand('android list targets --compact', msg)
.then(function(output) {
var targets = output.split('\n');
if (targets.indexOf(valid_target) >= 0) {
return targets;
}
var androidCmd = module.exports.getAbsoluteAndroidCmd();
throw new CordovaError('Please install Android target: "' + valid_target + '".\n\n' +
'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
'You will require:\n' +
'1. "SDK Platform" for ' + valid_target + '\n' +
'2. "Android SDK Platform-tools (latest)\n' +
'3. "Android SDK Build-tools" (latest)');
});
};
// Returns a promise.
module.exports.run = function() {
return Q.all([this.check_java(), this.check_android().then(this.check_android_target)])
.then(function() {
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
});
};
/**
* Object thar represents one of requirements for current platform.
* @param {String} id The unique identifier for this requirements.
* @param {String} name The name of requirements. Human-readable field.
* @param {String} version The version of requirement installed. In some cases could be an array of strings
* (for example, check_android_target returns an array of android targets installed)
* @param {Boolean} installed Indicates whether the requirement is installed or not
*/
var Requirement = function (id, name, version, installed) {
this.id = id;
this.name = name;
this.installed = installed || false;
this.metadata = {
version: version,
};
};
/**
* Methods that runs all checks one by one and returns a result of checks
* as an array of Requirement objects. This method intended to be used by cordova-lib check_reqs method
*
* @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'),
new Requirement('androidTarget', 'Android target'),
new Requirement('gradle', 'Gradle')
];
var checkFns = [
this.check_java,
this.check_android,
this.check_android_target,
this.check_gradle
];
// Then execute requirement checks one-by-one
return checkFns.reduce(function (promise, checkFn, idx) {
// Update each requirement with results
var requirement = requirements[idx];
return promise.then(checkFn)
.then(function (version) {
requirement.installed = true;
requirement.metadata.version = version;
}, function (err) {
requirement.metadata.reason = err instanceof Error ? err.message : err;
});
}, Q())
.then(function () {
// When chain is completed, return requirements array to upstream API
return requirements;
});
};

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,78 +17,65 @@
under the License.
*/
var shell = require('shelljs'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
check_reqs = require('./check_reqs'),
ROOT = path.join(__dirname, '..', '..');
var MIN_SDK_VERSION = 14;
var path = require('path');
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, '..', '..');
var CordovaError = require('cordova-common').CordovaError;
var AndroidManifest = require('../templates/cordova/lib/AndroidManifest');
function setShellFatal(value, func) {
var oldVal = shell.config.fatal;
shell.config.fatal = value;
func();
shell.config.fatal = oldVal;
}
// Export all helper functions, and make sure internally within this module, we
// reference these methods via the `exports` object - this helps with testing
// (since we can then mock and control behaviour of all of these functions)
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;
exports.copyJsAndLibrary = copyJsAndLibrary;
exports.copyScripts = copyScripts;
exports.copyBuildRules = copyBuildRules;
exports.writeProjectProperties = writeProjectProperties;
exports.prepBuildFiles = prepBuildFiles;
function getFrameworkDir(projectPath, shared) {
function getFrameworkDir (projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
}
function copyJsAndLibrary(projectPath, shared, projectName) {
function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js');
shell.cp('-f', srcCordovaJsPath, path.join(projectPath, '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;
}
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.
setShellFatal(false, function() {
shell.ls(path.join(projectPath, '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'));
}
}
function extractSubProjectPaths(data) {
function extractSubProjectPaths (data) {
var ret = {};
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
var m;
@@ -100,7 +85,7 @@ function extractSubProjectPaths(data) {
return Object.keys(ret);
}
function writeProjectProperties(projectPath, target_api) {
function writeProjectProperties (projectPath, target_api) {
var dstPath = path.join(projectPath, 'project.properties');
var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties');
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
@@ -108,11 +93,10 @@ function writeProjectProperties(projectPath, target_api) {
var data = fs.readFileSync(srcPath, 'utf8');
data = data.replace(/^target=.*/m, 'target=' + target_api);
var subProjects = extractSubProjectPaths(data);
subProjects = subProjects.filter(function(p) {
subProjects = subProjects.filter(function (p) {
return !(/^CordovaLib$/m.exec(p) ||
/[\\\/]cordova-android[\\\/]framework$/m.exec(p) ||
/^(\.\.[\\\/])+framework$/m.exec(p)
);
/[\\/]cordova-android[\\/]framework$/m.exec(p) ||
/^(\.\.[\\/])+framework$/m.exec(p));
});
subProjects.unshift('CordovaLib');
data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
@@ -120,34 +104,59 @@ function writeProjectProperties(projectPath, target_api) {
data += '\n';
}
for (var i = 0; i < subProjects.length; ++i) {
data += 'android.library.reference.' + (i+1) + '=' + subProjects[i] + '\n';
data += 'android.library.reference.' + (i + 1) + '=' + subProjects[i] + '\n';
}
fs.writeFileSync(dstPath, data);
}
function prepBuildFiles(projectPath) {
var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders'));
buildModule.getBuilder('gradle').prepBuildFiles();
// This makes no sense, what if you're building with a different build system?
function prepBuildFiles (projectPath) {
var buildModule = require('../templates/cordova/lib/builders/builders');
buildModule.getBuilder(projectPath).prepBuildFiles();
}
function copyBuildRules(projectPath) {
function copyBuildRules (projectPath, isLegacy) {
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
if (isLegacy) {
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
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 {
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'));
}
}
function copyScripts(projectPath) {
var srcScriptsDir = path.join(ROOT, 'bin', 'templates', 'cordova');
function copyScripts (projectPath) {
var bin = path.join(ROOT, 'bin');
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(ROOT, 'bin', 'check_reqs*'), destScriptsDir);
shell.cp(path.join(ROOT, 'bin', 'lib', 'check_reqs.js'), path.join(projectPath, 'cordova', 'lib', 'check_reqs.js'));
shell.cp(path.join(ROOT, 'bin', 'android_sdk_version'), path.join(destScriptsDir, 'android_sdk_version'));
shell.cp(path.join(ROOT, 'bin', 'lib', 'android_sdk_version.js'), path.join(projectPath, 'cordova', 'lib', 'android_sdk_version.js'));
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 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.
const templatesCordovaRegex = /templates\/cordova\//;
utils.replaceFileContents(android_sdk_version, templatesCordovaRegex, '');
utils.replaceFileContents(check_reqs, templatesCordovaRegex, '');
}
/**
@@ -155,46 +164,37 @@ function copyScripts(projectPath) {
* Returns a promise, fulfilled if the package name is acceptable; rejected
* otherwise.
*/
function validatePackageName(package_name) {
//Make the package conform to Java package types
//http://developer.android.com/guide/topics/manifest/manifest-element.html#package
//Enforce underscore limitation
function validatePackageName (package_name) {
// Make the package conform to Java package types
// http://developer.android.com/guide/topics/manifest/manifest-element.html#package
// Enforce underscore limitation
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'));
// Class is a reserved word
if (/\b[Cc]lass\b/.test(package_name)) {
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.
*/
function validateProjectName(project_name) {
function validateProjectName (project_name) {
var msg = 'Error validating project name. ';
//Make sure there's something there
// 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();
}
/**
@@ -215,83 +215,92 @@ function validateProjectName(project_name) {
*
* @return {Promise<String>} Directory where application has been created
*/
exports.create = function(project_path, config, options, events) {
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'));
if (fs.existsSync(project_path)) {
return Promise.reject(new CordovaError('Project already exists! Delete and recreate'));
}
var package_name = config.packageName() || 'my.cordova.project';
var project_name = config.name() ?
config.name().replace(/[^\w.]/g,'_') : 'CordovaExample';
var package_name = config.android_packageName() || config.packageName() || 'my.cordova.project';
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();
var target_api = check_reqs.get_target();
//Make the package conform to Java package types
return validatePackageName(package_name)
.then(function() {
validateProjectName(project_name);
}).then(function() {
// Make the package conform to Java package types
return exports.validatePackageName(package_name)
.then(function () {
return exports.validateProjectName(project_name);
}).then(function () {
// Log the given values for the project
events.emit('log', 'Creating Cordova project for the Android platform:');
events.emit('log', '\tPath: ' + project_path);
events.emit('log', '\tPackage: ' + package_name);
events.emit('log', '\tName: ' + project_name);
events.emit('log', '\tActivity: ' + safe_activity_name);
events.emit('log', '\tAndroid target: ' + target_api);
events.emit('log', 'Creating Cordova project for the Android platform:');
events.emit('log', '\tPath: ' + project_path);
events.emit('log', '\tPackage: ' + package_name);
events.emit('log', '\tName: ' + project_name);
events.emit('log', '\tActivity: ' + safe_activity_name);
events.emit('log', '\tAndroid target: ' + target_api);
events.emit('verbose', 'Copying template files...');
events.emit('verbose', 'Copying android template project to ' + project_path);
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');
// copy project template
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
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(project_path, 'libs'));
fs.ensureDirSync(path.join(app_path, 'libs'));
// copy cordova.js, cordova.jar
copyJsAndLibrary(project_path, options.link, safe_activity_name);
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');
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(project_path, 'src', packagePath);
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(project_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_path = path.join(project_path, 'AndroidManifest.xml');
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
copyScripts(project_path);
copyBuildRules(project_path);
});
// Link it to local android install.
writeProjectProperties(project_path, target_api);
prepBuildFiles(project_path);
events.emit('log', generateDoneMessage('create', options.link));
}).thenResolve(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);
events.emit('log', generateDoneMessage('create', options.link));
}).then(() => project_path);
};
function generateDoneMessage(type, link) {
function generateDoneMessage (type, link) {
var pkg = require('../../package');
var msg = 'Android project ' + (type == 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
var msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
if (link) {
msg += ' and has a linked CordovaLib';
}
@@ -299,34 +308,16 @@ function generateDoneMessage(type, link) {
}
// Returns a promise.
exports.update = function(projectPath, options, events) {
options = options || {};
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' +
'To update your platform, you have to remove, then add your android platform again.\n' +
'Make sure you save your plugins beforehand using `cordova plugin save`, and save \n' + 'a copy of the platform first if you had manual changes in it.\n' +
'\tcordova plugin save\n' +
'\tcordova platform rm android\n' +
'\tcordova platform add android\n'
;
return Q()
.then(function() {
var manifest = new AndroidManifest(path.join(projectPath, 'AndroidManifest.xml'));
if (Number(manifest.getMinSdkVersion()) < MIN_SDK_VERSION) {
events.emit('verbose', 'Updating minSdkVersion to ' + MIN_SDK_VERSION + ' in AndroidManifest.xml');
manifest.setMinSdkVersion(MIN_SDK_VERSION);
}
manifest.setDebuggable(false).write();
var projectName = manifest.getActivity().getName();
var target_api = check_reqs.get_target();
copyJsAndLibrary(projectPath, options.link, projectName);
copyScripts(projectPath);
copyBuildRules(projectPath);
writeProjectProperties(projectPath, target_api);
prepBuildFiles(projectPath);
events.emit('log', generateDoneMessage('update', options.link));
}).thenResolve(projectPath);
return Promise.reject(errorString);
};
// For testing
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;

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

@@ -1,10 +0,0 @@
{
"node": true
, "bitwise": true
, "undef": true
, "trailing": true
, "quotmark": true
, "indent": 4
, "unused": "vars"
, "latedef": "nofunc"
}

View File

@@ -17,23 +17,40 @@
under the License.
*/
var Q = require('q');
var fs = require('fs');
/**
* @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 shell = require('shelljs');
var CordovaError = require('cordova-common').CordovaError;
var PlatformJson = require('cordova-common').PlatformJson;
var ActionStack = require('cordova-common').ActionStack;
var AndroidProject = require('./lib/AndroidProject');
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
var PluginManager = require('cordova-common').PluginManager;
var ConsoleLogger = require('./lib/ConsoleLogger');
var pluginHandlers = require('./lib/pluginHandlers');
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';
function setupEvents (externalEventEmitter) {
if (externalEventEmitter) {
// This will make the platform internal events visible outside
selfEvents.forwardEventsTo(externalEventEmitter);
return externalEventEmitter;
}
// There is no logger if external emitter is not present,
// so attach a console logger
CordovaLogger.get().subscribe(selfEvents);
return selfEvents;
}
/**
* Class, that acts as abstraction over particular platform. Encapsulates the
* platform's properties and methods.
@@ -44,461 +61,324 @@ var PLATFORM = 'android';
* 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.events = events || ConsoleLogger.get();
// NOTE: trick to share one EventEmitter instance across all js code
require('cordova-common').events = this.events;
class Api {
constructor (platform, platformRootDir, events) {
this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..');
this._platformJson = PlatformJson.load(this.root, platform);
this._pluginInfoProvider = new PluginInfoProvider();
this._munger = new PlatformMunger(this.platform, this.root, this._platformJson, this._pluginInfoProvider);
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'),
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'),
// NOTE: Due to platformApi spec we need to return relative paths here
cordovaJs: 'bin/templates/project/assets/www/cordova.js',
cordovaJsSrc: 'cordova-js-src'
};
}
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')
};
}
/**
* 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) {
return require('../../lib/create')
.create(destination, config, options, events || ConsoleLogger.get())
.then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi(PLATFORM, destination, events);
});
};
/**
* 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;
/**
* 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) {
return require('../../lib/create')
.update(destination, options, events || ConsoleLogger.get())
.then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('android', destination, events);
});
};
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;
/**
* 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);
return result;
};
return prepare.call(this, cordovaProject, prepareOptions);
}
/**
* 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) {
return require('./lib/prepare').prepare.call(this, cordovaProject);
};
/**
* 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) {
if (!plugin || plugin.constructor.name !== 'PluginInfo')
return Q.reject(new CordovaError('The parameter is incorrect. The first parameter to addPlugin should be a PluginInfo instance'));
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
var self = this;
var actions = new ActionStack();
var project = AndroidProject.getProjectFile(this.root);
// gather all files needs to be handled during install
plugin.getFilesAndFrameworks(this.platform)
.concat(plugin.getAssets(this.platform))
.concat(plugin.getJsModules(this.platform))
.forEach(function(item) {
actions.push(actions.createAction(
pluginHandlers.getInstaller(item.itemType), [item, plugin, project, installOptions],
pluginHandlers.getUninstaller(item.itemType), [item, plugin, project, installOptions]));
});
// run through the action stack
return actions.process(this.platform)
.then(function () {
if (project) {
project.write();
}
/**
* 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;
installOptions = installOptions || {};
installOptions.variables = installOptions.variables || {};
// Add PACKAGE_NAME variable into vars
if (!installOptions.variables.PACKAGE_NAME) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
self._munger
// Ignore passed `is_top_level` option since platform itself doesn't know
// anything about managing dependencies - it's responsibility of caller.
.add_plugin_changes(plugin, installOptions.variables, /*is_top_level=*/true, /*should_increment=*/true)
.save_all();
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>');
// 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
.then(() => true);
}
if (plugin.getFrameworks(self.platform).length > 0) {
self.events.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
/**
* 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;
}
var targetDir = installOptions.usePlatformWww ?
self.locations.platformWww :
self.locations.www;
return PluginManager.get(this.platform, this.locations, project)
.removePlugin(plugin, uninstallOptions)
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
self._addModulesInfo(plugin, targetDir);
});
};
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);
}
/**
* 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) {
/**
* 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;
if (!plugin || plugin.constructor.name !== 'PluginInfo')
return Q.reject(new CordovaError('The parameter is incorrect. The first parameter to addPlugin should be a PluginInfo instance'));
var self = this;
var actions = new ActionStack();
var project = AndroidProject.getProjectFile(this.root);
// queue up plugin files
plugin.getFilesAndFrameworks(this.platform)
.concat(plugin.getAssets(this.platform))
.concat(plugin.getJsModules(this.platform))
.forEach(function(item) {
actions.push(actions.createAction(
pluginHandlers.getUninstaller(item.itemType), [item, plugin, project, uninstallOptions],
pluginHandlers.getInstaller(item.itemType), [item, plugin, project, uninstallOptions]));
});
// run through the action stack
return actions.process(this.platform)
.then(function() {
if (project) {
project.write();
}
self._munger
// Ignore passed `is_top_level` option since platform itself doesn't know
// anything about managing dependencies - it's responsibility of caller.
.remove_plugin_changes(plugin, /*is_top_level=*/true)
.save_all();
if (plugin.getFrameworks(self.platform).length > 0) {
self.events.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
}
var targetDir = uninstallOptions.usePlatformWww ?
self.locations.platformWww :
self.locations.www;
self._removeModulesInfo(plugin, targetDir);
});
};
/**
* 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;
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'
};
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.
*
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError.
*/
Api.prototype.clean = function(cleanOptions) {
var self = this;
return require('./lib/check_reqs').run()
.then(function () {
return require('./lib/build').runClean.call(self, cleanOptions);
});
};
/**
* 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 = {};
}
/**
* 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();
};
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();
}
/**
* 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;
/**
* Removes the specified modules from list of installed modules and updates
* platform_json and cordova_plugins.js on disk.
*
* @param {PluginInfo} plugin PluginInfo instance for plugin, which modules
* needs to be added.
* @param {String} targetDir The directory, where updated cordova_plugins.js
* should be written to.
*/
Api.prototype._addModulesInfo = function(plugin, targetDir) {
var installedModules = this._platformJson.root.modules || [];
var installedPaths = installedModules.map(function (installedModule) {
return installedModule.file;
});
var modulesToInstall = plugin.getJsModules(this.platform)
.filter(function (moduleToInstall) {
return installedPaths.indexOf(moduleToInstall.file) === -1;
}).map(function (moduleToInstall) {
var moduleName = plugin.id + '.' + ( moduleToInstall.name || moduleToInstall.src.match(/([^\/]+)\.js/)[1] );
var obj = {
file: ['plugins', plugin.id, moduleToInstall.src].join('/'),
id: moduleName
};
if (moduleToInstall.clobbers.length > 0) {
obj.clobbers = moduleToInstall.clobbers.map(function(o) { return o.target; });
}
if (moduleToInstall.merges.length > 0) {
obj.merges = moduleToInstall.merges.map(function(o) { return o.target; });
}
if (moduleToInstall.runs) {
obj.runs = true;
}
return obj;
});
this._platformJson.root.modules = installedModules.concat(modulesToInstall);
if (!this._platformJson.root.plugin_metadata) {
this._platformJson.root.plugin_metadata = {};
}
this._platformJson.root.plugin_metadata[plugin.id] = plugin.version;
this._writePluginModules(targetDir);
this._platformJson.save();
};
/**
* Removes the specified modules from list of installed modules and updates
* platform_json and cordova_plugins.js on disk.
*
* @param {PluginInfo} plugin PluginInfo instance for plugin, which modules
* needs to be removed.
* @param {String} targetDir The directory, where updated cordova_plugins.js
* should be written to.
*/
Api.prototype._removeModulesInfo = function(plugin, targetDir) {
var installedModules = this._platformJson.root.modules || [];
var modulesToRemove = plugin.getJsModules(this.platform)
.map(function (jsModule) {
return ['plugins', plugin.id, jsModule.src].join('/');
});
var updatedModules = installedModules
.filter(function (installedModule) {
return (modulesToRemove.indexOf(installedModule.file) === -1);
});
this._platformJson.root.modules = updatedModules;
if (this._platformJson.root.plugin_metadata) {
delete this._platformJson.root.plugin_metadata[plugin.id];
}
this._writePluginModules(targetDir);
this._platformJson.save();
};
/**
* Fetches all installed modules, generates cordova_plugins contents and writes
* it to file.
*
* @param {String} targetDir Directory, where write cordova_plugins.js to.
* Ususally it is either <platform>/www or <platform>/platform_www
* directories.
*/
Api.prototype._writePluginModules = function (targetDir) {
// Write out moduleObjects as JSON wrapped in a cordova module to cordova_plugins.js
var final_contents = 'cordova.define(\'cordova/plugin_list\', function(require, exports, module) {\n';
final_contents += 'module.exports = ' + JSON.stringify(this._platformJson.root.modules, null, ' ') + ';\n';
final_contents += 'module.exports.metadata = \n';
final_contents += '// TOP OF METADATA\n';
final_contents += JSON.stringify(this._platformJson.root.plugin_metadata, null, 4) + ';\n';
final_contents += '// BOTTOM OF METADATA\n';
final_contents += '});'; // Close cordova.define.
shell.mkdir('-p', targetDir);
fs.writeFileSync(path.join(targetDir, 'cordova_plugins.js'), final_contents, 'utf-8');
};

View File

@@ -19,30 +19,33 @@
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;
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,17 +20,32 @@
*/
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);
}
new Api().clean({argv: process.argv.slice(2)})
.catch(function(err) {
console.error(err.stack);
process.exit(2);
});
// Do some basic argument parsing
var opts = nopt({
verbose: Boolean,
silent: Boolean
}, { d: '--verbose' });
// Make buildOptions compatible with PlatformApi clean method spec
opts.argv = opts.argv.original;
// Skip cleaning prepared files when not invoking via cordova CLI.
opts.noPrepare = true;
require('./loggingHelper').adjustLoggerLevel(opts);
new Api().clean(opts)
.catch(function (err) {
console.error(err.stack);
process.exit(2);
});

View File

@@ -17,19 +17,18 @@
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 = {};
function isDevice(line) {
function isDevice (line) {
return line.match(/\w+\tdevice/) && !line.match(/emulator/);
}
function isEmulator(line) {
function isEmulator (line) {
return line.match(/device/) && line.match(/emulator/);
}
@@ -44,8 +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);
@@ -56,40 +54,45 @@ Adb.devices = function (opts) {
};
Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on ' + target + '...');
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/))
return Q.reject(new CordovaError('Failed to install apk to device: ' + output));
if (output.match(/Failure/)) {
if (output.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
output += '\n\n' + 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
' or sign and deploy the unsigned apk manually using Android tools.';
} else if (output.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
output += '\n\n' + 'You\'re trying to install apk with a lower versionCode that is already installed.' +
'\nEither uninstall an app or increment the versionCode.';
}
return Promise.reject(new CordovaError('Failed to install apk to device: ' + output));
}
});
};
Adb.uninstall = function (target, packageId) {
events.emit('verbose', 'Uninstalling ' + packageId + ' from ' + target + '...');
return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()});
events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...');
return execa('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() }).then(({ stdout }) => stdout);
};
Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running command "' + shellCommand + '" on ' + target + '...');
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 + '...');
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));
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
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,144 +18,111 @@
*/
var fs = require('fs');
var et = require('elementtree');
var xml= require('cordova-common').xmlHelpers;
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(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,19 +21,19 @@ var fs = require('fs');
var path = require('path');
var properties_parser = require('properties-parser');
var AndroidManifest = require('./AndroidManifest');
var pluginHandlers = require('./pluginHandlers');
var projectFileCache = {};
function addToPropertyList(projectProperties, key, value) {
function addToPropertyList (projectProperties, key, value) {
var i = 1;
while (projectProperties.get(key + '.' + i))
i++;
while (projectProperties.get(key + '.' + i)) { i++; }
projectProperties.set(key + '.' + i, value);
projectProperties.dirty = true;
}
function removeFromPropertyList(projectProperties, key, value) {
function removeFromPropertyList (projectProperties, key, value) {
var i = 1;
var currentValue;
while ((currentValue = projectProperties.get(key + '.' + i))) {
@@ -52,133 +52,151 @@ function removeFromPropertyList(projectProperties, key, value) {
function getRelativeLibraryPath (parentDir, subDir) {
var libraryPath = path.relative(parentDir, subDir);
return (path.sep == '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
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');
}
AndroidProject.getProjectFile = function (projectDir) {
if (!projectFileCache[projectDir]) {
projectFileCache[projectDir] = new AndroidProject(projectDir);
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');
}
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
*/
getPackageName () {
var manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
return new AndroidManifest(manifestPath).getPackageId();
}
};
/**
* 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() {
return new AndroidManifest(path.join(this.projectDir, 'AndroidManifest.xml')).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;
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;
}
addToPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true;
};
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));
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 = true;
}
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;
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;
}
}
}
};
AndroidProject.prototype._getPropertiesFile = function (filename) {
if (!this._propertiesEditors[filename]) {
if (fs.existsSync(filename)) {
this._propertiesEditors[filename] = properties_parser.createEditor(filename);
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 {
this._propertiesEditors[filename] = properties_parser.createEditor();
projectFileCache = {};
}
}
return this._propertiesEditors[filename];
};
}
module.exports = AndroidProject;

View File

@@ -1,75 +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 loggerInstance;
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var CordovaError = require('cordova-common').CordovaError;
/**
* @class ConsoleLogger
* @extends EventEmitter
*
* Implementing basic logging for platform. Inherits regular NodeJS
* EventEmitter. All events, emitted on this class instance are immediately
* logged to console.
*
* Also attaches handler to process' uncaught exceptions, so these exceptions
* logged to console similar to regular error events.
*/
function ConsoleLogger() {
EventEmitter.call(this);
var isVerbose = process.argv.indexOf('-d') >= 0 || process.argv.indexOf('--verbose') >= 0;
// For CordovaError print only the message without stack trace unless we
// are in a verbose mode.
process.on('uncaughtException', function(err){
if ((err instanceof CordovaError) && isVerbose) {
console.error(err.stack);
} else {
console.error(err.message);
}
process.exit(1);
});
this.on('results', console.log);
this.on('verbose', function () {
if (isVerbose)
console.log.apply(console, arguments);
});
this.on('info', console.log);
this.on('log', console.log);
this.on('warn', console.warn);
}
util.inherits(ConsoleLogger, EventEmitter);
/**
* Returns already instantiated/newly created instance of ConsoleLogger class.
* This method should be used instead of creating ConsoleLogger directly,
* otherwise we'll get multiple handlers attached to process'
* uncaughtException
*
* @return {ConsoleLogger} New or already created instance of ConsoleLogger
*/
ConsoleLogger.get = function () {
loggerInstance = loggerInstance || new ConsoleLogger();
return loggerInstance;
};
module.exports = ConsoleLogger;

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;

101
bin/templates/cordova/lib/android_sdk.js vendored Executable file
View File

@@ -0,0 +1,101 @@
/*
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 execa = require('execa');
var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort:
// android-19
// android-L
// Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20
// The idea is to sort based on largest "suffix" number - meaning the bigger
// 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) {
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 () {
return module.exports.list_targets().then(function (targets) {
targets.sort(sort_by_largest_numerical_suffix);
console.log(targets[0]);
});
};
// 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,
'4.1': 16,
'4.2': 17,
'4.3': 18,
'4.4': 19,
'4.4W': 20,
'5.0': 21,
'5.1': 22,
'6.0': 23,
'7.0': 24,
'7.1.1': 25,
'8.0': 26
};
/* eslint-enable quote-props */
function parse_targets (output) {
var target_out = output.split('\n');
var targets = [];
for (var i = target_out.length - 1; i >= 0; i--) {
if (target_out[i].match(/id:/)) { // if "id:" is in the line...
targets.push(target_out[i].match(/"(.+)"/)[1]); // .. match whatever is in quotes.
}
}
return targets;
}
module.exports.list_targets_with_android = function () {
return execa('android', ['list', 'target']).then(result => parse_targets(result.stdout));
};
module.exports.list_targets_with_avdmanager = function () {
return execa('avdmanager', ['list', 'target']).then(result => parse_targets(result.stdout));
};
module.exports.list_targets = function () {
return module.exports.list_targets_with_avdmanager().catch(function (err) {
// If there's an error, like avdmanager could not be found, we can try
// as a last resort, to run `android`, in case this is a super old
// SDK installation.
if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) {
return module.exports.list_targets_with_android();
} else throw err;
}).then(function (targets) {
if (targets.length === 0) {
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,89 +17,82 @@
under the License.
*/
var Q = require('q'),
path = require('path'),
fs = require('fs'),
nopt = require('nopt');
var path = require('path');
var fs = require('fs');
var nopt = require('nopt');
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');
function parseOpts(options, resolvedTarget, projectRoot) {
module.exports.parseBuildOptions = parseOpts;
function parseOpts (options, resolvedTarget, projectRoot) {
options = options || {};
options.argv = nopt({
gradle: Boolean,
ant: Boolean,
prepenv: Boolean,
versionCode: String,
minSdkVersion: String,
gradleArg: 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 || 'gradle',
prepEnv: options.argv.prepenv,
arch: resolvedTarget && resolvedTarget.arch,
extraArgs: []
};
if (options.argv.ant || options.argv.gradle)
ret.buildMethod = options.argv.ant ? 'ant' : '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.gradleArg)
ret.extraArgs.push(options.argv.gradleArg);
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);
}
var packageArgs = {};
if (options.argv.keystore)
packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore));
if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); }
['alias','storePassword','password','keystoreType'].forEach(function (flagName) {
if (options.argv[flagName])
packageArgs[flagName] = options.argv[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);
}
events.emit('log', 'Reading build config file: '+ path.resolve(buildConfig));
events.emit('log', 'Reading build config file: ' + path.resolve(buildConfig));
var buildjson = fs.readFileSync(buildConfig, 'utf8');
//var config = JSON.parse(fs.readFileSync(buildConfig, 'utf8'));
var config = JSON.parse(buildjson);
var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
if (config.android && config.android[ret.buildType]) {
var androidInfo = config.android[ret.buildType];
if(androidInfo.keystore && !packageArgs.keystore) {
if(androidInfo.keystore.substr(0,1) === '~') {
if (androidInfo.keystore && !packageArgs.keystore) {
if (androidInfo.keystore.substr(0, 1) === '~') {
androidInfo.keystore = process.env.HOME + androidInfo.keystore.substr(1);
}
packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore);
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];
});
}
@@ -112,12 +103,39 @@ function parseOpts(options, resolvedTarget, projectRoot) {
packageArgs.password, packageArgs.keystoreType);
}
if(!ret.packageInfo) {
if(Object.keys(packageArgs).length > 0) {
if (!ret.packageInfo) {
// 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;
}
@@ -125,11 +143,11 @@ function parseOpts(options, resolvedTarget, projectRoot) {
* Builds the project with the specifed options
* Returns a promise.
*/
module.exports.runClean = function(options) {
module.exports.runClean = function (options) {
var opts = parseOpts(options, null, this.root);
var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts)
.then(function() {
var builder = builders.getBuilder();
return builder.prepEnv(opts).then(function () {
return builder.clean(opts);
});
};
@@ -146,24 +164,28 @@ module.exports.runClean = function(options) {
* @return {Promise<Object>} Promise, resolved with built packages
* information.
*/
module.exports.run = function(options, optResolvedTarget) {
module.exports.run = function (options, optResolvedTarget) {
var opts = parseOpts(options, optResolvedTarget, this.root);
var builder = builders.getBuilder(opts.buildMethod);
var self = this;
return builder.prepEnv(opts)
.then(function() {
var builder = builders.getBuilder();
return builder.prepEnv(opts).then(function () {
if (opts.prepEnv) {
self.events.emit('verbose', 'Build file successfully prepared.');
events.emit('verbose', 'Build file successfully prepared.');
return;
}
return builder.build(opts)
.then(function() {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
self.events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
return builder.build(opts).then(function () {
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
};
});
});
@@ -173,50 +195,53 @@ module.exports.run = function(options, optResolvedTarget) {
* Detects the architecture of a device/emulator
* Returns "arm" or "x86".
*/
module.exports.detectArchitecture = function(target) {
function helper() {
return Adb.shell(target, 'cat /proc/cpuinfo')
.then(function(output) {
module.exports.detectArchitecture = function (target) {
function helper () {
return Adb.shell(target, 'cat /proc/cpuinfo').then(function (output) {
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.
return spawn('killall', ['adb'])
.then(function() {
events.emit('verbose', 'adb seems hung. retrying.');
return helper()
.then(null, function() {
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
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', 'Now device not found... restarting adb again.');
return spawn('killall', ['adb'])
.then(function() {
return helper()
.then(null, function() {
return Q.reject(new CordovaError('USB is flakey. Try unplugging & replugging the device.'));
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return execa('killall', ['adb']).then(function () {
return helper().then(null, function () {
return Promise.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
});
});
});
}, function() {
}, function () {
// For non-killall OS's.
return Q.reject(err);
return Promise.reject(err);
});
}
throw err;
});
};
module.exports.findBestApkForArchitecture = function(buildResults, arch) {
var paths = buildResults.apkPaths.filter(function(p) {
module.exports.findBestApkForArchitecture = function (buildResults, arch) {
var paths = buildResults.apkPaths.filter(function (p) {
var apkName = path.basename(p);
if (buildResults.buildType == 'debug') {
if (buildResults.buildType === 'debug') {
return /-debug/.exec(apkName);
}
return !/-debug/.exec(apkName);
@@ -236,61 +261,40 @@ module.exports.findBestApkForArchitecture = function(buildResults, arch) {
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
};
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
};
}
function PackageInfo (keystore, alias, storePassword, password, 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();
}
};
module.exports.help = function() {
module.exports.help = function () {
console.log('Usage: ' + path.relative(process.cwd(), path.join('../build')) + ' [flags] [Signed APK flags]');
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,141 +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 path = require('path');
var util = require('util');
var shell = require('shelljs');
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
var SIGNING_PROPERTIES = '-signing.properties';
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
var TEMPLATE =
'# This file is automatically generated.\n' +
'# Do not modify this file -- ' + MARKER + '\n';
var GenericBuilder = require('./GenericBuilder');
function AntBuilder (projectRoot) {
GenericBuilder.call(this, projectRoot);
this.binDirs = {ant: this.binDirs.ant};
}
util.inherits(AntBuilder, GenericBuilder);
AntBuilder.prototype.getArgs = function(cmd, opts) {
var args = [cmd, '-f', path.join(this.root, 'build.xml')];
// custom_rules.xml is required for incremental builds.
if (hasCustomRules(this.root)) {
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
}
if(opts.packageInfo) {
args.push('-propertyfile=' + path.join(this.root, opts.buildType + SIGNING_PROPERTIES));
}
return args;
};
AntBuilder.prototype.prepEnv = function(opts) {
var self = this;
return check_reqs.check_ant()
.then(function() {
// Copy in build.xml on each build so that:
// A) we don't require the Android SDK at project creation time, and
// B) we always use the SDK's latest version of it.
/*jshint -W069 */
var sdkDir = process.env['ANDROID_HOME'];
/*jshint +W069 */
var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
function writeBuildXml(projectPath) {
var newData = buildTemplate.replace('PROJECT_NAME', self.extractRealProjectNameFromManifest());
fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
fs.writeFileSync(path.join(projectPath, 'local.properties'), TEMPLATE);
}
}
writeBuildXml(self.root);
var propertiesObj = self.readProjectProperties();
var subProjects = propertiesObj.libs;
for (var i = 0; i < subProjects.length; ++i) {
writeBuildXml(path.join(self.root, subProjects[i]));
}
if (propertiesObj.systemLibs.length > 0) {
throw new CordovaError('Project contains at least one plugin that requires a system library. This is not supported with ANT. Please build using gradle.');
}
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 ant.
* Returns a promise.
*/
AntBuilder.prototype.build = function(opts) {
// Without our custom_rules.xml, we need to clean before building.
var ret = Q();
if (!hasCustomRules(this.root)) {
// clean will call check_ant() for us.
ret = this.clean(opts);
}
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
return check_reqs.check_ant()
.then(function() {
return spawn('ant', args, {stdio: 'inherit'});
});
};
AntBuilder.prototype.clean = function(opts) {
var args = this.getArgs('clean', opts);
var self = this;
return check_reqs.check_ant()
.then(function() {
return spawn('ant', args, {stdio: 'inherit'});
})
.then(function () {
shell.rm('-rf', path.join(self.root, 'out'));
['debug', 'release'].forEach(function(config) {
var propertiesFilePath = path.join(self.root, config + SIGNING_PROPERTIES);
if(isAutoGenerated(propertiesFilePath)){
shell.rm('-f', propertiesFilePath);
}
});
});
};
module.exports = AntBuilder;
function hasCustomRules(projectRoot) {
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
}
function isAutoGenerated(file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}

View File

@@ -1,138 +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 path = require('path');
var shell = require('shelljs');
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
function GenericBuilder (projectDir) {
this.root = projectDir || path.resolve(__dirname, '../../..');
this.binDirs = {
ant: path.join(this.root, hasCustomRules(this.root) ? 'ant-build' : 'bin'),
gradle: path.join(this.root, 'build', 'outputs', 'apk')
};
}
function hasCustomRules(projectRoot) {
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
}
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);
};
GenericBuilder.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)
};
};
GenericBuilder.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);
};
module.exports = GenericBuilder;
function apkSorter(fileA, fileB) {
var timeDiff = fs.statSync(fileA).mtime - fs.statSync(fileB).mtime;
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
}
function findOutputApksHelper(dir, build_type, arch) {
var shellSilent = shell.config.silent;
shell.config.silent = true;
var ret = shell.ls(path.join(dir, '*.apk'))
.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) {
ret = ret.filter(function(p) {
return path.basename(p).indexOf('-' + arch) != -1;
});
}
return ret;
}

View File

@@ -1,275 +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 child_process = require('child_process');
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 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');
// 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;
};
// 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;
for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') {
shell.cp('-f', pluginBuildGradle, path.join(this.root, subProjects[i], 'build.gradle'));
}
}
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 = '';
subProjects.forEach(function(p) {
var libName=p.replace(/[/\\]/g, ':').replace(name+'-','');
depsList += ' debugCompile project(path: "' + libName + '", configuration: "debug")\n';
depsList += ' releaseCompile project(path: "' + libName + '", configuration: "release")\n';
});
// 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');
fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
};
GradleBuilder.prototype.prepEnv = function(opts) {
var self = this;
return check_reqs.check_gradle()
.then(function() {
return self.prepBuildFiles();
}).then(function() {
// Copy the gradle wrapper on each build so that:
// A) we don't require the Android SDK at project creation time, and
// B) we always use the SDK's latest version of it.
// check_reqs ensures that this is set.
/*jshint -W069 */
var sdkDir = process.env['ANDROID_HOME'];
/*jshint +W069 */
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
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'] || 'http\\://services.gradle.org/distributions/gradle-2.2.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 spawnAndSuppressJavaOptions(wrapper, args);
};
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 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 isAutoGenerated(file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}
/**
* A special superspawn-like implementation, required to workaround 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.
*
* This function needed because superspawn does not provide a way to get and
* manage spawned process output streams. There is a CB-10052 which describes
* an improvements for superspawn, needed to get rid of this.
* TODO: Once this improvement added to cordova-common, we could remove this functionality.
*
* @param {String} cmd A command to spawn
* @param {String[]} args Command arguments. Note that on Windows arguments
* will be concatenated into string and passed to 'cmd.exe' along with '/s'
* and '/c' switches for proper space-in-path handling
*
* @return {Promise} A promise, rejected with error message if
* underlying command exits with nonzero exit code, fulfilled otherwise
*/
function spawnAndSuppressJavaOptions(cmd, args) {
var opts = { stdio: 'pipe' };
if (process.platform === 'win32') {
// Work around spawn not being able to find .bat files.
var joinedArgs = [cmd]
.concat(args)
.map(function(a){
// Add quotes to arguments which contains whitespaces
if (/^[^"].* .*[^"]/.test(a)) return '"' + a + '"';
return a;
}).join(' ');
args = ['/s', '/c'].concat('"' + joinedArgs + '"');
cmd = 'cmd';
opts.windowsVerbatimArguments = true;
}
return Q.Promise(function (resolve, reject) {
var proc = child_process.spawn(cmd, args, opts);
proc.stdout.on('data', process.stdout.write.bind(process.stdout));
proc.stderr.on('data', function (data) {
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(data.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(data);
});
proc.on('exit', function(code) {
if (code) {
reject('Error code ' + code + ' for command: ' + cmd + ' with args: ' + args);
} else {
resolve();
}
});
});
}

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,47 +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 = {
ant: 'AntBuilder',
gradle: 'GradleBuilder',
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);
}
};

480
bin/templates/cordova/lib/check_reqs.js vendored Normal file
View File

@@ -0,0 +1,480 @@
/*
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 execa = require('execa');
var path = require('path');
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, '..', '..');
const { CordovaError, ConfigParser, events } = require('cordova-common');
var android_sdk = require('./android_sdk');
const { createEditor } = require('properties-parser');
function forgivingWhichSync (cmd) {
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 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 () {
return (os.platform() === 'win32');
};
module.exports.isDarwin = function () {
return (os.platform() === 'darwin');
};
/**
* @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 () {
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;
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function () {
return execa('ant', ['-version']).then(({ stdout: output }) => {
// Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
}).catch(function (err) {
if (err) {
throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
}
});
};
module.exports.get_gradle_wrapper = function () {
var androidStudioPath;
var i = 0;
var foundStudio = false;
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 = 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') + '/';
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');
} else { ++i; }
}
}
} else {
// console.log('got android studio path from registry');
// remove the (os independent) new line char at the end of stdout
// add gradle to match the above.
androidStudioPath = path.join(result.stdout.toString().split('\r\n')[0], 'gradle');
}
}
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
var dirs = fs.readdirSync(androidStudioPath);
if (dirs[0].split('-')[0] === 'gradle') {
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
}
} else {
// OK, let's try to check for Gradle!
return forgivingWhichSync('gradle');
}
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function () {
var sdkDir = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
if (!sdkDir) {
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) 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 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');
}
} else {
if (javacPath) {
// OS X has a command for finding JAVA_HOME.
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 execa(find_java).then(({ stdout }) => {
process.env.JAVA_HOME = stdout;
}).catch(function (err) {
if (err) {
throw new CordovaError(default_java_error_msg);
}
});
} else {
// See if we can derive it from javac's location.
var maybeJavaHome = path.dirname(path.dirname(javacPath));
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
process.env.JAVA_HOME = maybeJavaHome;
} else {
throw new CordovaError(default_java_error_msg);
}
}
} else if (module.exports.isWindows()) {
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) {
firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
if (!javacPath) {
process.env.PATH += path.delimiter + path.join(firstJdkDir, 'bin');
}
process.env.JAVA_HOME = firstJdkDir;
}
}
}
}).then(function () {
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 Promise.resolve().then(function () {
function maybeSetAndroidHome (value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
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
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
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
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
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) {
// 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'));
}
}
if (!hasAndroidHome) {
// 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);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
maybeSetAndroidHome(grandParentDir);
} else {
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.');
}
}
if (adbInPath) {
parentDir = path.dirname(adbInPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) === 'platform-tools') {
maybeSetAndroidHome(grandParentDir);
} else {
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.');
}
}
if (avdmanagerInPath) {
parentDir = path.dirname(avdmanagerInPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') {
maybeSetAndroidHome(path.dirname(grandParentDir));
} else {
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_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_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_SDK_ROOT, 'tools');
}
if (hasAndroidHome && !adbInPath) {
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_SDK_ROOT, 'tools', 'bin');
}
return hasAndroidHome;
});
};
// TODO: is this actually needed?
module.exports.getAbsoluteAndroidCmd = function () {
var cmd = forgivingWhichSync('android');
if (cmd.length === 0) {
cmd = forgivingWhichSync('sdkmanager');
}
if (module.exports.isWindows()) {
return '"' + cmd + '"';
}
return cmd.replace(/(\s)/g, '\\$1');
};
module.exports.check_android_target = function (originalError) {
// valid_target can look like:
// android-19
// android-L
// Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20
var desired_api_level = module.exports.get_target();
return android_sdk.list_targets().then(function (targets) {
if (targets.indexOf(desired_api_level) >= 0) {
return targets;
}
var androidCmd = module.exports.getAbsoluteAndroidCmd();
var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' +
'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
'You will require:\n' +
'1. "SDK Platform" for API level ' + desired_api_level + '\n' +
'2. "Android SDK Platform-tools (latest)\n' +
'3. "Android SDK Build-tools" (latest)';
if (originalError) {
msg = originalError + '\n' + msg;
}
throw new CordovaError(msg);
});
};
// Returns a promise.
module.exports.run = function () {
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)');
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! Android SDK was not detected.');
}
});
};
/**
* Object thar represents one of requirements for current platform.
* @param {String} id The unique identifier for this requirements.
* @param {String} name The name of requirements. Human-readable field.
* @param {String} version The version of requirement installed. In some cases could be an array of strings
* (for example, check_android_target returns an array of android targets installed)
* @param {Boolean} installed Indicates whether the requirement is installed or not
*/
var Requirement = function (id, name, version, installed) {
this.id = id;
this.name = name;
this.installed = installed || false;
this.metadata = {
version: version
};
};
/**
* Methods that runs all checks one by one and returns a result of checks
* as an array of Requirement objects. This method intended to be used by cordova-lib check_reqs method
*
* @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'),
new Requirement('androidTarget', 'Android target'),
new Requirement('gradle', 'Gradle')
];
var checkFns = [
this.check_java,
this.check_android,
this.check_android_target,
this.check_gradle
];
// Then execute requirement checks one-by-one
return checkFns.reduce(function (promise, checkFn, idx) {
// Update each requirement with results
var requirement = requirements[idx];
return promise.then(checkFn).then(function (version) {
requirement.installed = true;
requirement.metadata.version = version;
}, function (err) {
requirement.metadata.reason = err instanceof Error ? err.message : err;
});
}, 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'),
build = require('./build');
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;
@@ -32,18 +29,16 @@ var events = require('cordova-common').events;
* Returns a promise for the list of the device ID's found
* @param lookHarder When true, try restarting adb if no devices are found.
*/
module.exports.list = function(lookHarder) {
return Adb.devices()
.then(function(list) {
module.exports.list = function (lookHarder) {
return Adb.devices().then(function (list) {
if (list.length === 0 && 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() {
}, function () {
// For non-killall OS's.
return list;
});
@@ -52,21 +47,19 @@ module.exports.list = function(lookHarder) {
});
};
module.exports.resolveTarget = function(target) {
return this.list(true)
.then(function(device_list) {
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) {
return build.detectArchitecture(target).then(function (arch) {
return { target: target, arch: arch, isEmulator: false };
});
});
@@ -77,42 +70,39 @@ module.exports.resolveTarget = function(target) {
* and launches it.
* Returns a promise.
*/
module.exports.install = function(target, buildResults) {
return Q().then(function() {
if (target && typeof target == 'object') {
module.exports.install = function (target, buildResults) {
return Promise.resolve().then(function () {
if (target && typeof target === 'object') {
return target;
}
return module.exports.resolveTarget(target);
}).then(function(resolvedTarget) {
}).then(function (resolvedTarget) {
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
var manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml'));
var pkgName = manifest.getPackageId();
var launchName = pkgName + '/.' + manifest.getActivity().getName();
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;
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
'installed app already signed with different key');
// 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.uninstall(resolvedTarget.target, pkgName).then(function () {
return Adb.install(resolvedTarget.target, apk_path, { replace: true });
});
})
.then(function() {
//unlock screen
}).then(function () {
// unlock screen
return Adb.shell(resolvedTarget.target, 'input keyevent 82');
}).then(function() {
}).then(function () {
return Adb.start(resolvedTarget.target, launchName);
}).then(function() {
}).then(function () {
events.emit('log', 'LAUNCH SUCCESS');
});
});

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,64 +17,83 @@
under the License.
*/
/* jshint sub:true */
var retry = require('./retry');
var build = require('./build');
const execa = require('execa');
const fs = require('fs-extra');
var android_versions = require('android-versions');
var retry = require('./retry');
var build = require('./build');
var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
var Q = require('q');
var os = require('os');
var child_process = require('child_process');
var android_sdk = require('./android_sdk');
var check_reqs = require('./check_reqs');
var which = require('which');
var os = require('os');
// 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';
/**
* Returns a Promise for a list of emulator images in the form of objects
* {
name : <emulator_name>,
path : <path_to_emulator_image>,
target : <api_target>,
abi : <cpu>,
skin : <skin>
}
*/
module.exports.list_images = function() {
return spawn('android', ['list', 'avds'])
.then(function(output) {
function forgivingWhichSync (cmd) {
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 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', '');
}
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/)) {
if (response[i + 1].match(/Target:\s/)) {
i++;
img_obj['target'] = response[i].replace('\r', '');
}
if (response[i + 1].match(/ABI:\s/)) {
i++;
img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', '');
if (response[i + 1].match(/ABI:\s/)) {
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
// best emulator target to spawn (see below in `best_image`)
// 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();
}
}
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 + ')';
}
}
}
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);
@@ -84,33 +101,109 @@ module.exports.list_images = function() {
/* To just return a list of names use this
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 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', '');
if (response[i + 1].match(/Device:\s/)) {
i++;
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', '');
}
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', '');
}
if (response[i + 1].match(/ABI:\s/)) {
i++;
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', '');
}
emulator_list.push(img_obj);
}
/* To just return a list of names use this
if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
} */
}
return emulator_list;
});
};
/**
* Returns a Promise for a list of emulator images in the form of objects
* {
name : <emulator_name>,
device : <device>,
path : <path_to_emulator_image>,
target : <api_target>,
abi : <cpu>,
skin : <skin>
}
*/
module.exports.list_images = 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 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.
return avds.map(function (avd) {
if (avd.target && avd.target.indexOf('Android API') > -1 && avd.target.indexOf('API level') < 0) {
var api_level = avd.target.match(/\d+/);
if (api_level) {
var level = android_versions.get(api_level);
if (level) {
avd.target = 'Android ' + level.semver + ' (API level ' + api_level + ')';
}
}
}
return avd;
});
});
};
/**
* Will return the closest avd to the projects target
* or undefined if no avds exist.
* Returns a promise.
*/
module.exports.best_image = function() {
return this.list_images()
.then(function(images) {
module.exports.best_image = function () {
return this.list_images().then(function (images) {
// Just return undefined if there is no images
if (images.length === 0) return;
var closest = 9999;
var best = images[0];
// Loading check_reqs at run-time to avoid test-time vs run-time directory structure difference issue
var project_target = require('./check_reqs').get_target().replace('android-', '');
var project_target = parseInt(check_reqs.get_target().replace('android-', ''));
for (var i in images) {
var target = images[i].target;
if(target) {
var num = target.split('(API level ')[1].replace(')', '');
if (num == project_target) {
if (target && target.indexOf('API level') > -1) {
var num = parseInt(target.split('(API level ')[1].replace(')', ''));
if (num === project_target) {
return images[i];
} else if (project_target - num < closest && project_target > num) {
closest = project_target - num;
@@ -123,18 +216,18 @@ module.exports.best_image = function() {
};
// Returns a promise.
module.exports.list_started = function() {
return Adb.devices({emulators: true});
module.exports.list_started = function () {
return Adb.devices({ emulators: true });
};
// Returns a promise.
module.exports.list_targets = function() {
return spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
.then(function(output) {
// TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function () {
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--) {
if(target_out[i].match(/id:/)) {
if (target_out[i].match(/id:/)) {
targets.push(targets[i].split(' ')[1]);
}
}
@@ -142,6 +235,24 @@ module.exports.list_targets = function() {
});
};
/*
* Gets unused port for android emulator, between 5554 and 5584
* Returns a promise.
*/
module.exports.get_available_port = function () {
var self = this;
return self.list_started().then(function (emulators) {
for (var p = 5584; p >= 5554; p -= 2) {
if (emulators.indexOf('emulator-' + p) === -1) {
events.emit('verbose', 'Found available port: ' + p);
return p;
}
}
throw new CordovaError('Could not find an available avd port');
});
};
/*
* Starts an emulator with the given ID,
* and returns the started ID of that emulator.
@@ -152,52 +263,49 @@ module.exports.list_targets = function() {
*
* Returns a promise.
*/
module.exports.start = function(emulator_ID, boot_timeout) {
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) {
return self.best_image().then(function (best) {
if (best && best.name) {
events.emit('warn', 'No emulator specified, defaulting to ' + best.name);
return best.name;
}
// Loading check_reqs at run-time to avoid test-time vs run-time directory structure difference issue
var androidCmd = require('./check_reqs').getAbsoluteAndroidCmd();
return Q.reject(new CordovaError('No emulator images (avds) found.\n' +
var androidCmd = check_reqs.getAbsoluteAndroidCmd();
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'));
});
}).then(function(emulatorId) {
var uuid = 'cordova_emulator_' + new Date().getTime();
var uuidProp = 'emu.uuid=' + uuid;
var args = ['-avd', emulatorId, '-prop', uuidProp];
// 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 })
.unref();
}).then(function (emulatorId) {
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(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.
execa('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
.unref();
// wait for emulator to start
events.emit('log', 'Waiting for emulator...');
return self.wait_for_emulator(uuid);
}).then(function(emulatorId) {
if (!emulatorId)
return Q.reject(new CordovaError('Failed to start emulator'));
// wait for emulator to start
events.emit('log', 'Waiting for emulator to start...');
return self.wait_for_emulator(port);
});
}).then(function (emulatorId) {
if (!emulatorId) { return Promise.reject(new CordovaError('Failed to start emulator')); }
//wait for emulator to boot up
process.stdout.write('Booting up emulator (this may take a while)...');
return self.wait_for_boot(emulatorId, boot_timeout)
.then(function(success) {
// wait for emulator to boot up
process.stdout.write('Waiting for emulator to boot (this may take a while)...');
return self.wait_for_boot(emulatorId, boot_timeout).then(function (success) {
if (success) {
events.emit('log','BOOT COMPLETE');
//unlock screen
return Adb.shell(emulatorId, 'input keyevent 82')
.then(function() {
//return the new emulator id for the started emulators
events.emit('log', 'BOOT COMPLETE');
// unlock screen
return Adb.shell(emulatorId, 'input keyevent 82').then(function () {
// return the new emulator id for the started emulators
return emulatorId;
});
} else {
@@ -209,31 +317,32 @@ module.exports.start = function(emulator_ID, boot_timeout) {
};
/*
* Waits for an emulator with given uuid to apear on the started-emulator list.
* Returns a promise with this emulator's ID.
* Waits for an emulator to boot on a given port.
* Returns this emulator's ID in a promise.
*/
module.exports.wait_for_emulator = function(uuid) {
module.exports.wait_for_emulator = function (port) {
var self = this;
return self.list_started()
.then(function(new_started) {
var emulator_id = null;
var promises = [];
new_started.forEach(function (emulator) {
promises.push(
Adb.shell(emulator, 'getprop emu.uuid')
.then(function (output) {
if (output.indexOf(uuid) >= 0) {
emulator_id = emulator;
}
})
);
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) {
return emulator_id;
}
return self.wait_for_emulator(port);
}, function (error) {
if ((error && error.message &&
(error.message.indexOf('not found') > -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 {
// something unexpected has happened
throw error;
}
});
return Q.all(promises).then(function () {
return emulator_id || self.wait_for_emulator(uuid);
});
});
});
};
/*
@@ -241,21 +350,23 @@ module.exports.wait_for_emulator = function(uuid) {
* promise that resolves to a boolean indicating success. Not specifying a
* time_remaining or passing a negative value will cause it to wait forever
*/
module.exports.wait_for_boot = function(emulator_id, time_remaining) {
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);
});
}
});
@@ -266,46 +377,43 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
* TODO : Enter the stdin input required to complete the creation of an avd.
* Returns a promise.
*/
module.exports.create_image = function(name, target) {
console.log('Creating avd named ' + name);
module.exports.create_image = function (name, target) {
console.log('Creating new avd named ' + name);
if (target) {
return 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.');
return spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
.then(function() {
// TODO: there's a more robust method for finding targets in android_sdk.js
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('Please insure you have targets available by running the "android" command');
return Q.reject();
}, function(error) {
console.error('Ensure you have targets available by running the "android" command');
return Promise.reject(new CordovaError());
}, function (error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(error);
console.error(error.message);
});
}
};
module.exports.resolveTarget = function(target) {
return this.list_started()
.then(function(emulator_list) {
module.exports.resolveTarget = function (target) {
return this.list_started().then(function (emulator_list) {
if (emulator_list.length < 1) {
return Q.reject('No started emulators found, please start an emultor 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 build.detectArchitecture(target).then(function (arch) {
return { target: target, arch: arch, isEmulator: true };
});
});
};
@@ -316,15 +424,16 @@ module.exports.resolveTarget = function(target) {
* If no started emulators are found, error out.
* Returns a promise.
*/
module.exports.install = function(givenTarget, buildResults) {
module.exports.install = function (givenTarget, buildResults) {
var target;
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
var pkgName = manifest.getPackageId();
// We need to find the proper path to the Android Manifest
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 () {
if (givenTarget && typeof givenTarget == 'object') {
return Promise.resolve().then(function () {
if (givenTarget && typeof givenTarget === 'object') {
return givenTarget;
} else {
return module.exports.resolveTarget(givenTarget);
@@ -338,65 +447,65 @@ 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(),
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
killSignal: EXEC_KILL_SIGNAL
};
events.emit('log', 'Using apk: ' + apk_path);
events.emit('log', 'Package name: ' + pkgName);
events.emit('verbose', 'Installing app on emulator...');
// A special function to call adb install in specific environment w/ specific options.
// Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119
// to workaround sporadic emulator hangs
function adbInstallWithOptions(target, apk, opts) {
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)) reject(new CordovaError('Failed to install apk to emulator: ' + stdout));
else resolve(stdout);
});
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.';
}
throw new CordovaError('Failed to install apk to emulator: ' + stdout);
}
});
}
function installPromise () {
return adbInstallWithOptions(target.target, apk_path, execOptions)
.catch(function (error) {
return adbInstallWithOptions(target.target, apk_path, execOptions).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;
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
'installed app already signed with different key');
events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
'currently installed app was signed with different key');
// 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(target.target, pkgName)
.then(function() {
return Adb.uninstall(target.target, pkgName).then(function () {
return adbInstallWithOptions(target.target, apk_path, execOptions);
});
});
}
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise)
.then(function (output) {
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise).then(function (output) {
events.emit('log', 'INSTALL SUCCESS');
});
});
// unlock screen
}).then(function () {
events.emit('verbose', 'Unlocking screen...');
return Adb.shell(target.target, 'input keyevent 82');
}).then(function () {

View File

@@ -0,0 +1,3 @@
@ECHO OFF
for /f "tokens=2*" %%a in ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Android Studio" /v Path') do set "ASPath=%%~b"
ECHO %ASPath%

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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0install-device"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'install-device' script in 'cordova\lib' folder, aborting...>&2

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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0install-emulator"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'install-emulator' script in 'cordova\lib' folder, aborting...>&2

View File

@@ -22,12 +22,13 @@
var devices = require('./device');
// Usage support for when args are given
devices.list().done(function(device_list) {
device_list && device_list.forEach(function(dev) {
console.log(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) {
console.error('ERROR: ' + err);
process.exit(2);
});
}, 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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0list-devices"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'list-devices' script in 'cordova\lib' folder, aborting...>&2

View File

@@ -22,11 +22,13 @@
var emulators = require('./emulator');
// Usage support for when args are given
emulators.list_images().done(function(emulator_list) {
emulator_list && emulator_list.forEach(function(emu) {
console.log(emu.name);
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) {
console.error('ERROR: ' + err);
process.exit(2);
});
}, 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
@@ -18,9 +18,9 @@
@ECHO OFF
SET script_path="%~dp0list-emulator-images"
IF EXIST %script_path% (
node "%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,11 +22,13 @@
var emulators = require('./emulator');
// Usage support for when args are given
emulators.list_started().done(function(emulator_list) {
emulator_list && emulator_list.forEach(function(emu) {
console.log(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) {
console.error('ERROR: ' + err);
process.exit(2);
});
}, 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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0list-started-emulators"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'list-started-emulators' script in 'cordova\lib' folder, aborting...>&2

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,28 @@
under the License.
*/
var path = require('path'),
os = require('os'),
Q = require('q'),
child_process = require('child_process'),
ROOT = path.join(__dirname, '..', '..');
var path = require('path');
var os = require('os');
var execa = require('execa');
var ROOT = path.join(__dirname, '..', '..');
/*
* Starts running logcat in the shell.
* Returns a promise.
*/
module.exports.run = function() {
var d = Q.defer();
var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
module.exports.run = function () {
var adb = execa('adb', ['logcat'], { cwd: os.tmpdir(), stderr: 'inherit' });
adb.stdout.on('data', function(data) {
adb.stdout.on('data', function (data) {
var lines = data ? data.toString().split('\n') : [];
var out = lines.filter(function(x) { return x.indexOf('nativeGetEnabledTags') < 0; });
var out = lines.filter(function (x) { return x.indexOf('nativeGetEnabledTags') < 0; });
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() {
module.exports.help = function () {
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log')));
console.log('Gives the logcat output on the command line.');
process.exit(0);

View File

@@ -20,32 +20,23 @@
buildscript {
repositories {
mavenCentral()
google()
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
if (gradle.gradleVersion >= "2.2") {
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0+'
}
} else if (gradle.gradleVersion >= "2.1") {
dependencies {
classpath 'com.android.tools.build:gradle:0.14.0+'
}
} else {
dependencies {
classpath 'com.android.tools.build:gradle:0.12.0+'
}
dependencies {
classpath 'com.android.tools.build:gradle:1.0.0+'
}
}
apply plugin: 'android-library'
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")
}
@@ -53,7 +44,6 @@ dependencies {
android {
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
publishNonDefault true
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6

View File

@@ -1,7 +1,4 @@
/*
*
* Copyright 2013 Anis Kadri
*
* Licensed 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
@@ -17,49 +14,61 @@
*
*/
/* 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;
var handlers = {
'source-file':{
install:function(obj, plugin, project, options) {
if (!obj.src) throw new CordovaError('<source-file> element is missing "src" attribute for plugin: ' + plugin.id);
if (!obj.targetDir) throw new CordovaError('<source-file> element is missing "target-dir" attribute for plugin: ' + plugin.id);
var dest = path.join(obj.targetDir, path.basename(obj.src));
copyNewFile(plugin.dir, obj.src, project.projectDir, dest, options && options.link);
'source-file': {
install: function (obj, plugin, project, options) {
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 = getInstallDestination(obj);
if (options && options.force) {
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
} else {
copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
}
},
uninstall:function(obj, plugin, project, options) {
var dest = path.join(obj.targetDir, path.basename(obj.src));
deleteJava(project.projectDir, dest);
uninstall: function (obj, plugin, project, options) {
var dest = getInstallDestination(obj);
// 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));
}
}
},
'lib-file':{
install:function(obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src));
copyFile(plugin.dir, obj.src, project.projectDir, dest, options && options.link);
'lib-file': {
install: function (obj, plugin, project, options) {
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));
removeFile(project.projectDir, dest);
uninstall: function (obj, plugin, project, options) {
var dest = path.join('app/libs', path.basename(obj.src));
removeFile(path.resolve(project.projectDir, dest));
}
},
'resource-file':{
install:function(obj, plugin, project, options) {
copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), options && options.link);
'resource-file': {
install: function (obj, plugin, project, options) {
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) {
removeFile(project.projectDir, path.normalize(obj.target));
uninstall: function (obj, plugin, project, options) {
var dest = path.join('app', 'src', 'main', obj.target);
removeFile(path.resolve(project.projectDir, dest));
}
},
'framework': {
install:function(obj, plugin, project, options) {
framework: {
install: function (obj, plugin, project, options) {
var src = obj.src;
if (!src) throw new CordovaError('src not specified in <framework> for plugin: ' + plugin.id);
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
events.emit('verbose', 'Installing Android library: ' + src);
var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
@@ -67,24 +76,24 @@ var handlers = {
if (obj.custom) {
var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, options && options.link);
copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, !!(options && options.link));
subDir = path.resolve(project.projectDir, subRelativeDir);
} else {
obj.type = 'sys';
subDir = src;
}
if (obj.type == 'gradleReference') {
if (obj.type === 'gradleReference') {
project.addGradleReference(parentDir, subDir);
} else if (obj.type == 'sys') {
} else if (obj.type === 'sys') {
project.addSystemLibrary(parentDir, subDir);
} else {
project.addSubProject(parentDir, subDir);
}
},
uninstall:function(obj, plugin, project, options) {
uninstall: function (obj, plugin, project, options) {
var src = obj.src;
if (!src) throw new CordovaError('src not specified in <framework> for plugin: ' + plugin.id);
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
events.emit('verbose', 'Uninstalling Android library: ' + src);
var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
@@ -92,11 +101,11 @@ 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);
if (fs.readdirSync(parDir).length === 0) {
if (fs.existsSync(parDir) && fs.readdirSync(parDir).length === 0) {
fs.rmdirSync(parDir);
}
} else {
@@ -104,42 +113,49 @@ var handlers = {
subDir = src;
}
if (obj.type == 'gradleReference') {
if (obj.type === 'gradleReference') {
project.removeGradleReference(parentDir, subDir);
} else if (obj.type == 'sys') {
} else if (obj.type === 'sys') {
project.removeSystemLibrary(parentDir, subDir);
} else {
project.removeSubProject(parentDir, subDir);
}
}
},
asset:{
install:function(obj, plugin, project, options) {
asset: {
install: function (obj, plugin, project, options) {
if (!obj.src) {
throw new CordovaError('<asset> tag without required "src" attribute. plugin=' + plugin.dir);
throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
}
if (!obj.target) {
throw new CordovaError('<asset> tag without required "target" attribute');
throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
}
var www = options.usePlatformWww ? project.platformWww : project.www;
copyFile(plugin.dir, obj.src, www, obj.target);
copyFile(plugin.dir, obj.src, project.www, obj.target);
if (options && options.usePlatformWww) {
// CB-11022 copy file to both directories if usePlatformWww is specified
copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
}
},
uninstall:function(obj, plugin, project, options) {
uninstall: function (obj, plugin, project, options) {
var target = obj.target || obj.src;
if (!target) throw new CordovaError('<asset> tag without required "target" attribute');
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
var www = options.usePlatformWww ? project.platformWww : project.www;
removeFile(www, target);
removeFileF(path.resolve(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
removeFile(path.resolve(project.platformWww, target));
removeFile(path.resolve(project.platformWww, 'plugins', plugin.id));
}
}
},
'js-module': {
install: function (obj, plugin, project, options) {
// Copy the plugin's files into the www directory.
var moduleSource = path.resolve(plugin.dir, obj.src);
var moduleName = plugin.id + '.' + (obj.name || path.parse(obj.src).name);
var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src)));
// Read in the file, prepend the cordova.define, and write it back out.
var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
@@ -148,15 +164,24 @@ var handlers = {
}
scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';
var www = options.usePlatformWww ? project.platformWww : project.www;
var moduleDestination = path.resolve(www, 'plugins', plugin.id, obj.src);
shell.mkdir('-p', path.dirname(moduleDestination));
fs.writeFileSync(moduleDestination, scriptContent, 'utf-8');
var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
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);
fs.ensureDirSync(path.dirname(platformWwwDest));
fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
}
},
uninstall: function (obj, plugin, project, options) {
var pluginRelativePath = path.join('plugins', plugin.id, obj.src);
var www = options.usePlatformWww ? project.platformWww : project.www;
removeFileAndParents(www, pluginRelativePath);
removeFileAndParents(project.www, pluginRelativePath);
if (options && options.usePlatformWww) {
// CB-11022 remove file from both directories if usePlatformWww is specified
removeFileAndParents(project.platformWww, pluginRelativePath);
}
}
}
};
@@ -169,7 +194,7 @@ module.exports.getInstaller = function (type) {
events.emit('verbose', '<' + type + '> is not supported for android plugins');
};
module.exports.getUninstaller = function(type) {
module.exports.getUninstaller = function (type) {
if (handlers[type] && handlers[type].uninstall) {
return handlers[type].uninstall;
}
@@ -184,45 +209,46 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// check that src path is inside plugin directory
var real_path = fs.realpathSync(src);
var real_plugin_path = fs.realpathSync(plugin_dir);
if (real_path.indexOf(real_plugin_path) !== 0)
throw new CordovaError('"' + src + '" not located within plugin!');
if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
dest = path.resolve(project_dir, dest);
// check that dest path is located in project directory
if (dest.indexOf(project_dir) !== 0)
throw new CordovaError('"' + dest + '" not located within project!');
shell.mkdir('-p', path.dirname(dest));
if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
fs.ensureDirSync(path.dirname(dest));
if (link) {
fs.symlinkSync(path.relative(path.dirname(dest), 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);
symlinkFileOrDirTree(src, dest);
} else {
shell.cp('-f', src, dest);
fs.copySync(src, dest);
}
}
// Same as copy file but throws error if target exists
function copyNewFile (plugin_dir, src, project_dir, dest, link) {
var target_path = path.resolve(project_dir, dest);
if (fs.existsSync(target_path))
throw new CordovaError('"' + target_path + '" already exists!');
if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); }
copyFile(plugin_dir, src, project_dir, dest, !!link);
}
// 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);
function symlinkFileOrDirTree (src, dest) {
if (fs.existsSync(dest)) {
fs.removeSync(dest);
}
if (fs.statSync(src).isDirectory()) {
fs.ensureDirSync(path.dirname(dest));
fs.readdirSync(src).forEach(function (entry) {
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
});
} else {
fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
}
}
// 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
@@ -235,13 +261,13 @@ 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);
while(curDir !== path.resolve(baseDir, stopper)) {
if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
while (curDir !== path.resolve(baseDir, stopper)) {
if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
fs.rmdirSync(curDir);
curDir = path.resolve(curDir, '..');
} else {
@@ -250,3 +276,48 @@ 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

@@ -17,35 +17,101 @@
under the License.
*/
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');
var xmlHelpers = require('cordova-common').xmlHelpers;
var CordovaError = require('cordova-common').CordovaError;
var ConfigParser = require('cordova-common').ConfigParser;
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');
module.exports.prepare = function (cordovaProject) {
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;
this._config = updateConfigFilesFrom(cordovaProject.projectConfig,
this._munger, this.locations);
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(updateWwwFrom(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 () {
handleIcons(cordovaProject.projectConfig, self.root);
handleSplashes(cordovaProject.projectConfig, self.root);
})
.then(function () {
self.events.emit('verbose', 'updated project successfully');
}).then(function () {
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
}).then(function () {
events.emit('verbose', 'Prepared android project successfully');
});
};
module.exports.clean = function (options) {
// A cordovaProject isn't passed into the clean() function, because it might have
// been called from the platform shell script rather than the CLI. Check for the
// noPrepare option passed in by the non-CLI clean script. If that's present, or if
// there's no config.xml found at the project root, then don't clean prepared files.
var projectRoot = path.resolve(this.root, '../..');
if ((options && options.noPrepare) || !fs.existsSync(this.locations.configXml) ||
!fs.existsSync(this.locations.configXml)) {
return Promise.resolve();
}
var projectConfig = new ConfigParser(this.locations.configXml);
var self = this;
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));
cleanFileResources(projectRoot, projectConfig, path.relative(projectRoot, self.locations.root));
});
};
@@ -63,26 +129,34 @@ module.exports.prepare = function (cordovaProject) {
* represents current project's configuration. When returned, the
* configuration is already dumped to appropriate config.xml file.
*/
function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
events.emit('verbose', 'Generating config.xml from defaults for platform "android"');
function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
events.emit('verbose', 'Generating platform-specific config.xml from defaults for android at ' + locations.configXml);
// 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)
configMunger.reapply_global_munge().save_all();
events.emit('verbose', 'Merging project\'s config.xml into platform-specific android config.xml');
// Merge changes from app's config.xml into platform's one
var config = new ConfigParser(locations.configXml);
xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
config.doc.getroot(), 'android', /*clobber=*/true);
config.doc.getroot(), 'android', /* clobber= */true);
config.write();
return config;
}
/**
* Logs all file operations via the verbose event stream, indented.
*/
function logFileOp (message) {
events.emit('verbose', ' ' + message);
}
/**
* Updates platform 'www' directory by replacing it with contents of
* 'platform_www' and app www. Also copies project's overrides' folder into
@@ -92,21 +166,36 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
* @param {Object} destinations An object that contains destination
* paths for www files.
*/
function updateWwwFrom(cordovaProject, destinations) {
shell.rm('-rf', destinations.www);
shell.mkdir('-p', destinations.www);
// Copy source files from project's www directory
shell.cp('-rf', path.join(cordovaProject.locations.www, '*'), destinations.www);
// Override www sources by files in 'platform_www' directory
shell.cp('-rf', path.join(destinations.platformWww, '*'), destinations.www);
function updateWww (cordovaProject, destinations) {
var sourceDirs = [
path.relative(cordovaProject.root, cordovaProject.locations.www),
path.relative(cordovaProject.root, destinations.platformWww)
];
// If project contains 'merges' for our platform, use them as another overrides
var merges_path = path.join(cordovaProject.root, 'merges', 'android');
if (fs.existsSync(merges_path)) {
events.emit('verbose', 'Found "merges" for android platform. Copying over existing "www" files.');
var overrides = path.join(merges_path, '*');
shell.cp('-rf', overrides, destinations.www);
events.emit('verbose', 'Found "merges/android" folder. Copying its contents into the android project.');
sourceDirs.push(path.join('merges', 'android'));
}
var targetDir = path.relative(cordovaProject.root, destinations.www);
events.emit(
'verbose', 'Merging and updating files from [' + sourceDirs.join(', ') + '] to ' + targetDir);
FileUpdater.mergeAndUpdateDir(
sourceDirs, targetDir, { rootDir: cordovaProject.root }, logFileOp);
}
/**
* Cleans all files from the platform 'www' directory.
*/
function cleanWww (projectRoot, locations) {
var targetDir = path.relative(projectRoot, locations.www);
events.emit('verbose', 'Cleaning ' + targetDir);
// No source paths are specified, so mergeAndUpdateDir() will clear the target directory.
FileUpdater.mergeAndUpdateDir(
[], targetDir, { rootDir: projectRoot, all: true }, logFileOp);
}
/**
@@ -116,56 +205,72 @@ function updateWwwFrom(cordovaProject, destinations) {
* be used to update project
* @param {Object} locations A map of locations for this platform
*/
function updateProjectAccordingTo(platformConfig, locations) {
function updateProjectAccordingTo (platformConfig, locations) {
// Update app name by editing res/values/strings.xml
var name = platformConfig.name();
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
strings.find('string[@name="app_name"]').text = name;
fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8');
events.emit('verbose', 'Wrote out Android application name to "' + name + '"');
var name = platformConfig.name();
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, '\\\'');
}
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
var pkg = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
var androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
var manifest = new AndroidManifest(locations.manifest);
var orig_pkg = manifest.getPackageId();
var manifestId = manifest.getPackageId();
manifest.getActivity()
.setOrientation(findOrientationValue(platformConfig))
.setOrientation(platformConfig.getPreference('orientation'))
.setLaunchMode(findAndroidLaunchModePreference(platformConfig));
manifest.setVersionName(platformConfig.version())
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
.setPackageId(pkg)
.setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
.setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
.setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
.setPackageId(androidPkgName)
.write();
var javaPattern = path.join(locations.root, 'src', orig_pkg.replace(/\./g, '/'), '*.java');
var java_files = shell.ls(javaPattern).filter(function(f) {
return shell.grep(/extends\s+CordovaActivity/g, f);
// Java file paths shouldn't be hard coded
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) {
throw new CordovaError('No Java files found which extend CordovaActivity.');
} else if(java_files.length > 1) {
events.emit('log', 'Multiple candidate Java files (.java files which extend CordovaActivity) found. Guessing at the first one, ' + java_files[0]);
throw new CordovaError('No Java files found that extend CordovaActivity.');
} else if (java_files.length > 1) {
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, 'src', pkg.replace(/\./g, '/'), path.basename(java_files[0]));
shell.mkdir('-p', path.dirname(destFile));
shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile);
events.emit('verbose', 'Wrote out Android package name to "' + pkg + '"');
const destFile = java_files[0];
if (orig_pkg !== pkg) {
// 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;
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');
while(currentDir !== sourcesRoot) {
if(fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
while (currentDir !== sourcesRoot) {
if (fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
fs.rmdirSync(currentDir);
currentDir = path.resolve(currentDir, '..');
} else {
@@ -178,7 +283,7 @@ function updateProjectAccordingTo(platformConfig, locations) {
// Consturct the default value for versionCode as
// PATCH + MINOR * 100 + MAJOR * 10000
// see http://developer.android.com/tools/publishing/versioning.html
function default_versionCode(version) {
function default_versionCode (version) {
var nums = version.split('-')[0].split('.');
var versionCode = 0;
if (+nums[0]) {
@@ -190,68 +295,272 @@ function default_versionCode(version) {
if (+nums[2]) {
versionCode += +nums[2];
}
events.emit('verbose', 'android-versionCode not found in config.xml. Generating a code based on version in config.xml (' + version + '): ' + versionCode);
return versionCode;
}
function copyImage(src, resourcesDir, density, name) {
var destFolder = path.join(resourcesDir, (density ? 'drawable-': 'drawable') + density);
var isNinePatch = !!/\.9\.png$/.exec(src);
var ninePatchName = name.replace(/\.png$/, '.9.png');
// default template does not have default asset for this density
if (!fs.existsSync(destFolder)) {
fs.mkdirSync(destFolder);
function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png');
}
var destFilePath = path.join(destFolder, isNinePatch ? ninePatchName : name);
events.emit('verbose', 'copying image from ' + src + ' to ' + destFilePath);
shell.cp('-f', src, destFilePath);
var resourcePath = path.join(resourcesDir, (density ? type + '-' + density : type), name);
return resourcePath;
}
function handleSplashes(projectConfig, platformRoot) {
var resources = projectConfig.getSplashScreens('android');
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');
// if there are "splash" elements in config.xml
if (resources.length > 0) {
deleteDefaultResourceAt(platformRoot, 'screen.png');
events.emit('verbose', 'splash screens: ' + JSON.stringify(resources));
if (resources.length === 0) {
events.emit('verbose', 'This app does not have splash screens defined');
return;
}
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
var projectRoot = path.dirname(projectConfig.path);
var destination = path.join(platformRoot, 'res');
var resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'drawable', 'screen.png');
var hadMdpi = false;
resources.forEach(function (resource) {
if (!resource.density) {
return;
}
if (resource.density == 'mdpi') {
hadMdpi = true;
}
copyImage(path.join(projectRoot, resource.src), destination, resource.density, 'screen.png');
});
// There's no "default" drawable, so assume default == mdpi.
if (!hadMdpi && resources.defaultResource) {
copyImage(path.join(projectRoot, resources.defaultResource.src), destination, 'mdpi', 'screen.png');
var hadMdpi = false;
resources.forEach(function (resource) {
if (!resource.density) {
return;
}
if (resource.density === 'mdpi') {
hadMdpi = true;
}
var targetPath = getImageResourcePath(
platformResourcesDir, 'drawable', resource.density, 'screen.png', path.basename(resource.src));
resourceMap[targetPath] = resource.src;
});
// There's no "default" drawable, so assume default == mdpi.
if (!hadMdpi && resources.defaultResource) {
var targetPath = getImageResourcePath(
platformResourcesDir, 'drawable', 'mdpi', 'screen.png', path.basename(resources.defaultResource.src));
resourceMap[targetPath] = resources.defaultResource.src;
}
events.emit('verbose', 'Updating splash screens at ' + platformResourcesDir);
FileUpdater.updatePaths(
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
var resources = projectConfig.getSplashScreens('android');
if (resources.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png');
events.emit('verbose', 'Cleaning splash screens 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);
}
}
function handleIcons(projectConfig, platformRoot) {
var icons = projectConfig.getIcons('android');
function updateIcons (cordovaProject, platformResourcesDir) {
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;
}
deleteDefaultResourceAt(platformRoot, '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',
@@ -259,11 +568,15 @@ function handleIcons(projectConfig, platformRoot) {
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) {
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;
@@ -276,15 +589,37 @@ function handleIcons(projectConfig, platformRoot) {
};
// iterate over all icon elements to find the default icon and call parseIcon
for (var i=0; i<icons.length; i++) {
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', 'more than one default icon: ' + JSON.stringify(icon));
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;
}
@@ -293,27 +628,84 @@ function handleIcons(projectConfig, platformRoot) {
}
}
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
var projectRoot = path.dirname(projectConfig.path);
var destination = path.join(platformRoot, 'res');
for (var density in android_icons) {
copyImage(path.join(projectRoot, android_icons[density].src), destination, density, 'icon.png');
}
// There's no "default" drawable, so assume default == mdpi.
if (default_icon && !android_icons.mdpi) {
copyImage(path.join(projectRoot, default_icon.src), destination, 'mdpi', 'icon.png');
}
return {
android_icons: android_icons,
default_icon: default_icon
};
}
// remove the default resource name from all drawable folders
function deleteDefaultResourceAt(baseDir, resourceName) {
shell.ls(path.join(baseDir, 'res/drawable-*'))
.forEach(function (drawableFolder) {
var imagePath = path.join(drawableFolder, resourceName);
shell.rm('-f', [imagePath, imagePath.replace(/\.png$/, '.9.png')]);
events.emit('verbose', 'Deleted ' + imagePath);
function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
var icons = projectConfig.getIcons('android');
// 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) {
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;
}
function updateFileResources (cordovaProject, platformDir) {
var files = cordovaProject.projectConfig.getFileResources('android');
// if there are resource-file elements in config.xml
if (files.length === 0) {
events.emit('verbose', 'This app does not have additional resource files defined');
return;
}
var resourceMap = {};
files.forEach(function (res) {
var targetPath = path.join(platformDir, res.target);
resourceMap[targetPath] = res.src;
});
events.emit('verbose', 'Updating resource files at ' + platformDir);
FileUpdater.updatePaths(
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function cleanFileResources (projectRoot, projectConfig, platformDir) {
var files = projectConfig.getFileResources('android', true);
if (files.length > 0) {
events.emit('verbose', 'Cleaning resource files at ' + platformDir);
var resourceMap = {};
files.forEach(function (res) {
var filePath = path.join(platformDir, res.target);
resourceMap[filePath] = null;
});
FileUpdater.updatePaths(
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
}
}
/**
@@ -327,7 +719,7 @@ function deleteDefaultResourceAt(baseDir, resourceName) {
* default value, if there is no such preference. The default value is
* 'singleTop'
*/
function findAndroidLaunchModePreference(platformConfig) {
function findAndroidLaunchModePreference (platformConfig) {
var launchMode = platformConfig.getPreference('AndroidLaunchMode');
if (!launchMode) {
// Return a default value
@@ -344,36 +736,3 @@ function findAndroidLaunchModePreference(platformConfig) {
return launchMode;
}
/**
* Queries ConfigParser object for the orientation <preference> value. Warns if
* global preference value is not supported by platform.
*
* @param {Object} platformConfig ConfigParser object
*
* @return {String} Global/platform-specific orientation in lower-case
* (or empty string if both are undefined).
*/
function findOrientationValue(platformConfig) {
var ORIENTATION_DEFAULT = 'default';
var orientation = platformConfig.getPreference('orientation');
if (!orientation) {
return ORIENTATION_DEFAULT;
}
var GLOBAL_ORIENTATIONS = ['default', 'portrait','landscape'];
function isSupported(orientation) {
return GLOBAL_ORIENTATIONS.indexOf(orientation.toLowerCase()) >= 0;
}
// Check if the given global orientation is supported
if (orientation && isSupported(orientation)) {
return orientation;
}
events.emit('warn', 'Unsupported global orientation: ' + orientation +
'. Defaulting to value: ' + ORIENTATION_DEFAULT);
return ORIENTATION_DEFAULT;
}

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,39 +25,36 @@ 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) {
function onFulfilled (value) {
return value;
},
// on rejection either retry, or throw the error
function onRejected(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,15 +17,13 @@
under the License.
*/
/* jshint loopfunc:true */
var path = require('path');
var emulator = require('./emulator');
var device = require('./device');
var PackageType = require('./PackageType');
const { CordovaError, events } = require('cordova-common');
var path = require('path'),
build = require('./build'),
emulator = require('./emulator'),
device = require('./device'),
Q = require('q');
function getInstallTarget(runOptions) {
function getInstallTarget (runOptions) {
var install_target;
if (runOptions.target) {
install_target = runOptions.target;
@@ -50,83 +46,84 @@ function getInstallTarget(runOptions) {
*
* @return {Promise}
*/
module.exports.run = function(runOptions) {
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) {
return device.list().then(function (device_list) {
if (device_list.length > 0) {
self.events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
install_target = device_list[0];
} else {
self.events.emit('warn', 'No target specified, deploying to emulator');
events.emit('warn', 'No target specified and no devices found, deploying to emulator');
install_target = '--emulator';
}
});
}
}).then(function() {
if (install_target == '--device') {
}).then(function () {
if (install_target === '--device') {
return device.resolveTarget(null);
} else if (install_target == '--emulator') {
} else if (install_target === '--emulator') {
// Give preference to any already started emulators. Else, start one.
return emulator.list_started()
.then(function(started) {
return emulator.list_started().then(function (started) {
return started && started.length > 0 ? started[0] : emulator.start();
}).then(function(emulatorId) {
}).then(function (emulatorId) {
return emulator.resolveTarget(emulatorId);
});
}
// They specified a specific device/emulator ID.
return device.list()
.then(function(devices) {
return device.list().then(function (devices) {
if (devices.indexOf(install_target) > -1) {
return device.resolveTarget(install_target);
}
return emulator.list_started()
.then(function(started_emulators) {
return emulator.list_started().then(function (started_emulators) {
if (started_emulators.indexOf(install_target) > -1) {
return emulator.resolveTarget(install_target);
}
return emulator.list_images()
.then(function(avds) {
return emulator.list_images().then(function (avds) {
// if target emulator isn't started, then start it.
for (var avd in avds) {
if (avds[avd].name == install_target) {
return emulator.start(install_target)
.then(function(emulatorId) {
if (avds[avd].name === install_target) {
return emulator.start(install_target).then(function (emulatorId) {
return emulator.resolveTarget(emulatorId);
});
}
}
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 emulator.wait_for_boot(resolvedTarget.target)
.then(function () {
}).then(function (resolvedTarget) {
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);
});
});
};
module.exports.help = function() {
module.exports.help = function () {
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + ' [options]');
console.log('Build options :');
console.log(' --debug : Builds project in debug mode');

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
@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0start-emulator"
IF EXIST %script_path% (
node "%script_path%" %*
node %script_path% %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'start-emulator' script in 'cordova\lib' folder, aborting...>&2

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

18
bin/templates/cordova/loggingHelper.js vendored Normal file
View File

@@ -0,0 +1,18 @@
var CordovaLogger = require('cordova-common').CordovaLogger;
module.exports = {
adjustLoggerLevel: function (opts) {
if (opts instanceof Array) {
opts.silent = opts.indexOf('--silent') !== -1;
opts.verbose = opts.indexOf('--verbose') !== -1;
}
if (opts.silent) {
CordovaLogger.get().setLevel('error');
}
if (opts.verbose) {
CordovaLogger.get().setLevel('verbose');
}
}
};

View File

@@ -24,28 +24,31 @@ 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;
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 = "5.2.0-dev";
const Api = require('./Api');
module.exports.version = VERSION;
if (!module.parent) {
console.log(VERSION);
}
console.log(Api.version());

View File

@@ -28,6 +28,13 @@ public class __ACTIVITY__ extends CordovaActivity
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// enable Cordova apps to be started in the background
Bundle extras = getIntent().getExtras();
if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
moveTaskToBack(true);
}
// Set by <content src="index.html" /> in config.xml
loadUrl(launchUrl);
}

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="@drawable/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="14" android:targetSdkVersion="__APILEVEL__"/>
</manifest>

View File

@@ -0,0 +1,420 @@
/*
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'
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()
}
dependencies {
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()
jcenter()
}
}
task wrapper(type: Wrapper) {
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;
}
// 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
}
// 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
if (!project.hasProperty('cdvVersionCodeForceAbiDigit')) {
cdvVersionCodeForceAbiDigit = 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 hasBuildExtras1 = file('build-extras.gradle').exists()
if (hasBuildExtras1) {
apply from: 'build-extras.gradle'
}
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
}
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.cdvVersionCodeForceAbiDigit = cdvVersionCodeForceAbiDigit == null ? false : cdvVersionCodeForceAbiDigit.toBoolean();
// 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) {
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 {
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")
if (cdvMinSdkVersion != null) {
minSdkVersion cdvMinSdkVersion
}
if (cdvMaxSdkVersion != null) {
maxSdkVersion cdvMaxSdkVersion
}
if(cdvTargetSdkVersion != null) {
targetSdkVersion cdvTargetSdkVersion
}
}
lintOptions {
abortOnError false;
}
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.
if (Boolean.valueOf(cdvBuildMultipleApks)) {
flavorDimensions "default"
productFlavors {
armeabi {
versionCode defaultConfig.versionCode*10 + 1
ndk {
abiFilters = ["armeabi"]
}
}
armv7 {
versionCode defaultConfig.versionCode*10 + 2
ndk {
abiFilters = ["armeabi-v7a"]
}
}
arm64 {
versionCode defaultConfig.versionCode*10 + 3
ndk {
abiFilters = ["arm64-v8a"]
}
}
x86 {
versionCode defaultConfig.versionCode*10 + 4
ndk {
abiFilters = ["x86"]
}
}
x86_64 {
versionCode defaultConfig.versionCode*10 + 5
ndk {
abiFilters = ["x86_64"]
}
}
}
} else if (Boolean.valueOf(cdvVersionCodeForceAbiDigit)) {
// This provides compatibility to the default logic for versionCode before cordova-android 5.2.0
defaultConfig {
versionCode defaultConfig.versionCode*10
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
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)
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
}
/*
* WARNING: Cordova Lib and platform scripts do management inside of this code here,
* if you are adding the dependencies manually, do so outside the comments, otherwise
* the Cordova tools will overwrite them
*/
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"))
// 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(['validateReleaseSigning', 'validateSigningRelease', 'validateSigningArmv7Release', 'validateSigningX76Release'].contains(task.name)) {
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()
}
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

@@ -18,25 +18,25 @@
*/
var app = {
// Application Constructor
initialize: function() {
initialize: function () {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
bindEvents: function () {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
onDeviceReady: function () {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
receivedEvent: function (id) {
var parentElement = document.getElementById(id);
var listeningElement = parentElement.querySelector('.listening');
var receivedElement = parentElement.querySelector('.received');
@@ -48,4 +48,4 @@ var app = {
}
};
app.initialize();
app.initialize();

View File

@@ -1,303 +1,52 @@
/*
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.
*/
// GENERATED FILE! DO NOT EDIT!
apply plugin: 'android'
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
mavenCentral()
google()
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:1.5.0'
}
}
// Allow plugins to declare Maven dependencies via build-extras.gradle.
repositories {
mavenCentral()
}
task wrapper(type: Wrapper) {
gradleVersion = '2.8'
}
// 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)
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
}
}
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']
}
allprojects {
repositories {
google()
jcenter()
}
defaultConfig {
versionCode cdvVersionCode ?: Integer.parseInt("" + privateHelpers.extractIntFromManifest("versionCode") + "0")
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 + 2
ndk {
abiFilters "armeabi-v7a", ""
}
}
x86 {
versionCode defaultConfig.versionCode + 4
ndk {
abiFilters "x86", ""
}
}
all {
ndk {
abiFilters "all", ""
}
}
}
} 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)
//This replaces project.properties w.r.t. build settings
project.ext {
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
}
}
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') {
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()
task clean(type: Delete) {
delete rootProject.buildDir
}

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,15 +1,13 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# This file was originally created by the Android Tools, but is now
# used by cordova-android to manage the state of the various third party
# libraries used in your application
# This is the Library Module that contains the Cordova Library, this is not
# required when using an AAR
android.library.reference.1=CordovaLib
# This is the application project. This is only required for Android Studio Gradle projects
android.library.reference.2=app
# Project target.
target=This_gets_replaced

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 478 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 493 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

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