Compare commits

..

20 Commits
8.0.x ... 6.2.x

Author SHA1 Message Date
filmaj
c0df73c3c1 Set VERSION to 6.2.3 (via coho) 2017-05-02 16:19:19 -07:00
filmaj
211a7fc6a8 Update JS snapshot to version 6.2.3 (via coho) 2017-05-02 16:19:19 -07:00
filmaj
ce67804b65 Set version to 6.2.3 2017-05-02 16:03:36 -07:00
filmaj
f3ded26e35 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:01:31 -07:00
filmaj
4b688f8715 CB-12746: decrement working dev version on master to 6.2.4-dev, to align with impending patch release. 2017-05-02 16:00:43 -07:00
Simon MacDonald
e3b3bcd75f CB-12015: initial-scale values less than 1.0 are ignored on Android
This closes #376
2017-05-02 15:59:34 -07:00
Steve Gill
5a7590397d CB-12697 Updated checked-in node_modules 2017-05-02 15:59:23 -07:00
filmaj
af31c83ec1 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-05-02 15:59:16 -07:00
filmaj
bd94735ba2 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-05-02 15:59:08 -07:00
filmaj
46e47a3c5c 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-05-02 15:58:56 -07:00
Steve Gill
814aca2b68 Set VERSION to 6.2.2 (via coho) 2017-04-24 22:05:42 -07:00
Steve Gill
cf159c1ae3 Update JS snapshot to version 6.2.2 (via coho) 2017-04-24 22:05:42 -07:00
Steve Gill
4868d5e59e updated build.gradle version info for bintray upload 2017-04-06 19:03:10 -05:00
Steve Gill
34c7e8a534 updated build.gradle version for bintray publish 2017-04-04 13:37:06 -05:00
Steve Gill
d36cfeafa2 CB-12627 Updated RELEASENOTES and Version for release 6.2.1 2017-04-02 18:59:56 -05:00
Steve Gill
d2c71e7d75 CB-12621: reverted elementtree dep to 0.1.6 2017-04-02 18:59:41 -05:00
Steve Gill
fc8cd37495 Set VERSION to 6.2.1 (via coho) 2017-04-02 17:43:13 -05:00
Steve Gill
3f1113ed8f Update JS snapshot to version 6.2.1 (via coho) 2017-04-02 17:43:13 -05:00
Steve Gill
4d55fdb3e5 Set VERSION to 6.2.0 (via coho) 2017-03-28 15:24:44 -07:00
Steve Gill
b175d611bd Update JS snapshot to version 6.2.0 (via coho) 2017-03-28 15:18:35 -07:00
952 changed files with 94650 additions and 7293 deletions

View File

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

View File

@@ -1,10 +0,0 @@
root: true
extends: semistandard
rules:
indent:
- error
- 4
camelcase: off
padded-blocks: off
operator-linebreak: off
no-throw-literal: off

View File

@@ -1,42 +0,0 @@
<!--
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

View File

@@ -1,50 +0,0 @@
---
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

@@ -1,29 +0,0 @@
---
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

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

View File

@@ -1,5 +1,6 @@
<!--
Please make sure the checklist boxes are all checked before submitting the PR. The checklist is intended as a quick reference, for complete details please see our Contributor Guidelines:
Please make sure the checklist boxes are all checked before submitting the PR. The checklist
is intended as a quick reference, for complete details please see our Contributor Guidelines:
http://cordova.apache.org/contribute/contribute_guidelines.html
@@ -9,27 +10,13 @@ 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. -->
### What does this PR do?
### Description
<!-- Describe your changes in detail -->
### Testing
<!-- Please describe in detail how you tested your changes. -->
### What testing has been done on this change?
### 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
- [ ] [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.

129
.gitignore vendored
View File

@@ -1,21 +1,11 @@
.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
@@ -25,24 +15,119 @@ example
/framework/libs
/framework/javadoc-public
/framework/javadoc-private
/test/.externalNativeBuild
/test/libs
example
/test/bin
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/gradle
/test/gradlew
/test/gradlew.bat
/test/assets/www/.tmp*
/test/assets/www/cordova.js
/test/bin
/test/build
/test/captures
/test/libs
.gradle
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
node_modules/
coverage/
.nyc_output/
package-lock.json
node_modules/jshint
node_modules/promise-matchers
node_modules/jasmine
node_modules/rewire
node_modules/istanbul
node_modules/.bin/cake
node_modules/.bin/coffee
node_modules/.bin/escodegen
node_modules/.bin/esgenerate
node_modules/.bin/esparse
node_modules/.bin/esvalidate
node_modules/.bin/handlebars
node_modules/.bin/istanbul
node_modules/.bin/jasmine
node_modules/.bin/js-yaml
node_modules/.bin/jshint
node_modules/.bin/mkdirp
node_modules/.bin/r.js
node_modules/.bin/r_js
node_modules/.bin/strip-json-comments
node_modules/.bin/uglifyjs
node_modules/.bin/which
node_modules/align-text/
node_modules/amdefine/
node_modules/argparse/
node_modules/async/
node_modules/camelcase/
node_modules/center-align/
node_modules/cli/
node_modules/cliui/
node_modules/coffee-script/
node_modules/console-browserify/
node_modules/core-util-is/
node_modules/date-now/
node_modules/decamelize/
node_modules/deep-is/
node_modules/dom-serializer/
node_modules/domelementtype/
node_modules/domhandler/
node_modules/domutils/
node_modules/entities/
node_modules/escodegen/
node_modules/esprima/
node_modules/estraverse/
node_modules/esutils/
node_modules/exit/
node_modules/fast-levenshtein/
node_modules/fileset/
node_modules/gaze/
node_modules/growl/
node_modules/handlebars/
node_modules/has-flag/
node_modules/htmlparser2/
node_modules/is-buffer/
node_modules/isarray/
node_modules/isexe/
node_modules/jasmine-growl-reporter/
node_modules/jasmine-reporters/
node_modules/js-yaml/
node_modules/kind-of/
node_modules/lazy-cache/
node_modules/levn/
node_modules/longest/
node_modules/lru-cache/
node_modules/minimist/
node_modules/mkdirp/
node_modules/optimist/
node_modules/optionator/
node_modules/prelude-ls/
node_modules/readable-stream/
node_modules/repeat-string/
node_modules/requirejs/
node_modules/resolve/
node_modules/right-align/
node_modules/sigmund/
node_modules/source-map/
node_modules/sprintf-js/
node_modules/string_decoder/
node_modules/strip-json-comments/
node_modules/supports-color/
node_modules/type-check/
node_modules/uglify-js/
node_modules/uglify-to-browserify/
node_modules/walkdir/
node_modules/which/
node_modules/window-size/
node_modules/wordwrap/
node_modules/yargs/
node_modules/jasmine-core/
node_modules/fs.realpath/
/coverage

3
.jshintignore Normal file
View File

@@ -0,0 +1,3 @@
bin/node_modules/*
bin/templates/project/*
spec/fixtures/*

10
.jshintrc Normal file
View File

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

View File

@@ -6,4 +6,3 @@ spec
appveyor.yml
framework/build
ic_launcher.png
build

View File

@@ -2,36 +2,21 @@ language: android
sudo: false
jdk:
- oraclejdk8
android:
components:
- build-tools-28.0.3
env:
global:
# Keep gradle from crapping all over the log
- TERM=dumb
matrix:
- nodejs_version=6
- nodejs_version=8
- nodejs_version=10
before_install:
- nvm install 6
- node --version
- gradle --version
install:
# Install a sdkmanager version that supports the --licenses switch and
# accept any Android SDK licenses. The output redirection prevents us from
# hitting the travis log size limit of 4MB which would fail the build.
- yes | sdkmanager tools > /dev/null
- yes | sdkmanager --licenses > /dev/null
- nvm install $nodejs_version
- npm install
- npm install -g codecov
- echo y | android update sdk -u --filter android-22,android-23,android-24,android-25
android:
components:
- tools
- tools
script:
- gradle --version
- node --version
- npm --version
- npm test
- npm run jshint
- npm run cover
- npm run test-build
after_script:
- codecov

View File

@@ -30,6 +30,7 @@ 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.
- Run the tests so your patch doesn't break existing functionality.

View File

@@ -31,9 +31,12 @@ at the core, applications written with web technology: HTML, CSS and JavaScript.
[Apache Cordova](https://cordova.apache.org) is a project of The Apache Software Foundation (ASF).
:warning: Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Android%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC)
## Requires
- Java JDK 1.8 or greater
- Java JDK 1.6 or greater
- Android SDK [http://developer.android.com](http://developer.android.com)
@@ -59,9 +62,3 @@ 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!

View File

@@ -20,121 +20,6 @@
-->
## Release Notes for Cordova (Android) ##
### 8.0.0 (Feb 13, 2019)
* [GH-669](https://github.com/apache/cordova-android/pull/669) Added Missing License Headers
* [GH-655](https://github.com/apache/cordova-android/pull/655) Use custom Gradle properties to read minSdkVersion value from `config.xml`
* [GH-656](https://github.com/apache/cordova-android/pull/656) Quick fix to support **Android**_SDK_ROOT
* [GH-632](https://github.com/apache/cordova-android/pull/632) Ignore more Gradle build artifacts in **Android** project
* [GH-642](https://github.com/apache/cordova-android/pull/642) **Android** tools 3.3 & **Gradle** 4.10.3 update
* [GH-654](https://github.com/apache/cordova-android/pull/654) Quick updates to top-level `project.properties`
* [GH-635](https://github.com/apache/cordova-android/pull/635) Ignore **Android** Studio `.idea` files in project
* [GH-624](https://github.com/apache/cordova-android/pull/624) Add missing log to Java version check
* [GH-630](https://github.com/apache/cordova-android/pull/630) Update `emulator.js` to fix issue [GH-608](https://github.com/apache/cordova-android/pull/608)
* [GH-626](https://github.com/apache/cordova-android/pull/626) Added `package-lock.json` to `.gitignore`
* [GH-620](https://github.com/apache/cordova-android/pull/620) Fix requirements error messages for JDK 8
* [GH-619](https://github.com/apache/cordova-android/pull/619) javac error message fixes in requirements check
* [GH-612](https://github.com/apache/cordova-android/pull/612) Android Platform Release Preparation (Cordova 9)
* [GH-607](https://github.com/apache/cordova-android/pull/607) Copy `node_modules` if the directory exists
* [GH-582](https://github.com/apache/cordova-android/pull/582) Improve Test `README`
* [GH-589](https://github.com/apache/cordova-android/pull/589) Rewrite install dir resolution for legacy plugins
* [GH-572](https://github.com/apache/cordova-android/pull/572) Resolve issue with plugin `target-dir="app*"` subdirs
* [GH-567](https://github.com/apache/cordova-android/pull/567) Output current package name if package name can't be validated
* [GH-507](https://github.com/apache/cordova-android/pull/507) Gradle Updates
* [GH-559](https://github.com/apache/cordova-android/pull/559) Eslint ignore version file
* [GH-550](https://github.com/apache/cordova-android/pull/550) Fix for old plugins with non-Java sources
* [GH-558](https://github.com/apache/cordova-android/pull/558) Update `cordova.js` from `cordova-js@4.2.3`
* [GH-553](https://github.com/apache/cordova-android/pull/553) Check for `build-extras.gradle` in the app-parent directory
* [GH-551](https://github.com/apache/cordova-android/pull/551) Add missing cast for `cdvMinSdkVersion`
* [GH-539](https://github.com/apache/cordova-android/pull/539) Fix destination path fallback
* [GH-544](https://github.com/apache/cordova-android/pull/544) Remove obsolete check for JellyBean
* [GH-465](https://github.com/apache/cordova-android/pull/465) Removes Gradle property in-line command arguments for `gradle.properties`
* [GH-523](https://github.com/apache/cordova-android/pull/523) Always put the Google repo above jcenter
* [GH-486](https://github.com/apache/cordova-android/pull/486) Change deprecated "compile" to "implementation"
* [GH-495](https://github.com/apache/cordova-android/pull/495) Incorrect default sdk version issue fix
* [GH-493](https://github.com/apache/cordova-android/pull/493) Remove bundled dependencies
* [GH-490](https://github.com/apache/cordova-android/pull/490) Fixes build & run related bugs from builder refactor
* [GH-464](https://github.com/apache/cordova-android/pull/464) Unit tests for **Android**_sdk and **Android**Project
* [GH-448](https://github.com/apache/cordova-android/pull/448) [CB-13685](https://issues.apache.org/jira/browse/CB-13685) Adaptive Icon Support
* [GH-487](https://github.com/apache/cordova-android/pull/487) Do not attempt an activity intent AND a url load into the webview, return from the internal webview load.
* [GH-461](https://github.com/apache/cordova-android/pull/461) Remove old builders code
* [GH-463](https://github.com/apache/cordova-android/pull/463) Emulator: Add unit tests and remove Q
* [GH-462](https://github.com/apache/cordova-android/pull/462) Device: Add unit tests and remove Q
* [GH-457](https://github.com/apache/cordova-android/pull/457) Emulator: handle "device still connecting" error
* [GH-445](https://github.com/apache/cordova-android/pull/445) Run and retryPromise improvements and tests
* [GH-453](https://github.com/apache/cordova-android/pull/453) Lint JS files w/out extension too
* [GH-452](https://github.com/apache/cordova-android/pull/452) Emit log event instead of logging directly
* [GH-449](https://github.com/apache/cordova-android/pull/449) Increase old plugin compatibility
* [GH-442](https://github.com/apache/cordova-android/pull/442) Fixes and cleanup for Java tests and CI
* [GH-446](https://github.com/apache/cordova-android/pull/446) [CB-14101](https://issues.apache.org/jira/browse/CB-14101) Fix Java version check for Java >= 9
* [CB-14127](https://issues.apache.org/jira/browse/CB-14127) Move google maven repo ahead of jcenter
* [CB-14038](https://issues.apache.org/jira/browse/CB-14038) Fix false positive detecting project type
* [CB-14008](https://issues.apache.org/jira/browse/CB-14008) Updating Gradle Libraries to work with **Android** Studio 3.1.0
* [CB-13975](https://issues.apache.org/jira/browse/CB-13975) Fix to fire pause event when cdvStartInBackground=true
* [CB-13830](https://issues.apache.org/jira/browse/CB-13830) Add handlers for plugins that use non-Java source files, such as Camera
* [CB-13923](https://issues.apache.org/jira/browse/CB-13923) Fix -1 length for compressed files
### 7.1.0 (Feb 20, 2018)
* [CB-13879](https://issues.apache.org/jira/browse/CB-13879) updated gradle tools dependency to 3.0.1 for project template
* [CB-13831](https://issues.apache.org/jira/browse/CB-13831) Update `android-versions` to 1.3.0 to support SDK 27.
* [CB-13800](https://issues.apache.org/jira/browse/CB-13800) Drop pre-KitKat specific code
* [CB-13724](https://issues.apache.org/jira/browse/CB-13724) Updated the **Android** Tooling required for the latest version on both the test project, and the template
* [CB-13724](https://issues.apache.org/jira/browse/CB-13724) Bump Target SDK to API 27
* [CB-13646](https://issues.apache.org/jira/browse/CB-13646) Using the deprecated `NDK` by default breaks the build. Crosswalk users need to specify the Gradle parameters to keep it working.
* [CB-12218](https://issues.apache.org/jira/browse/CB-12218) Fix consistency of null result message
* [CB-13571](https://issues.apache.org/jira/browse/CB-13571) Prevent crash with unrecognized **Android** version
* [CB-13721](https://issues.apache.org/jira/browse/CB-13721) Fix build apps that use `cdvHelpers.getConfigPreference`
* [CB-13621](https://issues.apache.org/jira/browse/CB-13621) Wrote similar warning to [CB-12948](https://issues.apache.org/jira/browse/CB-12948) on **iOS**. We no longer support `cordova update` command.
### 7.0.0 (Nov 30, 2017)
* [CB-13612](https://issues.apache.org/jira/browse/CB-13612) Fix the remapper so that XML files copy over and the Camera works again.
* [CB-13741](https://issues.apache.org/jira/browse/CB-13741) Bump `package.json` so we can install plugins
* [CB-13610](https://issues.apache.org/jira/browse/CB-13610) Compress the default app assets
* [CB-12835](https://issues.apache.org/jira/browse/CB-12835) add a Context getter in CordovaInterface
* [CB-8976](https://issues.apache.org/jira/browse/CB-8976) Added the `cdvVersionCodeForceAbiDigit` flag to the template build.gradle that appends 0 to the versionCode when `cdvBuildMultipleApks` is not set
* [CB-12291](https://issues.apache.org/jira/browse/CB-12291) (android) Add x86_64, arm64 and armeabi architecture flavors
* [CB-13602](https://issues.apache.org/jira/browse/CB-13602) We were setting the path wrong, this is hacky but it works
* [CB-13601](https://issues.apache.org/jira/browse/CB-13601) Fixing the standalone run scripts to make sure this works without using the CLI
* [CB-13580](https://issues.apache.org/jira/browse/CB-13580) fix build for multiple apks (different product flavors)
* [CB-13558](https://issues.apache.org/jira/browse/CB-13558) Upgrading the gradle so we can upload the AAR
* [CB-13297](https://issues.apache.org/jira/browse/CB-13297) This just works once you bump the project structure. Java 1.8 compatibility baked-in
* [CB-11244](https://issues.apache.org/jira/browse/CB-11244) **Android** Studio 3 work, things have changed with how the platform is built
* [CB-11244](https://issues.apache.org/jira/browse/CB-11244) Found bug where the gradle subproject changes weren't actually getting written to the correct gradle file
* [CB-13470](https://issues.apache.org/jira/browse/CB-13470) Fix Clean so that it cleans the **Android** Studio structure
* [CB-11244](https://issues.apache.org/jira/browse/CB-11244) Adding specs for resource files inside an **Android** Studio Project
* [CB-11244](https://issues.apache.org/jira/browse/CB-11244) Added remapping for drawables
* [CB-11244](https://issues.apache.org/jira/browse/CB-11244) Found bug in Api.js where xml/strings.xml is used instead of values/strings.xml
* [CB-11244](https://issues.apache.org/jira/browse/CB-11244) Setup Api.js to support multiple builders based on project structure
* [CB-11244](https://issues.apache.org/jira/browse/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
* 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!
### 6.4.0 (Nov 06, 2017)
* [CB-13289](https://issues.apache.org/jira/browse/CB-13289) Fixing build problems with Studio Three, but keeping **Windows** Gradle fix for now, will be deprecated
* [CB-13289](https://issues.apache.org/jira/browse/CB-13289) Fix test to work with new Google **Android** Gradle DSL
* :CB-13501 : update appveyor node versions to support node 8
* [CB-13499](https://issues.apache.org/jira/browse/CB-13499) Remove duplicate "setting" in error strings
* Include missing values for task.name when 'cdvBuildMultipleApks' option is true, 'task.name' can have 'validateSigningArmv7Release' or 'validateSigningX86Release' values too.
* [CB-13406](https://issues.apache.org/jira/browse/CB-13406) Fixed AVD API level comparison when choosing sub-par API level match. Added tests for the best_image method.
* [CB-13404](https://issues.apache.org/jira/browse/CB-13404) add **Android**-versions to bundledDependencies. Ignore best emulator selection when parsed AVD information does not include API level in the target
* [CB-12895](https://issues.apache.org/jira/browse/CB-12895) : eslint ignoring cordova.js
* [CB-12895](https://issues.apache.org/jira/browse/CB-12895) Temporarily disabling eslint since cordova-js does not have eslint yet.
### 6.3.0 (Sep 25, 2017)
* [CB-6936](https://issues.apache.org/jira/browse/CB-6936) fix crash when calling methods on a destroyed webview
* [CB-12981](https://issues.apache.org/jira/browse/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.
* Updated maven repo to include most recent lib versions
* [CB-13177](https://issues.apache.org/jira/browse/CB-13177) Updating to API Level 26
* Revert [CB-12015](https://issues.apache.org/jira/browse/CB-12015) initial-scale values less than 1.0 are ignored on **Android**
* [CB-12730](https://issues.apache.org/jira/browse/CB-12730) The Cordova Compatibility Plugin is now integrated into cordova-android
* [CB-12453](https://issues.apache.org/jira/browse/CB-12453) Remove unnecessary double quotes from .bat files which are the causes of crash if project path contains spaces
* [CB-13031](https://issues.apache.org/jira/browse/CB-13031) Fix bug with case-sensitivity of **Android**-packageName
* [CB-10916](https://issues.apache.org/jira/browse/CB-10916) Support display name for **Android**
* [CB-12423](https://issues.apache.org/jira/browse/CB-12423) make explicit JDK 1.8 or greater is needed in the `README`, we require 1.8 for compilation, but do not have 1.8 Java features yet
* [CB-13006](https://issues.apache.org/jira/browse/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.
* [CB-12950](https://issues.apache.org/jira/browse/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.
* [CB-12605](https://issues.apache.org/jira/browse/CB-12605) In **Windows** get **Android** studio path from the registry
* [CB-12762](https://issues.apache.org/jira/browse/CB-12762) : pointed `package.json` repo items to github mirrors instead of apache repos site
* [CB-12617](https://issues.apache.org/jira/browse/CB-12617) : removed node0.x support for platforms and added engineStrict
### 6.2.3 (May 2, 2017)
* [CB-12640](https://issues.apache.org/jira/browse/CB-12640) better handling of unrecognized Android SDK commands on **Windows**.
* [CB-12640](https://issues.apache.org/jira/browse/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`

View File

@@ -1 +1 @@
8.0.0
6.2.3

View File

@@ -1,36 +1,20 @@
environment:
ANDROID_HOME: "C:\\android"
# If the gradle daemon is used, the build hangs after generating the wrapper
GRADLE_OPTS: -Dorg.gradle.daemon=false
# URL for SDK Tools, Revision 26.1.1 (September 2017)
SDK_TOOLS_URL: https://dl.google.com/android/repository/sdk-tools-windows-4333796.zip
matrix:
- nodejs_version: 6
- nodejs_version: 8
- nodejs_version: 10
matrix:
- nodejs_version: "0.10"
- nodejs_version: "0.12"
- nodejs_version: "4"
- nodejs_version: "6"
install:
# Install Android SDK Tools
- mkdir "%ANDROID_HOME%"
- appveyor DownloadFile "%SDK_TOOLS_URL%" -FileName "%TMP%/sdk-tools.zip"
- 7z x "%TMP%/sdk-tools.zip" -o"%ANDROID_HOME%" > nul
- set PATH=%PATH%;"%ANDROID_HOME%\tools\bin"
- yes 2> nul | sdkmanager --licenses > nul
- sdkmanager "build-tools;28.0.3"
- choco install gradle --version 3.4.1
- ps: Install-Product node $env:nodejs_version
- npm install
# - cinst android-sdk
# - echo y | android update sdk -u --filter android-22,android-23
- ps: Install-Product node $env:nodejs_version
- npm install
build: off
test_script:
- gradle --version
- node --version
- npm --version
- npm test
- node --version
- npm --version
- npm run test
# - npm run test-build

View File

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

View File

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

@@ -22,10 +22,9 @@
var check_reqs = require('./templates/cordova/lib/check_reqs');
check_reqs.run().done(
function success () {
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

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

View File

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

View File

@@ -19,49 +19,33 @@
under the License.
*/
var shell = require('shelljs');
var Q = require('q');
var path = require('path');
var fs = require('fs');
var check_reqs = require('./../templates/cordova/lib/check_reqs');
var ROOT = path.join(__dirname, '..', '..');
var shell = require('shelljs'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
check_reqs = require('./../templates/cordova/lib/check_reqs'),
ROOT = path.join(__dirname, '..', '..');
var MIN_SDK_VERSION = 16;
var CordovaError = require('cordova-common').CordovaError;
var AndroidManifest = require('../templates/cordova/lib/AndroidManifest');
// 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.setShellFatal = setShellFatal;
exports.copyJsAndLibrary = copyJsAndLibrary;
exports.copyScripts = copyScripts;
exports.copyBuildRules = copyBuildRules;
exports.writeProjectProperties = writeProjectProperties;
exports.prepBuildFiles = prepBuildFiles;
function setShellFatal (value, func) {
function setShellFatal(value, func) {
var oldVal = shell.config.fatal;
shell.config.fatal = value;
func();
shell.config.fatal = oldVal;
}
function getFrameworkDir (projectPath, shared) {
function getFrameworkDir(projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
}
function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
function copyJsAndLibrary(projectPath, shared, projectName) {
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js');
var app_path = path.join(projectPath, 'app', 'src', 'main');
if (isLegacy) {
app_path = projectPath;
}
shell.cp('-f', srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
shell.cp('-f', srcCordovaJsPath, path.join(projectPath, '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
@@ -73,8 +57,8 @@ function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www'));
// Don't fail if there are no old jars.
exports.setShellFatal(false, function () {
shell.ls(path.join(app_path, 'libs', 'cordova-*.jar')).forEach(function (oldJar) {
setShellFatal(false, function() {
shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function(oldJar) {
console.log('Deleting ' + oldJar);
shell.rm('-f', oldJar);
});
@@ -106,7 +90,7 @@ function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
}
}
function extractSubProjectPaths (data) {
function extractSubProjectPaths(data) {
var ret = {};
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
var m;
@@ -116,7 +100,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;
@@ -124,10 +108,11 @@ 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, '');
@@ -135,32 +120,24 @@ 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);
}
// This makes no sense, what if you're building with a different build system?
function prepBuildFiles (projectPath) {
function prepBuildFiles(projectPath) {
var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders'));
buildModule.getBuilder().prepBuildFiles();
buildModule.getBuilder('gradle').prepBuildFiles();
}
function copyBuildRules (projectPath, isLegacy) {
function copyBuildRules(projectPath) {
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
if (isLegacy) {
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
shell.cp('-f', path.join(srcDir, 'legacy', 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
} else {
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app'));
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
}
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
}
function copyScripts (projectPath) {
function copyScripts(projectPath) {
var bin = path.join(ROOT, 'bin');
var srcScriptsDir = path.join(bin, 'templates', 'cordova');
var destScriptsDir = path.join(projectPath, 'cordova');
@@ -168,10 +145,7 @@ function copyScripts (projectPath) {
shell.rm('-rf', destScriptsDir);
// Copy in the new ones.
shell.cp('-r', srcScriptsDir, projectPath);
let nodeModulesDir = path.join(ROOT, 'node_modules');
if (fs.existsSync(nodeModulesDir)) shell.cp('-r', nodeModulesDir, destScriptsDir);
shell.cp('-r', path.join(ROOT, 'node_modules'), destScriptsDir);
shell.cp(path.join(bin, 'check_reqs*'), destScriptsDir);
shell.cp(path.join(bin, 'android_sdk_version*'), destScriptsDir);
var check_reqs = path.join(destScriptsDir, 'check_reqs');
@@ -190,18 +164,17 @@ 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 + 'Must look like: `com.company.Name`. Currently is: `' + package_name + '`'));
return Q.reject(new CordovaError(msg + 'Package name must look like: com.company.Name'));
}
// Class is a reserved word
if (/\b[Cc]lass\b/.test(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'));
}
@@ -213,19 +186,19 @@ function validatePackageName (package_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'));
}
// Enforce stupid name error
//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
//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'));
}
@@ -251,95 +224,83 @@ 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)) {
if(fs.existsSync(project_path)) {
return Q.reject(new CordovaError('Project already exists! Delete and recreate'));
}
var package_name = config.android_packageName() || config.packageName() || 'my.cordova.project';
var package_name = config.packageName() || 'my.cordova.project';
var project_name = config.name() ?
config.name().replace(/[^\w.]/g, '_') : 'CordovaExample';
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 exports.validatePackageName(package_name)
.then(function () {
exports.validateProjectName(project_name);
}).then(function () {
//Make the package conform to Java package types
return validatePackageName(package_name)
.then(function() {
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 android template project to ' + project_path);
events.emit('verbose', 'Copying android template project to ' + project_path);
exports.setShellFatal(true, function () {
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main');
setShellFatal(true, function() {
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
// 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'));
// copy project template
shell.mkdir('-p', app_path);
shell.cp('-r', path.join(project_template_dir, 'assets'), app_path);
shell.cp('-r', path.join(project_template_dir, 'res'), app_path);
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir(path.join(project_path, 'libs'));
// Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir(path.join(app_path, 'libs'));
// copy cordova.js, cordova.jar
copyJsAndLibrary(project_path, options.link, safe_activity_name);
// copy cordova.js, cordova.jar
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name);
// 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_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);
// Set up ther Android Studio paths
var java_path = path.join(app_path, 'java');
var assets_path = path.join(app_path, 'assets');
var resource_path = path.join(app_path, 'res');
shell.mkdir('-p', java_path);
shell.mkdir('-p', assets_path);
shell.mkdir('-p', resource_path);
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);
// interpolate the activity name and package
var packagePath = package_name.replace(/\./g, path.sep);
var activity_dir = path.join(java_path, packagePath);
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
manifest.write(manifest_path);
shell.mkdir('-p', activity_dir);
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path);
shell.sed('-i', /__NAME__/, project_name, path.join(app_path, 'res', 'values', 'strings.xml'));
shell.sed('-i', /__ID__/, package_name, activity_path);
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(app_path, 'AndroidManifest.xml');
manifest.write(manifest_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));
}).thenResolve(project_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);
};
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';
}
@@ -347,17 +308,34 @@ function generateDoneMessage (type, link) {
}
// Returns a promise.
exports.update = function (projectPath, options, events) {
exports.update = function(projectPath, options, events) {
options = options || {};
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() {
return Q.reject(errorString);
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);
};
// For testing
exports.validatePackageName = validatePackageName;
exports.validateProjectName = validateProjectName;

View File

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

View File

@@ -21,6 +21,7 @@ var path = require('path');
var Q = require('q');
var AndroidProject = require('./lib/AndroidProject');
var AndroidStudio = require('./lib/AndroidStudio');
var PluginManager = require('cordova-common').PluginManager;
var CordovaLogger = require('cordova-common').CordovaLogger;
@@ -28,7 +29,8 @@ var selfEvents = require('cordova-common').events;
var PLATFORM = 'android';
function setupEvents (externalEventEmitter) {
function setupEvents(externalEventEmitter) {
if (externalEventEmitter) {
// This will make the platform internal events visible outside
selfEvents.forwardEventsTo(externalEventEmitter);
@@ -41,6 +43,7 @@ function setupEvents (externalEventEmitter) {
return selfEvents;
}
/**
* Class, that acts as abstraction over particular platform. Encapsulates the
* platform's properties and methods.
@@ -52,30 +55,39 @@ function setupEvents (externalEventEmitter) {
*
* * platform: String that defines a platform name.
*/
function Api (platform, platformRootDir, events) {
function Api(platform, platformRootDir, events) {
this.platform = PLATFORM;
this.root = path.resolve(__dirname, '..');
setupEvents(events);
const appMain = path.join(this.root, 'app', 'src', 'main');
const appRes = path.join(appMain, 'res');
var self = this;
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'),
root: self.root,
www: path.join(self.root, 'assets/www'),
res: path.join(self.root, 'res'),
platformWww: path.join(self.root, 'platform_www'),
configXml: path.join(self.root, 'res/xml/config.xml'),
defaultConfigXml: path.join(self.root, 'cordova/defaults.xml'),
strings: path.join(self.root, 'res/values/strings.xml'),
manifest: path.join(self.root, 'AndroidManifest.xml'),
build: path.join(self.root, 'build'),
// NOTE: Due to platformApi spec we need to return relative paths here
cordovaJs: 'bin/templates/project/assets/www/cordova.js',
cordovaJsSrc: 'cordova-js-src'
};
// XXX Override some locations for Android Studio projects
if(AndroidStudio.isAndroidStudioProject(self.root) === true) {
selfEvents.emit('log', 'Android Studio project detected');
this.android_studio = true;
this.locations.configXml = path.join(self.root, 'app/src/main/res/xml/config.xml');
this.locations.strings = path.join(self.root, 'app/src/main/res/xml/strings.xml');
this.locations.manifest = path.join(self.root, 'app/src/main/AndroidManifest.xml');
this.locations.www = path.join(self.root, 'app/src/main/assets/www');
this.locations.res = path.join(self.root, 'app/src/main/res');
}
}
/**
@@ -100,13 +112,16 @@ Api.createPlatform = function (destination, config, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
result = require('../../lib/create')
.create(destination, config, options, events)
.then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi(PLATFORM, destination, events);
});
} catch (e) {
events.emit('error', 'createPlatform is not callable from the android project API.');
throw (e);
}
catch (e) {
events.emit('error','createPlatform is not callable from the android project API.');
throw(e);
}
return result;
};
@@ -131,13 +146,16 @@ Api.updatePlatform = function (destination, options, events) {
events = setupEvents(events);
var result;
try {
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
result = require('../../lib/create')
.update(destination, options, events)
.then(function (destination) {
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
return new PlatformApi('android', destination, events);
});
} catch (e) {
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
throw (e);
}
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;
};
@@ -208,15 +226,40 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
installOptions.variables.PACKAGE_NAME = project.getPackageName();
}
return Q().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
if(this.android_studio === true) {
installOptions.android_studio = true;
}
return Q()
.then(function () {
//CB-11964: Do a clean when installing the plugin code to get around
//the Gradle bug introduced by the Android Gradle Plugin Version 2.2
//TODO: Delete when the next version of Android Gradle plugin comes out
// Since clean doesn't just clean the build, it also wipes out www, we need
// to pass additional options.
// Do some basic argument parsing
var opts = {};
// Skip cleaning prepared files when not invoking via cordova CLI.
opts.noPrepare = true;
if(!AndroidStudio.isAndroidStudioProject(self.root) && !project.isClean()) {
return self.clean(opts);
}
})
.then(function () {
return PluginManager.get(self.platform, self.locations, project)
.addPlugin(plugin, installOptions);
})
.then(function () {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
};
@@ -236,8 +279,9 @@ Api.prototype.addPlugin = function (plugin, installOptions) {
Api.prototype.removePlugin = function (plugin, uninstallOptions) {
var project = AndroidProject.getProjectFile(this.root);
if (uninstallOptions && uninstallOptions.usePlatformWww === true) {
uninstallOptions.usePlatformWww = false;
if(uninstallOptions && uninstallOptions.usePlatformWww === true && this.android_studio === true) {
uninstallOptions.usePlatformWww = false;
uninstallOptions.android_studio = true;
}
return PluginManager.get(this.platform, this.locations, project)
@@ -246,7 +290,7 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) {
if (plugin.getFrameworks(this.platform).length === 0) return;
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
require('./lib/builders/builders').getBuilder().prepBuildFiles();
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
}.bind(this))
// CB-11022 Return truthy value to prevent running prepare after
.thenResolve(true);
@@ -299,10 +343,11 @@ Api.prototype.removePlugin = function (plugin, uninstallOptions) {
*/
Api.prototype.build = function (buildOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/check_reqs').run()
.then(function () {
return require('./lib/build').run.call(self, buildOptions);
}).then(function (buildResults) {
})
.then(function (buildResults) {
// Cast build result to array of build artifacts
return buildResults.apkPaths.map(function (apkPath) {
return {
@@ -327,9 +372,10 @@ Api.prototype.build = function (buildOptions) {
* @return {Promise} A promise either fulfilled if package was built and ran
* successfully, or rejected with CordovaError.
*/
Api.prototype.run = function (runOptions) {
Api.prototype.run = function(runOptions) {
var self = this;
return require('./lib/check_reqs').run().then(function () {
return require('./lib/check_reqs').run()
.then(function () {
return require('./lib/run').run.call(self, runOptions);
});
};
@@ -341,20 +387,19 @@ Api.prototype.run = function (runOptions) {
* @return {Promise} Return a promise either fulfilled, or rejected with
* CordovaError.
*/
Api.prototype.clean = function (cleanOptions) {
Api.prototype.clean = function(cleanOptions) {
var self = this;
// This will lint, checking for null won't
if (typeof cleanOptions === 'undefined') {
cleanOptions = {};
}
return require('./lib/check_reqs').run().then(function () {
return require('./lib/build').runClean.call(self, cleanOptions);
}).then(function () {
return require('./lib/prepare').clean.call(self, cleanOptions);
});
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
@@ -363,7 +408,7 @@ Api.prototype.clean = function (cleanOptions) {
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
* objects for current platform.
*/
Api.prototype.requirements = function () {
Api.prototype.requirements = function() {
return require('./lib/check_reqs').check_all();
};

View File

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

View File

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

View File

@@ -25,11 +25,11 @@ 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,7 +44,8 @@ function isEmulator (line) {
* devices/emulators
*/
Adb.devices = function (opts) {
return spawn('adb', ['devices'], { cwd: os.tmpdir() }).then(function (output) {
return spawn('adb', ['devices'], {cwd: os.tmpdir()})
.then(function(output) {
return output.split('\n').filter(function (line) {
// Filter out either real devices or emulators, depending on options
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
@@ -58,7 +59,8 @@ Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r');
return spawn('adb', args.concat(packagePath), { cwd: os.tmpdir() }).then(function (output) {
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()})
.then(function(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/)) {
@@ -77,14 +79,15 @@ Adb.install = function (target, packagePath, opts) {
Adb.uninstall = function (target, packageId) {
events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...');
return spawn('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() });
return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()});
};
Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/);
return spawn('adb', args.concat(shellCommand), { cwd: os.tmpdir() }).catch(function (output) {
return 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));
});
@@ -92,7 +95,8 @@ Adb.shell = function (target, shellCommand) {
Adb.start = function (target, activityName) {
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch(function (output) {
return 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));
});

View File

@@ -19,12 +19,12 @@
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) {
function AndroidManifest(path) {
this.path = path;
this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') {
@@ -32,34 +32,38 @@ function AndroidManifest (path) {
}
}
AndroidManifest.prototype.getVersionName = function () {
AndroidManifest.prototype.getVersionName = function() {
return this.doc.getroot().attrib['android:versionName'];
};
AndroidManifest.prototype.setVersionName = function (versionName) {
AndroidManifest.prototype.setVersionName = function(versionName) {
this.doc.getroot().attrib['android:versionName'] = versionName;
return this;
};
AndroidManifest.prototype.getVersionCode = function () {
AndroidManifest.prototype.getVersionCode = function() {
return this.doc.getroot().attrib['android:versionCode'];
};
AndroidManifest.prototype.setVersionCode = function (versionCode) {
AndroidManifest.prototype.setVersionCode = function(versionCode) {
this.doc.getroot().attrib['android:versionCode'] = versionCode;
return this;
};
AndroidManifest.prototype.getPackageId = function () {
AndroidManifest.prototype.getPackageId = function() {
/*jshint -W069 */
return this.doc.getroot().attrib['package'];
/*jshint +W069 */
};
AndroidManifest.prototype.setPackageId = function (pkgId) {
AndroidManifest.prototype.setPackageId = function(pkgId) {
/*jshint -W069 */
this.doc.getroot().attrib['package'] = pkgId;
/*jshint +W069 */
return this;
};
AndroidManifest.prototype.getActivity = function () {
AndroidManifest.prototype.getActivity = function() {
var activity = this.doc.getroot().find('./application/activity');
return {
getName: function () {
@@ -98,16 +102,17 @@ AndroidManifest.prototype.getActivity = function () {
};
};
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion'].forEach(function (sdkPrefName) {
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion']
.forEach(function(sdkPrefName) {
// Copy variable reference to avoid closure issues
var prefName = sdkPrefName;
AndroidManifest.prototype['get' + capitalize(prefName)] = function () {
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) {
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
@@ -123,11 +128,11 @@ AndroidManifest.prototype.getActivity = function () {
};
});
AndroidManifest.prototype.getDebuggable = function () {
AndroidManifest.prototype.getDebuggable = function() {
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
};
AndroidManifest.prototype.setDebuggable = function (value) {
AndroidManifest.prototype.setDebuggable = function(value) {
var application = this.doc.getroot().find('./application');
if (value) {
application.attrib['android:debuggable'] = 'true';
@@ -145,8 +150,8 @@ AndroidManifest.prototype.setDebuggable = function (value) {
* @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');
AndroidManifest.prototype.write = function(destPath) {
fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8');
};
module.exports = AndroidManifest;

View File

@@ -21,19 +21,21 @@ var fs = require('fs');
var path = require('path');
var properties_parser = require('properties-parser');
var AndroidManifest = require('./AndroidManifest');
var AndroidStudio = require('./AndroidStudio');
var pluginHandlers = require('./pluginHandlers');
var projectFileCache = {};
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,16 +54,19 @@ 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) {
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, 'app/src/main/assets/www');
this.www = path.join(this.projectDir, 'assets/www');
if(AndroidStudio.isAndroidStudioProject(projectDir) === true) {
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
}
}
AndroidProject.getProjectFile = function (projectDir) {
@@ -87,12 +92,15 @@ AndroidProject.purgeCache = function (projectDir) {
*
* @return {String} The name of the package
*/
AndroidProject.prototype.getPackageName = function () {
var manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
AndroidProject.prototype.getPackageName = function() {
var manifestPath = path.join(this.projectDir, 'AndroidManifest.xml');
if(AndroidStudio.isAndroidStudioProject(this.projectDir) === true) {
manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
}
return new AndroidManifest(manifestPath).getPackageId();
};
AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, src) {
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();
@@ -102,7 +110,7 @@ AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, s
return subRelativeDir;
};
AndroidProject.prototype.addSubProject = function (parentDir, subDir) {
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);
@@ -118,7 +126,7 @@ AndroidProject.prototype.addSubProject = function (parentDir, subDir) {
this._dirty = true;
};
AndroidProject.prototype.removeSubProject = function (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));
@@ -126,35 +134,35 @@ AndroidProject.prototype.removeSubProject = function (parentDir, subDir) {
this._dirty = true;
};
AndroidProject.prototype.addGradleReference = function (parentDir, subDir) {
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) {
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) {
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) {
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 () {
AndroidProject.prototype.write = function() {
if (!this._dirty) {
return;
}
@@ -193,9 +201,9 @@ AndroidProject.prototype.getUninstaller = function (type) {
* This checks if an Android project is clean or has old build artifacts
*/
AndroidProject.prototype.isClean = function () {
AndroidProject.prototype.isClean = function() {
var build_path = path.join(this.projectDir, 'build');
// If the build directory doesn't exist, it's clean
//If the build directory doesn't exist, it's clean
return !(fs.existsSync(build_path));
};

View File

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

View File

@@ -17,7 +17,8 @@
under the License.
*/
var superspawn = require('cordova-common').superspawn;
var Q = require('q'),
superspawn = require('cordova-common').superspawn;
var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort:
@@ -28,7 +29,7 @@ var suffix_number_regex = /(\d+)$/;
// 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) {
function sort_by_largest_numerical_suffix(a, b) {
var suffix_a = a.match(suffix_number_regex);
var suffix_b = b.match(suffix_number_regex);
if (suffix_a && suffix_b) {
@@ -42,8 +43,9 @@ function sort_by_largest_numerical_suffix (a, b) {
}
}
module.exports.print_newest_available_sdk_target = function () {
return module.exports.list_targets().then(function (targets) {
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]);
});
@@ -61,40 +63,43 @@ module.exports.version_string_to_api_level = {
'5.1': 22,
'6.0': 23,
'7.0': 24,
'7.1.1': 25,
'8.0': 26
'7.1.1': 25
};
function parse_targets (output) {
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.
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 superspawn.spawn('android', ['list', 'target']).then(parse_targets);
module.exports.list_targets_with_android = function() {
return superspawn.spawn('android', ['list', 'target'])
.then(parse_targets);
};
module.exports.list_targets_with_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets);
module.exports.list_targets_with_avdmanager = function() {
return superspawn.spawn('avdmanager', ['list', 'target'])
.then(parse_targets);
};
module.exports.list_targets = function () {
return module.exports.list_targets_with_avdmanager().catch(function (err) {
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/)))) {
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) {
})
.then(function(targets) {
if (targets.length === 0) {
return Promise.reject(new Error('No android targets (SDKs) installed!'));
return Q.reject(new Error('No android targets (SDKs) installed!'));
}
return targets;
});

View File

@@ -19,10 +19,10 @@
under the License.
*/
var Q = require('q');
var path = require('path');
var fs = require('fs');
var nopt = require('nopt');
var Q = require('q'),
path = require('path'),
fs = require('fs'),
nopt = require('nopt');
var Adb = require('./Adb');
@@ -31,10 +31,11 @@ var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError;
module.exports.parseBuildOptions = parseOpts;
function parseOpts (options, resolvedTarget, projectRoot) {
function parseOpts(options, resolvedTarget, projectRoot) {
options = options || {};
options.argv = nopt({
gradle: Boolean,
ant: Boolean,
prepenv: Boolean,
versionCode: String,
minSdkVersion: String,
@@ -46,26 +47,37 @@ function parseOpts (options, resolvedTarget, projectRoot) {
keystoreType: 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.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); }
if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); }
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 = 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'].forEach(function (flagName) {
if (options.argv[flagName])
packageArgs[flagName] = options.argv[flagName];
});
var buildConfig = options.buildConfig;
@@ -76,20 +88,20 @@ function parseOpts (options, resolvedTarget, projectRoot) {
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(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'].forEach(function (key){
packageArgs[key] = packageArgs[key] || androidInfo[key];
});
}
@@ -100,8 +112,8 @@ function parseOpts (options, resolvedTarget, projectRoot) {
packageArgs.password, packageArgs.keystoreType);
}
if (!ret.packageInfo) {
if (Object.keys(packageArgs).length > 0) {
if(!ret.packageInfo) {
if(Object.keys(packageArgs).length > 0) {
events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.');
}
}
@@ -113,11 +125,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();
return builder.prepEnv(opts).then(function () {
var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts)
.then(function() {
return builder.clean(opts);
});
};
@@ -134,21 +146,23 @@ 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();
return builder.prepEnv(opts).then(function () {
var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts)
.then(function() {
if (opts.prepEnv) {
events.emit('verbose', 'Build file successfully prepared.');
return;
}
return builder.build(opts).then(function () {
return builder.build(opts)
.then(function() {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
return {
apkPaths: apkPaths,
buildType: opts.buildType
buildType: opts.buildType,
buildMethod: opts.buildMethod
};
});
});
@@ -158,31 +172,38 @@ 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';
});
}
// 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 helper()
.timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.'))
.then(null, function(err) {
if (/timed out/.exec('' + err)) {
// adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines.
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']).then(function () {
return helper().then(null, function () {
return spawn('killall', ['adb'])
.then(function() {
return helper()
.then(null, function() {
// The double kill is sadly often necessary, at least on mac.
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']).then(function () {
return helper().then(null, function () {
return spawn('killall', ['adb'])
.then(function() {
return helper()
.then(null, function() {
return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
});
});
});
}, function () {
}, function() {
// For non-killall OS's.
return Q.reject(err);
});
@@ -191,10 +212,10 @@ module.exports.detectArchitecture = function (target) {
});
};
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);
@@ -214,7 +235,7 @@ 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) {
function PackageInfo(keystore, alias, storePassword, password, keystoreType) {
this.keystore = {
'name': 'key.store',
'value': keystore
@@ -244,10 +265,10 @@ function PackageInfo (keystore, alias, storePassword, password, keystoreType) {
}
PackageInfo.prototype = {
toProperties: function () {
toProperties: function() {
var self = this;
var result = '';
Object.keys(self).forEach(function (key) {
Object.keys(self).forEach(function(key) {
result += self[key].name;
result += '=';
result += self[key].value.replace(/\\/g, '\\\\');
@@ -257,15 +278,17 @@ PackageInfo.prototype = {
}
};
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.');
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build. Useful for uploading multiple APKs.');
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(' \'--gradleArg=<gradle command line arg>\': Extra args to pass to the gradle command. Use one flag per arg. Ex. --gradleArg=-PcdvBuildMultipleApks=true');
console.log('');
console.log('Signed APK flags (overwrites debug/release-signing.proprties) :');

View File

@@ -0,0 +1,156 @@
/*
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. Use gradle instead.');
}
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: 'pipe'});
}).progress(function (stdio){
if (stdio.stderr) {
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}).catch(function (error) {
if (error.toString().indexOf('Unable to resolve project target') >= 0) {
return check_reqs.check_android_target(error).then(function() {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
});
}
return Q.reject(error);
});
};
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

@@ -0,0 +1,147 @@
/*
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) {
// De-prioritize unsigned builds
var unsignedRE = /-unsigned/;
if (unsignedRE.exec(fileA)) {
return 1;
} else if (unsignedRE.exec(fileB)) {
return -1;
}
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 && arch) {
ret = ret.filter(function(p) {
return path.basename(p).indexOf('-' + arch) != -1;
});
}
return ret;
}

View File

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

View File

@@ -1,370 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
/* eslint no-self-assign: 0 */
/* eslint no-unused-vars: 0 */
var Q = require('q');
var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var spawn = require('cordova-common').superspawn.spawn;
var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs');
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';
class ProjectBuilder {
constructor (rootDirectory) {
this.root = rootDirectory || path.resolve(__dirname, '../../..');
this.binDir = path.join(this.root, 'app', 'build', 'outputs', 'apk');
}
getArgs (cmd, opts) {
if (cmd === 'release') {
cmd = 'cdvBuildRelease';
} else if (cmd === 'debug') {
cmd = 'cdvBuildDebug';
}
let args = [cmd, '-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 spawn(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) {
shell.cp('-f', pluginBuildGradle, subProjectGradle);
}
};
for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') {
checkAndCopy(subProjects[i], this.root);
}
}
var name = this.extractRealProjectNameFromManifest();
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function (p) {
var realDir = p.replace(/[/\\]/g, ':');
var libName = realDir.replace(name + '-', '');
var str = 'include ":' + libName + '"\n';
if (realDir.indexOf(name + '-') !== -1) {
str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n';
}
return str;
});
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
'// GENERATED FILE - DO NOT EDIT\n' +
'include ":"\n' + settingsGradlePaths.join(''));
// Update dependencies within build.gradle.
var buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
var depsList = '';
var root = this.root;
var insertExclude = function (p) {
var gradlePath = path.join(root, p, 'build.gradle');
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
depsList += '{\n exclude module:("CordovaLib")\n }\n';
} else {
depsList += '\n';
}
};
subProjects.forEach(function (p) {
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(function () {
// If the gradle distribution URL is set, make sure it points to version we want.
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
// For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/;
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.10.3-all.zip';
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.chmod('u+w', gradleWrapperPropertiesPath);
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath);
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
var propertiesFilePath = path.join(self.root, propertiesFile);
if (opts.packageInfo) {
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
} else if (isAutoGenerated(propertiesFilePath)) {
shell.rm('-f', propertiesFilePath);
}
});
}
/*
* Builds the project with gradle.
* Returns a promise.
*/
build (opts) {
var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
return spawn(wrapper, args, { stdio: 'pipe' })
.progress(function (stdio) {
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}).catch(function (error) {
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
return check_reqs.check_android_target(error).then(function () {
// If due to some odd reason - check_android_target succeeds
// we should still fail here.
return Q.reject(error);
});
}
return Q.reject(error);
});
}
clean (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);
}
});
});
}
findOutputApks (build_type, arch) {
return findOutputApksHelper(this.binDir, build_type, arch).sort(apkSorter);
}
fetchBuildResults (build_type, arch) {
return {
apkPaths: this.findOutputApks(build_type, arch),
buildType: build_type
};
}
}
module.exports = ProjectBuilder;
function apkSorter (fileA, fileB) {
// De-prioritize arch specific builds
var archSpecificRE = /-x86|-arm/;
if (archSpecificRE.exec(fileA)) {
return 1;
} else if (archSpecificRE.exec(fileB)) {
return -1;
}
// De-prioritize unsigned builds
var unsignedRE = /-unsigned/;
if (unsignedRE.exec(fileA)) {
return 1;
} else if (unsignedRE.exec(fileB)) {
return -1;
}
var timeDiff = fs.statSync(fileB).mtime - fs.statSync(fileA).mtime;
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
}
function findOutputApksHelper (dir, build_type, arch) {
var shellSilent = shell.config.silent;
shell.config.silent = true;
// list directory recursively
var ret = shell.ls('-R', dir).map(function (file) {
// ls does not include base directory
return path.join(dir, file);
}).filter(function (file) {
// find all APKs
return file.match(/\.apk?$/i);
}).filter(function (candidate) {
var apkName = path.basename(candidate);
// Need to choose between release and debug .apk.
if (build_type === 'debug') {
return /-debug/.exec(apkName) && !/-unaligned|-unsigned/.exec(apkName);
}
if (build_type === 'release') {
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
}
return true;
}).sort(apkSorter);
shellSilent = shellSilent;
if (ret.length === 0) {
return ret;
}
// Assume arch-specific build if newest apk has -x86 or -arm.
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
// And show only arch-specific ones (or non-arch-specific)
ret = ret.filter(function (p) {
return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific;
});
if (archSpecific && ret.length > 1 && arch) {
ret = ret.filter(function (p) {
return path.basename(p).indexOf('-' + arch) !== -1;
});
}
return ret;
}
function isAutoGenerated (file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
}

View File

@@ -1,34 +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
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.
*/
const CordovaError = require('cordova-common').CordovaError;
var CordovaError = require('cordova-common').CordovaError;
var knownBuilders = {
ant: 'AntBuilder',
gradle: 'GradleBuilder',
none: 'GenericBuilder'
};
/**
* Helper method that instantiates and returns a builder for specified build type.
* Helper method that instantiates and returns a builder for specified build
* type.
*
* @return {Builder} A builder instance 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.
*/
module.exports.getBuilder = function () {
module.exports.getBuilder = function (builderType, projectRoot) {
if (!knownBuilders[builderType])
throw new CordovaError('Builder ' + builderType + ' is not supported.');
try {
const Builder = require('./ProjectBuilder');
return new Builder();
var Builder = require('./' + knownBuilders[builderType]);
return new Builder(projectRoot);
} catch (err) {
throw new CordovaError('Failed to instantiate ProjectBuilder builder: ' + err);
throw new CordovaError('Failed to instantiate ' + knownBuilders[builderType] + ' builder: ' + err);
}
};

View File

@@ -19,19 +19,21 @@
under the License.
*/
var shelljs = require('shelljs');
var child_process = require('child_process');
var Q = require('q');
var path = require('path');
var fs = require('fs');
var os = require('os');
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
var PROJECT_ROOT = path.join(__dirname, '..', '..');
/* jshint sub:true */
var shelljs = require('shelljs'),
child_process = require('child_process'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
os = require('os'),
REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'),
PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn;
var android_sdk = require('./android_sdk');
function forgivingWhichSync (cmd) {
function forgivingWhichSync(cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
@@ -39,18 +41,29 @@ function forgivingWhichSync (cmd) {
}
}
module.exports.isWindows = function () {
return (os.platform() === 'win32');
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;
}
module.exports.isWindows = function() {
return (os.platform() == 'win32');
};
module.exports.isDarwin = function () {
return (os.platform() === 'darwin');
module.exports.isDarwin = function() {
return (os.platform() == 'darwin');
};
// Get valid target from framework/project.properties if run from this repo
// Otherwise get target from project.properties file within a generated cordova-android project
module.exports.get_target = function () {
function extractFromFile (filePath) {
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);
@@ -70,82 +83,77 @@ module.exports.get_target = function () {
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function () {
return superspawn.spawn('ant', ['-version']).then(function (output) {
module.exports.check_ant = function() {
return superspawn.spawn('ant', ['-version'])
.then(function(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.');
}
}).catch(function(err) {
throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
});
};
module.exports.get_gradle_wrapper = function () {
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 = child_process.spawnSync(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; }
}
if (module.exports.isDarwin()) {
program_dir = fs.readdirSync('/Applications');
while (i < program_dir.length && !foundStudio) {
if (program_dir[i].startsWith('Android Studio')) {
//TODO: Check for a specific Android Studio version, make sure it's not Canary
androidStudioPath = path.join('/Applications', program_dir[i], 'Contents', 'gradle');
foundStudio = true;
} else { ++i; }
}
} else if (module.exports.isWindows()) {
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') {
if(dirs[0].split('-')[0] == 'gradle') {
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
}
} else {
// OK, let's try to check for Gradle!
//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 () {
module.exports.check_gradle = function() {
var sdkDir = process.env['ANDROID_HOME'];
var d = Q.defer();
if (!sdkDir) {
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 gradlePath = module.exports.get_gradle_wrapper();
if (gradlePath.length !== 0) { d.resolve(gradlePath); } else {
if (gradlePath.length !== 0)
d.resolve(gradlePath);
else
d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio'));
}
return d.promise;
};
// Returns a promise.
module.exports.check_java = function () {
module.exports.check_java = function() {
var javacPath = forgivingWhichSync('javac');
var hasJavaHome = !!process.env['JAVA_HOME'];
return Q().then(function () {
return Q().then(function() {
if (hasJavaHome) {
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
if (!javacPath) {
@@ -155,14 +163,13 @@ module.exports.check_java = function () {
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.';
var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
if (fs.existsSync(find_java)) {
return superspawn.spawn(find_java).then(function (stdout) {
return superspawn.spawn(find_java)
.then(function(stdout) {
process.env['JAVA_HOME'] = stdout.trim();
}).catch(function (err) {
if (err) {
throw new CordovaError(default_java_error_msg);
}
}).catch(function(err) {
throw new CordovaError(default_java_error_msg);
});
} else {
// See if we can derive it from javac's location.
@@ -193,36 +200,32 @@ module.exports.check_java = function () {
}
}
}
}).then(function () {
return Q.denodeify(child_process.exec)('javac -version')
.then(outputs => {
// outputs contains two entries: stdout and stderr
// Java <= 8 writes version info to stderr, Java >= 9 to stdout
const output = outputs.join('').trim();
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);
});
}).then(function() {
var msg =
'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
if (process.env['JAVA_HOME']) {
msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
}
// We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true)
.then(function (output) {
//Let's check for at least Java 8, and keep it future proof so we can support Java 10
var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
return match && match[1];
});
});
};
// Returns a promise.
module.exports.check_android = function () {
return Q().then(function () {
module.exports.check_android = function() {
return Q().then(function() {
var androidCmdPath = forgivingWhichSync('android');
var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
function maybeSetAndroidHome (value) {
function maybeSetAndroidHome(value) {
if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true;
process.env['ANDROID_HOME'] = value;
@@ -231,14 +234,6 @@ module.exports.check_android = function () {
// 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_SDK_ROOT']) {
// Quick fix to set ANDROID_HOME according to ANDROID_SDK_ROOT
// if ANDROID_HOME is **not** defined and
// ANDROID_SDK_ROOT **is** defined
// according to environment variables as documented in:
// https://developer.android.com/studio/command-line/variables
maybeSetAndroidHome(path.join(process.env['ANDROID_SDK_ROOT']));
}
if (module.exports.isWindows()) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
@@ -270,10 +265,10 @@ module.exports.check_android = function () {
if (androidCmdPath) {
parentDir = path.dirname(androidCmdPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
if (path.basename(parentDir) == 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_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 valid path to SDK' + path.sep + 'tools directory.');
}
@@ -281,10 +276,10 @@ module.exports.check_android = function () {
if (adbInPath) {
parentDir = path.dirname(adbInPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) === 'platform-tools') {
if (path.basename(parentDir) == 'platform-tools') {
maybeSetAndroidHome(grandParentDir);
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting 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.');
}
@@ -292,17 +287,17 @@ module.exports.check_android = function () {
if (avdmanagerInPath) {
parentDir = path.dirname(avdmanagerInPath);
grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') {
if (path.basename(parentDir) == 'bin' && path.basename(grandParentDir) == 'tools') {
maybeSetAndroidHome(path.dirname(grandParentDir));
} else {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
}
}
}
if (!process.env['ANDROID_HOME']) {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting it manually.\n' +
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'])) {
@@ -335,19 +330,20 @@ module.exports.getAbsoluteAndroidCmd = function () {
return cmd.replace(/(\s)/g, '\\$1');
};
module.exports.check_android_target = function (originalError) {
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) {
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' +
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' +
@@ -361,25 +357,23 @@ module.exports.check_android_target = function (originalError) {
};
// Returns a promise.
module.exports.run = function () {
return Q.all([this.check_java(), this.check_android()]).then(function (values) {
console.log('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)');
module.exports.run = function() {
return Q.all([this.check_java(), this.check_android()])
.then(function(values) {
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
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[0]) {
throw new CordovaError('Requirements check failed for JDK 1.8 or greater');
}
if (!values[1]) {
throw new CordovaError('Requirements check failed for Android SDK! Android SDK was not detected.');
}
});
if (!values[1]) {
throw new CordovaError('Requirements check failed for Android SDK');
}
});
};
/**
* Object thar represents one of requirements for current platform.
* @param {String} id The unique identifier for this requirements.
@@ -393,7 +387,7 @@ var Requirement = function (id, name, version, installed) {
this.name = name;
this.installed = installed || false;
this.metadata = {
version: version
version: version,
};
};
@@ -403,7 +397,7 @@ var Requirement = function (id, name, version, installed) {
*
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
*/
module.exports.check_all = function () {
module.exports.check_all = function() {
var requirements = [
new Requirement('java', 'Java JDK'),
@@ -423,13 +417,15 @@ module.exports.check_all = function () {
return checkFns.reduce(function (promise, checkFn, idx) {
// Update each requirement with results
var requirement = requirements[idx];
return promise.then(checkFn).then(function (version) {
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 () {
}, Q())
.then(function () {
// When chain is completed, return requirements array to upstream API
return requirements;
});

View File

@@ -1,104 +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.
*/
let fs = require('fs');
let path = require('path');
let propertiesParser = require('properties-parser');
let 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',
// allow NDK to be used - required by Gradle 1.5 plugin
'android.useDeprecatedNdk': 'true'
// 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 => {
let value = this.gradleFile.get(key);
if (!value) {
events.emit('verbose', `[Gradle Properties] Appending configuration item: ${key}=${properties[key]}`);
this.gradleFile.set(key, properties[key]);
} else if (value !== properties[key]) {
events.emit('info', `[Gradle Properties] Detected Gradle property "${key}" with the value of "${value}", Cordova's recommended value is "${properties[key]}"`);
}
});
}
/**
* 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

@@ -19,7 +19,8 @@
under the License.
*/
var build = require('./build');
var Q = require('q'),
build = require('./build');
var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
@@ -31,16 +32,18 @@ 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 spawn('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;
});
@@ -49,19 +52,21 @@ 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 Promise.reject(new CordovaError('Failed to deploy to device, no devices found.'));
return Q.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 Promise.reject(new CordovaError('ERROR: Unable to find target \'' + target + '\'.'));
return Q.reject('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 };
});
});
@@ -72,39 +77,43 @@ module.exports.resolveTarget = function (target) {
* and launches it.
* Returns a promise.
*/
module.exports.install = function (target, buildResults) {
return Promise.resolve().then(function () {
if (target && typeof target === 'object') {
module.exports.install = function(target, buildResults) {
return Q().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, '../../app/src/main/AndroidManifest.xml'));
var manifest = new AndroidManifest(path.join(__dirname, '../../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

@@ -19,9 +19,10 @@
under the License.
*/
var android_versions = require('android-versions');
var retry = require('./retry');
var build = require('./build');
/* jshint sub:true */
var retry = require('./retry');
var build = require('./build');
var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
@@ -32,19 +33,20 @@ var shelljs = require('shelljs');
var android_sdk = require('./android_sdk');
var check_reqs = require('./check_reqs');
var os = require('os');
var fs = require('fs');
var Q = require('q');
var os = require('os');
var fs = require('fs');
var child_process = require('child_process');
// constants
var ONE_SECOND = 1000; // in milliseconds
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
var 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';
var NUM_INSTALL_RETRIES = 3;
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
var EXEC_KILL_SIGNAL = 'SIGKILL';
function forgivingWhichSync (cmd) {
function forgivingWhichSync(cmd) {
try {
return fs.realpathSync(shelljs.which(cmd));
} catch (e) {
@@ -53,7 +55,8 @@ function forgivingWhichSync (cmd) {
}
module.exports.list_images_using_avdmanager = function () {
return superspawn.spawn('avdmanager', ['list', 'avd']).then(function (output) {
return superspawn.spawn('avdmanager', ['list', 'avd'])
.then(function(output) {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
@@ -105,15 +108,16 @@ module.exports.list_images_using_avdmanager = 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 superspawn.spawn('android', ['list', 'avd']).then(function (output) {
module.exports.list_images_using_android = function() {
return superspawn.spawn('android', ['list', 'avd'])
.then(function(output) {
var response = output.split('\n');
var emulator_list = [];
for (var i = 1; i < response.length; i++) {
@@ -148,7 +152,7 @@ module.exports.list_images_using_android = 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;
@@ -166,30 +170,16 @@ module.exports.list_images_using_android = function () {
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;
module.exports.list_images = function() {
if (forgivingWhichSync('avdmanager')) {
return module.exports.list_images_using_avdmanager();
} else if (forgivingWhichSync('android')) {
return module.exports.list_images_using_android();
} else {
return Q().then(function() {
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
});
});
}
};
/**
@@ -197,19 +187,20 @@ module.exports.list_images = function () {
* 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];
var project_target = parseInt(check_reqs.get_target().replace('android-', ''));
var project_target = check_reqs.get_target().replace('android-', '');
for (var i in images) {
var target = images[i].target;
if (target && target.indexOf('API level') > -1) {
var num = parseInt(target.split('(API level ')[1].replace(')', ''));
if (num === project_target) {
if(target) {
var num = 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;
@@ -222,18 +213,19 @@ 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.
// TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function () {
return superspawn.spawn('android', ['list', 'targets'], { cwd: os.tmpdir() }).then(function (output) {
module.exports.list_targets = function() {
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
.then(function(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]);
}
}
@@ -248,8 +240,9 @@ module.exports.list_targets = function () {
module.exports.get_available_port = function () {
var self = this;
return self.list_started().then(function (emulators) {
for (var p = 5584; p >= 5554; p -= 2) {
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;
@@ -269,26 +262,28 @@ module.exports.get_available_port = function () {
*
* Returns a promise.
*/
module.exports.start = function (emulator_ID, boot_timeout) {
module.exports.start = function(emulator_ID, boot_timeout) {
var self = this;
return Promise.resolve().then(function () {
if (emulator_ID) return Promise.resolve(emulator_ID);
return Q().then(function() {
if (emulator_ID) return Q(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;
}
var androidCmd = check_reqs.getAbsoluteAndroidCmd();
return Promise.reject(new CordovaError('No emulator images (avds) found.\n' +
return Q.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) {
return self.get_available_port().then(function (port) {
}).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(shelljs.which('emulator'));
@@ -302,17 +297,20 @@ module.exports.start = function (emulator_ID, boot_timeout) {
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')); }
}).then(function(emulatorId) {
if (!emulatorId)
return Q.reject(new CordovaError('Failed to start emulator'));
// wait for emulator to boot up
//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) {
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 {
@@ -327,21 +325,20 @@ module.exports.start = function (emulator_ID, boot_timeout) {
* Waits for an emulator to boot on a given port.
* Returns this emulator's ID in a promise.
*/
module.exports.wait_for_emulator = function (port) {
module.exports.wait_for_emulator = function(port) {
var self = this;
return Promise.resolve().then(function () {
return Q().then(function() {
var emulator_id = 'emulator-' + port;
return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) {
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)) {
if (error && error.message &&
(error.message.indexOf('not found') > -1) ||
error.message.indexOf('device offline') > -1) {
// emulator not yet started, continue waiting
return self.wait_for_emulator(port);
} else {
@@ -349,7 +346,7 @@ module.exports.wait_for_emulator = function (port) {
throw error;
}
});
});
});
};
/*
@@ -357,9 +354,10 @@ module.exports.wait_for_emulator = function (port) {
* 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) {
return Adb.shell(emulator_id, 'ps')
.then(function(output) {
if (output.match(/android\.process\.acore/)) {
return true;
} else if (time_remaining === 0) {
@@ -367,13 +365,10 @@ module.exports.wait_for_boot = function (emulator_id, time_remaining) {
} else {
process.stdout.write('.');
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);
// 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);
});
}
});
@@ -384,10 +379,11 @@ 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) {
module.exports.create_image = function(name, target) {
console.log('Creating new avd named ' + name);
if (target) {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
return superspawn.spawn('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);
@@ -395,32 +391,35 @@ module.exports.create_image = function (name, target) {
} else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
// TODO: there's a more robust method for finding targets in android_sdk.js
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
.then(function() {
// TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Ensure you have targets available by running the "android" command');
return Promise.reject(new CordovaError());
}, function (error) {
return Q.reject();
}, function(error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(error);
});
}
};
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 Promise.reject(new CordovaError('No running Android emulators found, please start an emulator before deploying your project.'));
return Q.reject('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 Promise.reject(new CordovaError('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'));
return Q.reject('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};
});
});
};
@@ -431,17 +430,15 @@ 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;
// 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();
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
var pkgName = manifest.getPackageId();
// resolve the target emulator
return Promise.resolve().then(function () {
if (givenTarget && typeof givenTarget === 'object') {
return Q().then(function () {
if (givenTarget && typeof givenTarget == 'object') {
return givenTarget;
} else {
return module.exports.resolveTarget(givenTarget);
@@ -455,12 +452,13 @@ 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 Promise.resolve().then(function () {
return Q.when()
.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
};
@@ -471,12 +469,12 @@ module.exports.install = function (givenTarget, buildResults) {
// 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 new Promise(function (resolve, reject) {
child_process.exec(command, opts, function (err, stdout, stderr) {
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.
@@ -496,23 +494,27 @@ module.exports.install = function (givenTarget, buildResults) {
}
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 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');
});
});

View File

@@ -1,3 +0,0 @@
@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');
var args = process.argv;
var device = require('./device'),
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).catch(function (err) {
device.install(install_target).done(null, 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().catch(function (err) {
device.install().done(null, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -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');
var args = process.argv;
var emulator = require('./emulator'),
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).catch(function (err) {
emulator.install(install_target).done(null, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

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

View File

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

View File

@@ -18,7 +18,7 @@
@ECHO OFF
SET script_path="%~dp0list-emulator-images"
IF EXIST %script_path% (
node %script_path% %*
node "%script_path%" %*
) ELSE (
ECHO.
ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2

View File

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

View File

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

@@ -19,28 +19,28 @@
under the License.
*/
var path = require('path');
var os = require('os');
var Q = require('q');
var child_process = require('child_process');
var ROOT = path.join(__dirname, '..', '..');
var path = require('path'),
os = require('os'),
Q = require('q'),
child_process = require('child_process'),
ROOT = path.join(__dirname, '..', '..');
/*
* Starts running logcat in the shell.
* Returns a promise.
*/
module.exports.run = function () {
module.exports.run = function() {
var d = Q.defer();
var adb = child_process.spawn('adb', ['logcat'], { cwd: os.tmpdir() });
var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
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) {
adb.on('close', function(code) {
if (code > 0) {
d.reject('Failed to run logcat command.');
} else d.resolve();
@@ -49,7 +49,7 @@ module.exports.run = function () {
return d.promise;
};
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,7 +20,7 @@
buildscript {
repositories {
google()
mavenCentral()
jcenter()
}
@@ -36,7 +36,7 @@ buildscript {
apply plugin: 'com.android.library'
dependencies {
implementation fileTree(dir: 'libs', include: '*.jar')
compile fileTree(dir: 'libs', include: '*.jar')
debugCompile project(path: ":CordovaLib", configuration: "debug")
releaseCompile project(path: ":CordovaLib", configuration: "release")
}
@@ -44,6 +44,7 @@ dependencies {
android {
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
publishNonDefault true
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6

View File

@@ -1,4 +1,7 @@
/*
*
* 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
@@ -14,6 +17,8 @@
*
*/
/* jshint unused: vars */
var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
@@ -21,12 +26,16 @@ var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
var handlers = {
'source-file': {
install: function (obj, plugin, project, options) {
'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);
var dest = path.join(obj.targetDir, path.basename(obj.src));
if(options && options.android_studio === true) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
}
if (options && options.force) {
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
@@ -34,40 +43,42 @@ var handlers = {
copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
}
},
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(project.projectDir, dest);
uninstall:function(obj, plugin, project, options) {
var dest = path.join(obj.targetDir, path.basename(obj.src));
if(options && options.android_studio === true) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
}
deleteJava(project.projectDir, dest);
}
},
'lib-file': {
install: function (obj, plugin, project, options) {
var dest = path.join('app/libs', path.basename(obj.src));
'lib-file':{
install:function(obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src));
if(options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src));
}
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
},
uninstall: function (obj, plugin, project, options) {
var dest = path.join('app/libs', path.basename(obj.src));
uninstall:function(obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src));
if(options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src));
}
removeFile(project.projectDir, dest);
}
},
'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));
'resource-file':{
install:function(obj, plugin, project, options) {
copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
},
uninstall: function (obj, plugin, project, options) {
var dest = path.join('app', 'src', 'main', obj.target);
removeFile(project.projectDir, dest);
uninstall:function(obj, plugin, project, options) {
removeFile(project.projectDir, path.normalize(obj.target));
}
},
'framework': {
install: function (obj, plugin, project, options) {
install:function(obj, plugin, project, options) {
var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
@@ -84,15 +95,15 @@ var handlers = {
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(generateAttributeError('src', 'framework', plugin.id));
@@ -114,17 +125,17 @@ 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(generateAttributeError('src', 'asset', plugin.id));
}
@@ -138,7 +149,7 @@ var handlers = {
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(generateAttributeError('target', 'asset', plugin.id));
@@ -156,7 +167,7 @@ var handlers = {
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.basename(obj.src, path.extname(obj.src)));
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
@@ -195,7 +206,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;
}
@@ -210,19 +221,21 @@ 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('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
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('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
if (dest.indexOf(project_dir) !== 0)
throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project');
shell.mkdir('-p', path.dirname(dest));
if (link) {
symlinkFileOrDirTree(src, dest);
} else if (fs.statSync(src).isDirectory()) {
// XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
shell.cp('-Rf', src + '/*', dest);
shell.cp('-Rf', src+'/*', dest);
} else {
shell.cp('-f', src, dest);
}
@@ -231,22 +244,24 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// 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);
}
function symlinkFileOrDirTree (src, dest) {
function symlinkFileOrDirTree(src, dest) {
if (fs.existsSync(dest)) {
shell.rm('-Rf', dest);
}
if (fs.statSync(src).isDirectory()) {
shell.mkdir('-p', dest);
fs.readdirSync(src).forEach(function (entry) {
fs.readdirSync(src).forEach(function(entry) {
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
});
} else {
}
else {
fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
}
}
@@ -277,8 +292,8 @@ function removeFileAndParents (baseDir, destFile, stopper) {
// 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 {
@@ -288,47 +303,6 @@ function removeFileAndParents (baseDir, destFile, stopper) {
}
}
function generateAttributeError (attribute, element, id) {
function generateAttributeError(attribute, element, id) {
return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
}
function getInstallDestination (obj) {
var APP_MAIN_PREFIX = 'app/src/main';
var PATH_SEPARATOR = '/';
var PATH_SEP_MATCH = '\\' + PATH_SEPARATOR;
var PATH_SEP_OR_EOL_MATCH = '(\\' + PATH_SEPARATOR + '|$)';
var appReg = new RegExp('^app' + PATH_SEP_OR_EOL_MATCH);
var libsReg = new RegExp('^libs' + PATH_SEP_OR_EOL_MATCH);
var srcReg = new RegExp('^src' + PATH_SEP_OR_EOL_MATCH);
var srcMainReg = new RegExp('^src' + PATH_SEP_MATCH + 'main' + PATH_SEP_OR_EOL_MATCH);
if (appReg.test(obj.targetDir)) {
// If any source file is using the new app directory structure,
// don't penalize it
return path.join(obj.targetDir, path.basename(obj.src));
} else {
// Plugin using deprecated target directory structure (GH-580)
if (obj.src.endsWith('.java')) {
return path.join(APP_MAIN_PREFIX, 'java', obj.targetDir.replace(srcReg, ''),
path.basename(obj.src));
} else if (obj.src.endsWith('.aidl')) {
return path.join(APP_MAIN_PREFIX, 'aidl', obj.targetDir.replace(srcReg, ''),
path.basename(obj.src));
} else if (libsReg.test(obj.targetDir)) {
if (obj.src.endsWith('.so')) {
return path.join(APP_MAIN_PREFIX, 'jniLibs', obj.targetDir.replace(libsReg, ''),
path.basename(obj.src));
} else {
return path.join('app', obj.targetDir, path.basename(obj.src));
}
} else if (srcMainReg.test(obj.targetDir)) {
return path.join('app', obj.targetDir, path.basename(obj.src));
}
// For all other source files not using the new app directory structure,
// add 'app/src/main' to the targetDir
return path.join(APP_MAIN_PREFIX, obj.targetDir, path.basename(obj.src));
}
}

View File

@@ -16,7 +16,6 @@
specific language governing permissions and limitations
under the License.
*/
/* eslint no-useless-escape: 0 */
var Q = require('q');
var fs = require('fs');
@@ -24,7 +23,6 @@ var path = require('path');
var shell = require('shelljs');
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;
@@ -33,8 +31,6 @@ var PlatformJson = require('cordova-common').PlatformJson;
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
const GradlePropertiesParser = require('./config/GradlePropertiesParser');
module.exports.prepare = function (cordovaProject, options) {
var self = this;
@@ -43,24 +39,18 @@ module.exports.prepare = function (cordovaProject, options) {
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
// Get the min SDK version from config.xml
const minSdkVersion = this._config.getPreference('android-minSdkVersion', 'android');
let gradlePropertiesUserConfig = {};
if (minSdkVersion) gradlePropertiesUserConfig.cdvMinSdkVersion = minSdkVersion;
let gradlePropertiesParser = new GradlePropertiesParser(this.locations.root);
gradlePropertiesParser.configure(gradlePropertiesUserConfig);
// Update own www dir with project's www assets and plugins' assets and js-files
return Q.when(updateWww(cordovaProject, this.locations)).then(function () {
return Q.when(updateWww(cordovaProject, this.locations))
.then(function () {
// update project according to config.xml changes.
return updateProjectAccordingTo(self._config, self.locations);
}).then(function () {
})
.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 () {
})
.then(function () {
events.emit('verbose', 'Prepared android project successfully');
});
};
@@ -101,7 +91,7 @@ module.exports.clean = function (options) {
* represents current project's configuration. When returned, the
* configuration is already dumped to appropriate config.xml file.
*/
function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
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
@@ -116,7 +106,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
// 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;
@@ -125,7 +115,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
/**
* Logs all file operations via the verbose event stream, indented.
*/
function logFileOp (message) {
function logFileOp(message) {
events.emit('verbose', ' ' + message);
}
@@ -138,7 +128,7 @@ function logFileOp (message) {
* @param {Object} destinations An object that contains destination
* paths for www files.
*/
function updateWww (cordovaProject, destinations) {
function updateWww(cordovaProject, destinations) {
var sourceDirs = [
path.relative(cordovaProject.root, cordovaProject.locations.www),
path.relative(cordovaProject.root, destinations.platformWww)
@@ -161,7 +151,7 @@ function updateWww (cordovaProject, destinations) {
/**
* Cleans all files from the platform 'www' directory.
*/
function cleanWww (projectRoot, locations) {
function cleanWww(projectRoot, locations) {
var targetDir = path.relative(projectRoot, locations.www);
events.emit('verbose', 'Cleaning ' + targetDir);
@@ -177,26 +167,19 @@ function cleanWww (projectRoot, locations) {
* 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 strings = xmlHelpers.parseElementtreeSync(locations.strings);
var name = platformConfig.name();
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
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');
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 androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
var pkg = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
var manifest = new AndroidManifest(locations.manifest);
var manifestId = manifest.getPackageId();
var orig_pkg = manifest.getPackageId();
manifest.getActivity()
.setOrientation(platformConfig.getPreference('orientation'))
@@ -204,41 +187,36 @@ function updateProjectAccordingTo (platformConfig, locations) {
manifest.setVersionName(platformConfig.version())
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
.setPackageId(androidPkgName)
.setPackageId(pkg)
.setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
.setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
.setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
.write();
// Java file paths shouldn't be hard coded
var javaPattern = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'), '*.java');
var java_files = shell.ls(javaPattern).filter(function (f) {
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);
});
if (java_files.length === 0) {
throw new CordovaError('No Java files found that extend CordovaActivity.');
} else if (java_files.length > 1) {
} 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, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(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 ' + androidPkgName + ';', java_files[0]).to(destFile);
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile);
shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile);
events.emit('verbose', 'Wrote out Android package name "' + pkg + '" to ' + destFile);
var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin() ?
manifestId.toUpperCase() !== androidPkgName.toUpperCase() :
manifestId !== androidPkgName;
if (removeOrigPkg) {
if (orig_pkg !== pkg) {
// If package was name changed we need to remove old java with main activity
shell.rm('-Rf', java_files[0]);
shell.rm('-Rf',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 {
@@ -251,7 +229,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]) {
@@ -268,7 +246,7 @@ function default_versionCode (version) {
return versionCode;
}
function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
function getImageResourcePath(resourcesDir, type, density, name, sourceName) {
if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png');
}
@@ -276,15 +254,7 @@ function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
return resourcePath;
}
function getAdaptiveImageResourcePath (resourcesDir, type, density, name, sourceName) {
if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png');
}
var resourcePath = path.join(resourcesDir, (density ? type + '-' + density + '-v26' : type), name);
return resourcePath;
}
function updateSplashes (cordovaProject, platformResourcesDir) {
function updateSplashes(cordovaProject, platformResourcesDir) {
var resources = cordovaProject.projectConfig.getSplashScreens('android');
// if there are "splash" elements in config.xml
@@ -300,7 +270,7 @@ function updateSplashes (cordovaProject, platformResourcesDir) {
if (!resource.density) {
return;
}
if (resource.density === 'mdpi') {
if (resource.density == 'mdpi') {
hadMdpi = true;
}
var targetPath = getImageResourcePath(
@@ -320,7 +290,7 @@ function updateSplashes (cordovaProject, platformResourcesDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) {
var resources = projectConfig.getSplashScreens('android');
if (resources.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png');
@@ -332,198 +302,21 @@ function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
}
}
function updateIcons (cordovaProject, platformResourcesDir) {
let icons = cordovaProject.projectConfig.getIcons('android');
function updateIcons(cordovaProject, platformResourcesDir) {
var icons = cordovaProject.projectConfig.getIcons('android');
// Skip if there are no app defined icons in config.xml
// if there are icon elements in config.xml
if (icons.length === 0) {
events.emit('verbose', 'This app does not have launcher icons defined');
return;
}
// 1. loop icons determin if there is an error in the setup.
// 2. during initial loop, also setup for legacy support.
let errorMissingAttributes = [];
let 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 resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'icon.png');
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;
}
}
});
let 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')
);
let 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) {
let android_icons = preparedIcons.android_icons;
let 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 (let 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.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>`;
let 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) {
let android_icons = preparedIcons.android_icons;
let 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) {
var android_icons = {};
var default_icon;
// http://developer.android.com/design/style/iconography.html
const SIZE_TO_DENSITY_MAP = {
var sizeToDensityMap = {
36: 'ldpi',
48: 'mdpi',
72: 'hdpi',
@@ -531,15 +324,11 @@ function prepareIcons (icons) {
144: 'xxhdpi',
192: 'xxxhdpi'
};
let 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 || SIZE_TO_DENSITY_MAP[icon_size];
var density = icon.density || sizeToDensityMap[icon_size];
if (!density) {
// invalid icon defition ( or unsupported size)
return;
@@ -552,37 +341,15 @@ function prepareIcons (icons) {
};
// 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) {
let found = {};
let 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) + '.');
events.emit('verbose', 'Found extra default icon: ' + icon.src + ' (ignoring in favor of ' + default_icon.src + ')');
} else {
default_icon = icon;
}
@@ -591,50 +358,53 @@ function prepareIcons (icons) {
}
}
return {
android_icons: android_icons,
default_icon: default_icon
};
}
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;
// The source paths for icons and splashes are relative to
// project's config.xml location, so we use it as base path.
for (var density in android_icons) {
var targetPath = getImageResourcePath(
platformResourcesDir, 'mipmap', density, 'icon.png', path.basename(android_icons[density].src));
resourceMap[targetPath] = android_icons[density].src;
}
let 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')
);
// There's no "default" drawable, so assume default == mdpi.
if (default_icon && !android_icons.mdpi) {
var defaultTargetPath = getImageResourcePath(
platformResourcesDir, 'mipmap', 'mdpi', 'icon.png', path.basename(default_icon.src));
resourceMap[defaultTargetPath] = default_icon.src;
}
events.emit('verbose', 'Cleaning icons at ' + platformResourcesDir);
events.emit('verbose', 'Updating icons at ' + platformResourcesDir);
FileUpdater.updatePaths(
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
// No source paths are specified in the map, so updatePaths() will delete the target files.
FileUpdater.updatePaths(resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
function cleanIcons(projectRoot, projectConfig, platformResourcesDir) {
var icons = projectConfig.getIcons('android');
if (icons.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'icon.png');
events.emit('verbose', 'Cleaning icons at ' + platformResourcesDir);
// No source paths are specified in the map, so updatePaths() will delete the target files.
FileUpdater.updatePaths(
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
}
}
/**
* Gets a map containing resources of a specified name from all drawable folders in a directory.
*/
function mapImageResources (rootDir, subDir, type, resourceName) {
function mapImageResources(rootDir, subDir, type, resourceName) {
var pathMap = {};
shell.ls(path.join(rootDir, subDir, type + '-*')).forEach(function (drawableFolder) {
shell.ls(path.join(rootDir, subDir, type + '-*'))
.forEach(function (drawableFolder) {
var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
pathMap[imagePath] = null;
});
return pathMap;
}
function updateFileResources (cordovaProject, platformDir) {
function updateFileResources(cordovaProject, platformDir) {
var files = cordovaProject.projectConfig.getFileResources('android');
// if there are resource-file elements in config.xml
@@ -644,7 +414,7 @@ function updateFileResources (cordovaProject, platformDir) {
}
var resourceMap = {};
files.forEach(function (res) {
files.forEach(function(res) {
var targetPath = path.join(platformDir, res.target);
resourceMap[targetPath] = res.src;
});
@@ -654,20 +424,20 @@ function updateFileResources (cordovaProject, platformDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
}
function cleanFileResources (projectRoot, projectConfig, platformDir) {
var files = projectConfig.getFileResources('android', true);
function cleanFileResources(projectRoot, projectConfig, platformDir) {
var files = projectConfig.getFileResources('android');
if (files.length > 0) {
events.emit('verbose', 'Cleaning resource files at ' + platformDir);
var resourceMap = {};
files.forEach(function (res) {
files.forEach(function(res) {
var filePath = path.join(platformDir, res.target);
resourceMap[filePath] = null;
});
FileUpdater.updatePaths(
resourceMap, {
rootDir: projectRoot, all: true }, logFileOp);
resourceMap, { rootDir: projectRoot, all: true}, logFileOp);
}
}
@@ -682,7 +452,7 @@ function cleanFileResources (projectRoot, projectConfig, platformDir) {
* 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

View File

@@ -19,6 +19,8 @@
under the License.
*/
/* jshint node: true */
'use strict';
var events = require('cordova-common').events;
@@ -27,37 +29,39 @@ 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} attemptsLeft - The number of times to retry the passed call.
* @arg {Number} attemts_left - 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 (attemptsLeft, promiseFunction) {
module.exports.retryPromise = function (attemts_left, promiseFunction) {
// NOTE:
// get all trailing arguments, by skipping the first two (attemptsLeft and
// get all trailing arguments, by skipping the first two (attemts_left 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) {
attemptsLeft -= 1;
function onRejected(error) {
if (attemptsLeft < 1) {
attemts_left -= 1;
if (attemts_left < 1) {
throw error;
}
events.emit('verbose', 'A retried call failed. Retrying ' + attemptsLeft + ' more time(s).');
events.emit('verbose', 'A retried call failed. Retrying ' + attemts_left + ' more time(s).');
// retry call self again with the same arguments, except attemptsLeft is now lower
var fullArguments = [attemptsLeft, promiseFunction].concat(promiseFunctionArguments);
// retry call self again with the same arguments, except attemts_left is now lower
var fullArguments = [attemts_left, promiseFunction].concat(promiseFunctionArguments);
return module.exports.retryPromise.apply(undefined, fullArguments);
}
);

View File

@@ -19,13 +19,16 @@
under the License.
*/
var path = require('path');
var emulator = require('./emulator');
var device = require('./device');
var Q = require('q');
var events = require('cordova-common').events;
/* jshint loopfunc:true */
function getInstallTarget (runOptions) {
var path = require('path'),
build = require('./build'),
emulator = require('./emulator'),
device = require('./device'),
Q = require('q'),
events = require('cordova-common').events;
function getInstallTarget(runOptions) {
var install_target;
if (runOptions.target) {
install_target = runOptions.target;
@@ -48,16 +51,17 @@ function getInstallTarget (runOptions) {
*
* @return {Promise}
*/
module.exports.run = function (runOptions) {
runOptions = runOptions || {};
module.exports.run = function(runOptions) {
var self = this;
var install_target = getInstallTarget(runOptions);
return Q().then(function () {
return Q()
.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) {
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
install_target = device_list[0];
@@ -67,31 +71,36 @@ module.exports.run = function (runOptions) {
}
});
}
}).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);
});
}
@@ -100,24 +109,25 @@ module.exports.run = function (runOptions) {
});
});
});
}).then(function (resolvedTarget) {
return new Promise((resolve) => {
const builder = require('./builders/builders').getBuilder();
const buildOptions = require('./build').parseBuildOptions(runOptions, null, self.root);
resolve(builder.fetchBuildResults(buildOptions.buildType, buildOptions.arch));
}).then(function (buildResults) {
if (resolvedTarget && resolvedTarget.isEmulator) {
return emulator.wait_for_boot(resolvedTarget.target).then(function () {
}).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 () {
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,20 +19,21 @@
under the License.
*/
var emulator = require('./emulator');
var args = process.argv;
var emulator = require('./emulator'),
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).catch(function (err) {
emulator.start(install_target).done(null, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

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

View File

@@ -19,17 +19,17 @@
under the License.
*/
var log = require('./lib/log');
var reqs = require('./lib/check_reqs');
var args = process.argv;
var log = require('./lib/log'),
reqs = require('./lib/check_reqs'),
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().done(function() {
return log.run();
}, function (err) {
}, function(err) {
console.error('ERROR: ' + err);
process.exit(2);
});

View File

@@ -24,23 +24,22 @@ 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,
'verbose' : Boolean,
'silent' : Boolean,
'debug' : Boolean,
'release' : Boolean,
'nobuild': Boolean,
'buildConfig': path,
'archs': String,
'device': Boolean,
'buildConfig' : path,
'archs' : String,
'device' : Boolean,
'emulator': Boolean,
'target': String
}, { 'd': '--verbose' });
'target' : String
}, { 'd' : '--verbose' });
// Make runOptions compatible with PlatformApi run method spec
runOpts.argv = runOpts.argv.remain;
@@ -48,7 +47,7 @@ runOpts.argv = runOpts.argv.remain;
require('./loggingHelper').adjustLoggerLevel(runOpts);
new Api().run(runOpts)
.catch(function (err) {
console.error(err, err.stack);
process.exit(2);
});
.catch(function(err) {
console.error(err, err.stack);
process.exit(2);
});

View File

@@ -20,7 +20,7 @@
*/
// Coho updates this line:
var VERSION = "8.0.0";
var VERSION = "6.2.3";
module.exports.version = VERSION;

View File

@@ -30,7 +30,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<application android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
<application android:icon="@mipmap/icon" android:label="@string/app_name"
android:hardwareAccelerated="true" android:supportsRtl="true">
<activity android:name="__ACTIVITY__"
android:label="@string/activity_name"
@@ -45,5 +45,5 @@
</activity>
</application>
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="__APILEVEL__"/>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="__APILEVEL__"/>
</manifest>

View File

@@ -1,331 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
apply plugin: 'com.android.application'
buildscript {
repositories {
mavenCentral()
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0'
}
}
// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
repositories {
mavenCentral()
jcenter()
}
}
task wrapper(type: Wrapper) {
gradleVersion = '4.10.3'
}
// 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
}
// 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.
if (ext.cdvCompileSdkVersion == null) {
ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
//ext.cdvCompileSdkVersion = project.ext.defaultCompileSdkVersion
}
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();
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? defaultMinSdkVersion : 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 {
doLast {
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
println('cdvVersionCode=' + cdvVersionCode)
println('cdvVersionCodeForceAbiDigit=' + cdvVersionCodeForceAbiDigit)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
println('cdvBuildArch=' + cdvBuildArch)
println('computedVersionCode=' + android.defaultConfig.versionCode)
android.productFlavors.each { flavor ->
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
}
}
}
android {
defaultConfig {
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId privateHelpers.extractStringFromManifest("package")
if (cdvMinSdkVersion != null) {
minSdkVersion cdvMinSdkVersion
}
}
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)
}
}
/*
* 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')
// 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()
}

File diff suppressed because it is too large Load Diff

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,52 +1,311 @@
/* 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.
*/
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply plugin: 'com.android.application'
buildscript {
repositories {
google()
mavenCentral()
jcenter()
}
// Switch the Android Gradle plugin version requirement depending on the
// installed version of Gradle. This dependency is documented at
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
// and https://issues.apache.org/jira/browse/CB-8143
dependencies {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.android.tools.build:gradle:3.3.0'
classpath 'com.android.tools.build:gradle:2.2.3'
}
}
// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
repositories {
google()
mavenCentral();
jcenter()
}
}
//This replaces project.properties w.r.t. build settings
project.ext {
defaultBuildToolsVersion="28.0.3" //String
defaultMinSdkVersion=19 //Integer - Minimum requirement is Android 4.4
defaultTargetSdkVersion=28 //Integer - We ALWAYS target the latest by default
defaultCompileSdkVersion=28 //Integer - We ALWAYS compile with the latest by default
task wrapper(type: Wrapper) {
gradleVersion = '2.14.1'
}
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
// Refer to: http://www.gradle.org/docs/current/userguide/tutorial_this_and_that.html
ext {
apply from: 'CordovaLib/cordova.gradle'
// The value for android.compileSdkVersion.
if (!project.hasProperty('cdvCompileSdkVersion')) {
cdvCompileSdkVersion = null;
}
// The value for android.buildToolsVersion.
if (!project.hasProperty('cdvBuildToolsVersion')) {
cdvBuildToolsVersion = null;
}
// Sets the versionCode to the given value.
if (!project.hasProperty('cdvVersionCode')) {
cdvVersionCode = null
}
// Sets the minSdkVersion to the given value.
if (!project.hasProperty('cdvMinSdkVersion')) {
cdvMinSdkVersion = null
}
// Whether to build architecture-specific APKs.
if (!project.hasProperty('cdvBuildMultipleApks')) {
cdvBuildMultipleApks = null
}
// .properties files to use for release signing.
if (!project.hasProperty('cdvReleaseSigningPropertiesFile')) {
cdvReleaseSigningPropertiesFile = null
}
// .properties files to use for debug signing.
if (!project.hasProperty('cdvDebugSigningPropertiesFile')) {
cdvDebugSigningPropertiesFile = null
}
// Set by build.js script.
if (!project.hasProperty('cdvBuildArch')) {
cdvBuildArch = null
}
// Plugin gradle extensions can append to this to have code run at the end.
cdvPluginPostBuildExtras = []
}
// PLUGIN GRADLE EXTENSIONS START
// PLUGIN GRADLE EXTENSIONS END
def hasBuildExtras = file('build-extras.gradle').exists()
if (hasBuildExtras) {
apply from: 'build-extras.gradle'
}
// Set property defaults after extension .gradle files.
if (ext.cdvCompileSdkVersion == null) {
ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
}
if (ext.cdvBuildToolsVersion == null) {
ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
}
if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) {
ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties'
}
if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) {
ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties'
}
// Cast to appropriate types.
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : Integer.parseInt('' + cdvMinSdkVersion)
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
def computeBuildTargetName(debugBuild) {
def ret = 'assemble'
if (cdvBuildMultipleApks && cdvBuildArch) {
def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
}
return ret + (debugBuild ? 'Debug' : 'Release')
}
// Make cdvBuild a task that depends on the debug/arch-sepecific task.
task cdvBuildDebug
cdvBuildDebug.dependsOn {
return computeBuildTargetName(true)
}
task cdvBuildRelease
cdvBuildRelease.dependsOn {
return computeBuildTargetName(false)
}
task cdvPrintProps << {
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
println('cdvVersionCode=' + cdvVersionCode)
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
println('cdvBuildArch=' + cdvBuildArch)
println('computedVersionCode=' + android.defaultConfig.versionCode)
android.productFlavors.each { flavor ->
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
}
}
task clean(type: Delete) {
delete rootProject.buildDir
android {
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
defaultConfig {
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
applicationId privateHelpers.extractStringFromManifest("package")
if (cdvMinSdkVersion != null) {
minSdkVersion cdvMinSdkVersion
}
}
lintOptions {
abortOnError false;
}
compileSdkVersion cdvCompileSdkVersion
buildToolsVersion cdvBuildToolsVersion
if (Boolean.valueOf(cdvBuildMultipleApks)) {
productFlavors {
armv7 {
versionCode defaultConfig.versionCode*10 + 2
ndk {
abiFilters "armeabi-v7a", ""
}
}
x86 {
versionCode defaultConfig.versionCode*10 + 4
ndk {
abiFilters "x86", ""
}
}
all {
ndk {
abiFilters "all", ""
}
}
}
}
/*
ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO!
else if (!cdvVersionCode) {
def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion")
// Vary versionCode by the two most common API levels:
// 14 is ICS, which is the lowest API level for many apps.
// 20 is Lollipop, which is the lowest API level for the updatable system webview.
if (minSdkVersion >= 20) {
defaultConfig.versionCode += 9
} else if (minSdkVersion >= 14) {
defaultConfig.versionCode += 8
}
}
*/
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
}
if (cdvReleaseSigningPropertiesFile) {
signingConfigs {
release {
// These must be set or Gradle will complain (even if they are overridden).
keyAlias = ""
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
storeFile = null
storePassword = "__unset"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
}
if (cdvDebugSigningPropertiesFile) {
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
}
}
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
// SUB-PROJECT DEPENDENCIES START
// SUB-PROJECT DEPENDENCIES END
}
def promptForReleaseKeyPassword() {
if (!cdvReleaseSigningPropertiesFile) {
return;
}
if ('__unset'.equals(android.signingConfigs.release.storePassword)) {
android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ')
}
if ('__unset'.equals(android.signingConfigs.release.keyPassword)) {
android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: ');
}
}
gradle.taskGraph.whenReady { taskGraph ->
taskGraph.getAllTasks().each() { task ->
if (task.name == 'validateReleaseSigning' || task.name == 'validateSigningRelease') {
promptForReleaseKeyPassword()
}
}
}
def addSigningProps(propsFilePath, signingConfig) {
def propsFile = file(propsFilePath)
def props = new Properties()
propsFile.withReader { reader ->
props.load(reader)
}
def storeFile = new File(props.get('key.store') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'storeFile'))
if (!storeFile.isAbsolute()) {
storeFile = RelativePath.parse(true, storeFile.toString()).getFile(propsFile.getParentFile())
}
if (!storeFile.exists()) {
throw new FileNotFoundException('Keystore file does not exist: ' + storeFile.getAbsolutePath())
}
signingConfig.keyAlias = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias')
signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword))
signingConfig.storeFile = storeFile
signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword))
def storeType = props.get('storeType', props.get('key.store.type', ''))
if (!storeType) {
def filename = storeFile.getName().toLowerCase();
if (filename.endsWith('.p12') || filename.endsWith('.pfx')) {
storeType = 'pkcs12'
} else {
storeType = signingConfig.storeType // "jks"
}
}
signingConfig.storeType = storeType
}
for (def func : cdvPluginPostBuildExtras) {
func()
}
// This can be defined within build-extras.gradle as:
// ext.postBuildExtras = { ... code here ... }
if (hasProperty('postBuildExtras')) {
postBuildExtras()
}

View File

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

View File

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

View File

@@ -1,13 +1,15 @@
# 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 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 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: 63 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 493 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

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

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