Compare commits

..

10 Commits

Author SHA1 Message Date
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
197 changed files with 4634 additions and 7649 deletions

View File

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

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

36
.gitignore vendored
View File

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

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

View File

@@ -36,7 +36,7 @@ at the core, applications written with web technology: HTML, CSS and JavaScript.
## Requires ## Requires
- Java JDK 1.8 or greater - Java JDK 1.6 or greater
- Android SDK [http://developer.android.com](http://developer.android.com) - Android SDK [http://developer.android.com](http://developer.android.com)
@@ -62,9 +62,3 @@ These commands live in a generated Cordova Android project. Any interactions wit
1. Create a project 1. Create a project
2. Import it via "Non-Android Studio 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,64 +20,6 @@
--> -->
## Release Notes for Cordova (Android) ## ## Release Notes for Cordova (Android) ##
### 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`
* [CB-12640](https://issues.apache.org/jira/browse/CB-12640) support for android sdk tools 26.0.1.
### 6.2.2 (Apr 24, 2017)
* [CB-12697](https://issues.apache.org/jira/browse/CB-12697) Updated checked-in `node_modules`
### 6.2.1 (Apr 02, 2017) ### 6.2.1 (Apr 02, 2017)
* [CB-12621](https://issues.apache.org/jira/browse/CB-12621) reverted elementtree dep to 0.1.6 * [CB-12621](https://issues.apache.org/jira/browse/CB-12621) reverted elementtree dep to 0.1.6

View File

@@ -1 +1 @@
7.0.0 6.2.2

View File

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

View File

@@ -18,7 +18,7 @@
@ECHO OFF @ECHO OFF
SET script_path="%~dp0android_sdk_version" SET script_path="%~dp0android_sdk_version"
IF EXIST %script_path% ( IF EXIST %script_path% (
node %script_path% %* node "%script_path%" %*
) ELSE ( ) ELSE (
ECHO. ECHO.
ECHO ERROR: Could not find 'android_sdk_version' script in 'bin' folder, aborting...>&2 ECHO ERROR: Could not find 'android_sdk_version' script in 'bin' folder, aborting...>&2

View File

@@ -18,7 +18,7 @@
@ECHO OFF @ECHO OFF
SET script_path="%~dp0check_reqs" SET script_path="%~dp0check_reqs"
IF EXIST %script_path% ( IF EXIST %script_path% (
node %script_path% %* node "%script_path%" %*
) ELSE ( ) ELSE (
ECHO. ECHO.
ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2 ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2

View File

@@ -19,49 +19,33 @@
under the License. under the License.
*/ */
var shell = require('shelljs'); var shell = require('shelljs'),
var Q = require('q'); Q = require('q'),
var path = require('path'); path = require('path'),
var fs = require('fs'); fs = require('fs'),
var check_reqs = require('./../templates/cordova/lib/check_reqs'); check_reqs = require('./../templates/cordova/lib/check_reqs'),
var ROOT = path.join(__dirname, '..', '..'); ROOT = path.join(__dirname, '..', '..');
var MIN_SDK_VERSION = 16;
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var AndroidManifest = require('../templates/cordova/lib/AndroidManifest'); var AndroidManifest = require('../templates/cordova/lib/AndroidManifest');
// Export all helper functions, and make sure internally within this module, we function setShellFatal(value, func) {
// 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) {
var oldVal = shell.config.fatal; var oldVal = shell.config.fatal;
shell.config.fatal = value; shell.config.fatal = value;
func(); func();
shell.config.fatal = oldVal; shell.config.fatal = oldVal;
} }
function getFrameworkDir (projectPath, shared) { function getFrameworkDir(projectPath, shared) {
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib'); 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 nestedCordovaLibPath = getFrameworkDir(projectPath, false);
var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js'); var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js');
var app_path = path.join(projectPath, 'app', 'src', 'main'); shell.cp('-f', srcCordovaJsPath, path.join(projectPath, 'assets', 'www', 'cordova.js'));
if (isLegacy) {
app_path = projectPath;
}
shell.cp('-f', srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
// Copy the cordova.js file to platforms/<platform>/platform_www/ // 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 // 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')); shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www'));
// Don't fail if there are no old jars. // Don't fail if there are no old jars.
exports.setShellFatal(false, function () { setShellFatal(false, function() {
shell.ls(path.join(app_path, 'libs', 'cordova-*.jar')).forEach(function (oldJar) { shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function(oldJar) {
console.log('Deleting ' + oldJar); console.log('Deleting ' + oldJar);
shell.rm('-f', oldJar); shell.rm('-f', oldJar);
}); });
@@ -106,7 +90,7 @@ function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
} }
} }
function extractSubProjectPaths (data) { function extractSubProjectPaths(data) {
var ret = {}; var ret = {};
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg; var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
var m; var m;
@@ -116,7 +100,7 @@ function extractSubProjectPaths (data) {
return Object.keys(ret); return Object.keys(ret);
} }
function writeProjectProperties (projectPath, target_api) { function writeProjectProperties(projectPath, target_api) {
var dstPath = path.join(projectPath, 'project.properties'); var dstPath = path.join(projectPath, 'project.properties');
var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties'); var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties');
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath; var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
@@ -124,10 +108,11 @@ function writeProjectProperties (projectPath, target_api) {
var data = fs.readFileSync(srcPath, 'utf8'); var data = fs.readFileSync(srcPath, 'utf8');
data = data.replace(/^target=.*/m, 'target=' + target_api); data = data.replace(/^target=.*/m, 'target=' + target_api);
var subProjects = extractSubProjectPaths(data); var subProjects = extractSubProjectPaths(data);
subProjects = subProjects.filter(function (p) { subProjects = subProjects.filter(function(p) {
return !(/^CordovaLib$/m.exec(p) || return !(/^CordovaLib$/m.exec(p) ||
/[\\/]cordova-android[\\/]framework$/m.exec(p) || /[\\\/]cordova-android[\\\/]framework$/m.exec(p) ||
/^(\.\.[\\/])+framework$/m.exec(p)); /^(\.\.[\\\/])+framework$/m.exec(p)
);
}); });
subProjects.unshift('CordovaLib'); subProjects.unshift('CordovaLib');
data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, ''); data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
@@ -135,32 +120,24 @@ function writeProjectProperties (projectPath, target_api) {
data += '\n'; data += '\n';
} }
for (var i = 0; i < subProjects.length; ++i) { 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); fs.writeFileSync(dstPath, data);
} }
// This makes no sense, what if you're building with a different build system? function prepBuildFiles(projectPath) {
function prepBuildFiles (projectPath, builder) {
var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders')); var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders'));
buildModule.getBuilder(builder).prepBuildFiles(); buildModule.getBuilder('gradle').prepBuildFiles();
} }
function copyBuildRules (projectPath, isLegacy) { function copyBuildRules(projectPath) {
var srcDir = path.join(ROOT, 'bin', 'templates', 'project'); var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
if (isLegacy) { shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
// The project's build.gradle is identical to the earlier build.gradle, so it should still work shell.cp('-f', path.join(srcDir, 'wrapper.gradle'), projectPath);
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);
}
} }
function copyScripts (projectPath) { function copyScripts(projectPath) {
var bin = path.join(ROOT, 'bin'); var bin = path.join(ROOT, 'bin');
var srcScriptsDir = path.join(bin, 'templates', 'cordova'); var srcScriptsDir = path.join(bin, 'templates', 'cordova');
var destScriptsDir = path.join(projectPath, 'cordova'); var destScriptsDir = path.join(projectPath, 'cordova');
@@ -187,18 +164,17 @@ function copyScripts (projectPath) {
* Returns a promise, fulfilled if the package name is acceptable; rejected * Returns a promise, fulfilled if the package name is acceptable; rejected
* otherwise. * otherwise.
*/ */
function validatePackageName (package_name) { function validatePackageName(package_name) {
// Make the package conform to Java package types //Make the package conform to Java package types
// http://developer.android.com/guide/topics/manifest/manifest-element.html#package //http://developer.android.com/guide/topics/manifest/manifest-element.html#package
// Enforce underscore limitation //Enforce underscore limitation
var msg = 'Error validating package name. '; var msg = 'Error validating package name. ';
if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) { if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) {
return Q.reject(new CordovaError(msg + 'Package name must look like: com.company.Name')); return Q.reject(new CordovaError(msg + 'Package name must look like: com.company.Name'));
} }
// Class is a reserved word //Class is a reserved word
if (/\b[Cc]lass\b/.test(package_name)) { if(/\b[Cc]lass\b/.test(package_name)) {
return Q.reject(new CordovaError(msg + '"class" is a reserved word')); return Q.reject(new CordovaError(msg + '"class" is a reserved word'));
} }
@@ -210,19 +186,19 @@ function validatePackageName (package_name) {
* Returns a promise, fulfilled if the project name is acceptable; rejected * Returns a promise, fulfilled if the project name is acceptable; rejected
* otherwise. * otherwise.
*/ */
function validateProjectName (project_name) { function validateProjectName(project_name) {
var msg = 'Error validating project name. '; var msg = 'Error validating project name. ';
// Make sure there's something there //Make sure there's something there
if (project_name === '') { if (project_name === '') {
return Q.reject(new CordovaError(msg + 'Project name cannot be empty')); return Q.reject(new CordovaError(msg + 'Project name cannot be empty'));
} }
// Enforce stupid name error //Enforce stupid name error
if (project_name === 'CordovaActivity') { if (project_name === 'CordovaActivity') {
return Q.reject(new CordovaError(msg + 'Project name cannot be 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)) { if (/^[0-9]/.test(project_name)) {
return Q.reject(new CordovaError(msg + 'Project name must not begin with a number')); return Q.reject(new CordovaError(msg + 'Project name must not begin with a number'));
} }
@@ -248,95 +224,83 @@ function validateProjectName (project_name) {
* *
* @return {Promise<String>} Directory where application has been created * @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 || {}; options = options || {};
// Set default values for path, package and name // Set default values for path, package and name
project_path = path.relative(process.cwd(), (project_path || 'CordovaExample')); project_path = path.relative(process.cwd(), (project_path || 'CordovaExample'));
// Check if project already exists // 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')); 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() ? 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 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 //Make the package conform to Java package types
return exports.validatePackageName(package_name) return validatePackageName(package_name)
.then(function () { .then(function() {
exports.validateProjectName(project_name); validateProjectName(project_name);
}).then(function () { }).then(function() {
// Log the given values for the project // Log the given values for the project
events.emit('log', 'Creating Cordova project for the Android platform:'); events.emit('log', 'Creating Cordova project for the Android platform:');
events.emit('log', '\tPath: ' + project_path); events.emit('log', '\tPath: ' + project_path);
events.emit('log', '\tPackage: ' + package_name); events.emit('log', '\tPackage: ' + package_name);
events.emit('log', '\tName: ' + project_name); events.emit('log', '\tName: ' + project_name);
events.emit('log', '\tActivity: ' + safe_activity_name); events.emit('log', '\tActivity: ' + safe_activity_name);
events.emit('log', '\tAndroid target: ' + target_api); 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 () { setShellFatal(true, function() {
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project'); var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
var app_path = path.join(project_path, 'app', 'src', 'main'); // copy project template
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
// copy project template // Manually create directories that would be empty within the template (since git doesn't track directories).
shell.mkdir('-p', app_path); shell.mkdir(path.join(project_path, 'libs'));
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). // copy cordova.js, cordova.jar
shell.mkdir(path.join(app_path, 'libs')); copyJsAndLibrary(project_path, options.link, safe_activity_name);
// copy cordova.js, cordova.jar // interpolate the activity name and package
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name); 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 manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
var java_path = path.join(app_path, 'java'); manifest.setPackageId(package_name)
var assets_path = path.join(app_path, 'assets'); .setTargetSdkVersion(target_api.split('-')[1])
var resource_path = path.join(app_path, 'res'); .getActivity().setName(safe_activity_name);
shell.mkdir('-p', java_path);
shell.mkdir('-p', assets_path);
shell.mkdir('-p', resource_path);
// interpolate the activity name and package var manifest_path = path.join(project_path, 'AndroidManifest.xml');
var packagePath = package_name.replace(/\./g, path.sep); manifest.write(manifest_path);
var activity_dir = path.join(java_path, packagePath);
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
shell.mkdir('-p', activity_dir); copyScripts(project_path);
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path); copyBuildRules(project_path);
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path); });
shell.sed('-i', /__NAME__/, project_name, path.join(app_path, 'res', 'values', 'strings.xml')); // Link it to local android install.
shell.sed('-i', /__ID__/, package_name, activity_path); writeProjectProperties(project_path, target_api);
prepBuildFiles(project_path);
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml')); events.emit('log', generateDoneMessage('create', options.link));
manifest.setPackageId(package_name) }).thenResolve(project_path);
.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, 'studio');
events.emit('log', generateDoneMessage('create', options.link));
}).thenResolve(project_path);
}; };
function generateDoneMessage (type, link) { function generateDoneMessage(type, link) {
var pkg = require('../../package'); 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) { if (link) {
msg += ' and has a linked CordovaLib'; msg += ' and has a linked CordovaLib';
} }
@@ -344,17 +308,34 @@ function generateDoneMessage (type, link) {
} }
// Returns a promise. // Returns a promise.
exports.update = function (projectPath, options, events) { exports.update = function(projectPath, options, events) {
options = options || {};
var errorString = return Q()
'An in-place platform update is not supported. \n' + .then(function() {
'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.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

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

View File

@@ -25,11 +25,11 @@ var CordovaError = require('cordova-common').CordovaError;
var Adb = {}; var Adb = {};
function isDevice (line) { function isDevice(line) {
return line.match(/\w+\tdevice/) && !line.match(/emulator/); return line.match(/\w+\tdevice/) && !line.match(/emulator/);
} }
function isEmulator (line) { function isEmulator(line) {
return line.match(/device/) && line.match(/emulator/); return line.match(/device/) && line.match(/emulator/);
} }
@@ -44,7 +44,8 @@ function isEmulator (line) {
* devices/emulators * devices/emulators
*/ */
Adb.devices = function (opts) { 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) { return output.split('\n').filter(function (line) {
// Filter out either real devices or emulators, depending on options // Filter out either real devices or emulators, depending on options
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line); 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 + '...'); events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install']; var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r'); 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 // 'adb install' seems to always returns no error, even if installation fails
// so we catching output to detect installation failure // so we catching output to detect installation failure
if (output.match(/Failure/)) { if (output.match(/Failure/)) {
@@ -84,7 +86,8 @@ Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...'); events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
var args = ['-s', target, 'shell']; var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/); 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 "' + return Q.reject(new CordovaError('Failed to execute shell command "' +
shellCommand + '"" on device: ' + output)); shellCommand + '"" on device: ' + output));
}); });
@@ -92,7 +95,8 @@ Adb.shell = function (target, shellCommand) {
Adb.start = function (target, activityName) { Adb.start = function (target, activityName) {
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...'); 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 "' + return Q.reject(new CordovaError('Failed to start application "' +
activityName + '"" on device: ' + output)); activityName + '"" on device: ' + output));
}); });

View File

@@ -19,12 +19,12 @@
var fs = require('fs'); var fs = require('fs');
var et = require('elementtree'); var et = require('elementtree');
var xml = require('cordova-common').xmlHelpers; var xml= require('cordova-common').xmlHelpers;
var DEFAULT_ORIENTATION = 'default'; var DEFAULT_ORIENTATION = 'default';
/** Wraps an AndroidManifest file */ /** Wraps an AndroidManifest file */
function AndroidManifest (path) { function AndroidManifest(path) {
this.path = path; this.path = path;
this.doc = xml.parseElementtreeSync(path); this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') { if (this.doc.getroot().tag !== 'manifest') {
@@ -32,38 +32,38 @@ function AndroidManifest (path) {
} }
} }
AndroidManifest.prototype.getVersionName = function () { AndroidManifest.prototype.getVersionName = function() {
return this.doc.getroot().attrib['android:versionName']; return this.doc.getroot().attrib['android:versionName'];
}; };
AndroidManifest.prototype.setVersionName = function (versionName) { AndroidManifest.prototype.setVersionName = function(versionName) {
this.doc.getroot().attrib['android:versionName'] = versionName; this.doc.getroot().attrib['android:versionName'] = versionName;
return this; return this;
}; };
AndroidManifest.prototype.getVersionCode = function () { AndroidManifest.prototype.getVersionCode = function() {
return this.doc.getroot().attrib['android:versionCode']; return this.doc.getroot().attrib['android:versionCode'];
}; };
AndroidManifest.prototype.setVersionCode = function (versionCode) { AndroidManifest.prototype.setVersionCode = function(versionCode) {
this.doc.getroot().attrib['android:versionCode'] = versionCode; this.doc.getroot().attrib['android:versionCode'] = versionCode;
return this; return this;
}; };
AndroidManifest.prototype.getPackageId = function () { AndroidManifest.prototype.getPackageId = function() {
/* jshint -W069 */ /*jshint -W069 */
return this.doc.getroot().attrib['package']; return this.doc.getroot().attrib['package'];
/* jshint +W069 */ /*jshint +W069 */
}; };
AndroidManifest.prototype.setPackageId = function (pkgId) { AndroidManifest.prototype.setPackageId = function(pkgId) {
/* jshint -W069 */ /*jshint -W069 */
this.doc.getroot().attrib['package'] = pkgId; this.doc.getroot().attrib['package'] = pkgId;
/* jshint +W069 */ /*jshint +W069 */
return this; return this;
}; };
AndroidManifest.prototype.getActivity = function () { AndroidManifest.prototype.getActivity = function() {
var activity = this.doc.getroot().find('./application/activity'); var activity = this.doc.getroot().find('./application/activity');
return { return {
getName: function () { getName: function () {
@@ -102,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 // Copy variable reference to avoid closure issues
var prefName = sdkPrefName; var prefName = sdkPrefName;
AndroidManifest.prototype['get' + capitalize(prefName)] = function () { AndroidManifest.prototype['get' + capitalize(prefName)] = function() {
var usesSdk = this.doc.getroot().find('./uses-sdk'); var usesSdk = this.doc.getroot().find('./uses-sdk');
return usesSdk && usesSdk.attrib['android:' + prefName]; 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'); var usesSdk = this.doc.getroot().find('./uses-sdk');
if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first
@@ -127,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'; 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'); var application = this.doc.getroot().find('./application');
if (value) { if (value) {
application.attrib['android:debuggable'] = 'true'; application.attrib['android:debuggable'] = 'true';
@@ -149,7 +150,7 @@ AndroidManifest.prototype.setDebuggable = function (value) {
* @param {String} [destPath] File to write manifest to. If omitted, * @param {String} [destPath] File to write manifest to. If omitted,
* manifest will be written to file it has been read from. * manifest will be written to file it has been read from.
*/ */
AndroidManifest.prototype.write = function (destPath) { AndroidManifest.prototype.write = function(destPath) {
fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8'); fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8');
}; };

View File

@@ -26,15 +26,16 @@ var pluginHandlers = require('./pluginHandlers');
var projectFileCache = {}; var projectFileCache = {};
function addToPropertyList (projectProperties, key, value) { function addToPropertyList(projectProperties, key, value) {
var i = 1; var i = 1;
while (projectProperties.get(key + '.' + i)) { i++; } while (projectProperties.get(key + '.' + i))
i++;
projectProperties.set(key + '.' + i, value); projectProperties.set(key + '.' + i, value);
projectProperties.dirty = true; projectProperties.dirty = true;
} }
function removeFromPropertyList (projectProperties, key, value) { function removeFromPropertyList(projectProperties, key, value) {
var i = 1; var i = 1;
var currentValue; var currentValue;
while ((currentValue = projectProperties.get(key + '.' + i))) { while ((currentValue = projectProperties.get(key + '.' + i))) {
@@ -53,18 +54,18 @@ function removeFromPropertyList (projectProperties, key, value) {
function getRelativeLibraryPath (parentDir, subDir) { function getRelativeLibraryPath (parentDir, subDir) {
var libraryPath = path.relative(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._propertiesEditors = {};
this._subProjectDirs = {}; this._subProjectDirs = {};
this._dirty = false; this._dirty = false;
this.projectDir = projectDir; this.projectDir = projectDir;
this.platformWww = path.join(this.projectDir, 'platform_www'); this.platformWww = path.join(this.projectDir, 'platform_www');
this.www = path.join(this.projectDir, 'assets/www'); this.www = path.join(this.projectDir, 'assets/www');
if (AndroidStudio.isAndroidStudioProject(projectDir) === true) { if(AndroidStudio.isAndroidStudioProject(projectDir) === true) {
this.www = path.join(this.projectDir, 'app/src/main/assets/www'); this.www = path.join(this.projectDir, 'app/src/main/assets/www');
} }
} }
@@ -91,15 +92,15 @@ AndroidProject.purgeCache = function (projectDir) {
* *
* @return {String} The name of the package * @return {String} The name of the package
*/ */
AndroidProject.prototype.getPackageName = function () { AndroidProject.prototype.getPackageName = function() {
var manifestPath = path.join(this.projectDir, 'AndroidManifest.xml'); var manifestPath = path.join(this.projectDir, 'AndroidManifest.xml');
if (AndroidStudio.isAndroidStudioProject(this.projectDir) === true) { if(AndroidStudio.isAndroidStudioProject(this.projectDir) === true) {
manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml'); manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
} }
return new AndroidManifest(manifestPath).getPackageId(); 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. // 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. // This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
var packageName = this.getPackageName(); var packageName = this.getPackageName();
@@ -109,7 +110,7 @@ AndroidProject.prototype.getCustomSubprojectRelativeDir = function (plugin_id, s
return subRelativeDir; return subRelativeDir;
}; };
AndroidProject.prototype.addSubProject = function (parentDir, subDir) { AndroidProject.prototype.addSubProject = function(parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProjectFile = path.resolve(parentDir, 'project.properties');
var subProjectFile = path.resolve(subDir, 'project.properties'); var subProjectFile = path.resolve(subDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile); var parentProperties = this._getPropertiesFile(parentProjectFile);
@@ -125,7 +126,7 @@ AndroidProject.prototype.addSubProject = function (parentDir, subDir) {
this._dirty = true; this._dirty = true;
}; };
AndroidProject.prototype.removeSubProject = function (parentDir, subDir) { AndroidProject.prototype.removeSubProject = function(parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile); var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir)); removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
@@ -133,35 +134,35 @@ AndroidProject.prototype.removeSubProject = function (parentDir, subDir) {
this._dirty = true; this._dirty = true;
}; };
AndroidProject.prototype.addGradleReference = function (parentDir, subDir) { AndroidProject.prototype.addGradleReference = function(parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile); var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir)); addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true; this._dirty = true;
}; };
AndroidProject.prototype.removeGradleReference = function (parentDir, subDir) { AndroidProject.prototype.removeGradleReference = function(parentDir, subDir) {
var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile); var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir)); removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
this._dirty = true; this._dirty = true;
}; };
AndroidProject.prototype.addSystemLibrary = function (parentDir, value) { AndroidProject.prototype.addSystemLibrary = function(parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile); var parentProperties = this._getPropertiesFile(parentProjectFile);
addToPropertyList(parentProperties, 'cordova.system.library', value); addToPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true; this._dirty = true;
}; };
AndroidProject.prototype.removeSystemLibrary = function (parentDir, value) { AndroidProject.prototype.removeSystemLibrary = function(parentDir, value) {
var parentProjectFile = path.resolve(parentDir, 'project.properties'); var parentProjectFile = path.resolve(parentDir, 'project.properties');
var parentProperties = this._getPropertiesFile(parentProjectFile); var parentProperties = this._getPropertiesFile(parentProjectFile);
removeFromPropertyList(parentProperties, 'cordova.system.library', value); removeFromPropertyList(parentProperties, 'cordova.system.library', value);
this._dirty = true; this._dirty = true;
}; };
AndroidProject.prototype.write = function () { AndroidProject.prototype.write = function() {
if (!this._dirty) { if (!this._dirty) {
return; return;
} }
@@ -200,9 +201,9 @@ AndroidProject.prototype.getUninstaller = function (type) {
* This checks if an Android project is clean or has old build artifacts * 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'); 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)); return !(fs.existsSync(build_path));
}; };

View File

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

View File

@@ -1,3 +1,5 @@
/* /*
Licensed to the Apache Software Foundation (ASF) under one Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file or more contributor license agreements. See the NOTICE file
@@ -17,8 +19,8 @@
under the License. under the License.
*/ */
var Q = require('q'); var Q = require('q'),
var superspawn = require('cordova-common').superspawn; superspawn = require('cordova-common').superspawn;
var suffix_number_regex = /(\d+)$/; var suffix_number_regex = /(\d+)$/;
// Used for sorting Android targets, example strings to sort: // Used for sorting Android targets, example strings to sort:
@@ -29,7 +31,7 @@ var suffix_number_regex = /(\d+)$/;
// The idea is to sort based on largest "suffix" number - meaning the bigger // 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 // the number at the end, the more recent the target, the closer to the
// start of the array. // 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_a = a.match(suffix_number_regex);
var suffix_b = b.match(suffix_number_regex); var suffix_b = b.match(suffix_number_regex);
if (suffix_a && suffix_b) { if (suffix_a && suffix_b) {
@@ -43,8 +45,9 @@ function sort_by_largest_numerical_suffix (a, b) {
} }
} }
module.exports.print_newest_available_sdk_target = function () { module.exports.print_newest_available_sdk_target = function() {
return module.exports.list_targets().then(function (targets) { return module.exports.list_targets()
.then(function(targets) {
targets.sort(sort_by_largest_numerical_suffix); targets.sort(sort_by_largest_numerical_suffix);
console.log(targets[0]); console.log(targets[0]);
}); });
@@ -65,34 +68,65 @@ module.exports.version_string_to_api_level = {
'7.1.1': 25 '7.1.1': 25
}; };
function parse_targets (output) { module.exports.list_targets_with_android = function() {
var target_out = output.split('\n'); return superspawn.spawn('android', ['list', 'targets'])
var targets = []; .then(function(stdout) {
for (var i = target_out.length - 1; i >= 0; i--) { var target_out = stdout.split('\n');
if (target_out[i].match(/id:/)) { // if "id:" is in the line... var targets = [];
targets.push(target_out[i].match(/"(.+)"/)[1]); // .. match whatever is in quotes. for (var i = target_out.length - 1; i >= 0; i--) {
if(target_out[i].match(/id:/)) {
targets.push(target_out[i].match(/"(.+)"/)[1]);
}
} }
} return targets;
return targets; });
}
module.exports.list_targets_with_android = function () {
return superspawn.spawn('android', ['list', 'target']).then(parse_targets);
}; };
module.exports.list_targets_with_avdmanager = function () { module.exports.list_targets_with_sdkmanager = function() {
return superspawn.spawn('avdmanager', ['list', 'target']).then(parse_targets); return superspawn.spawn('sdkmanager', ['--list'])
.then(function(stdout) {
var parsing_installed_packages = false;
var lines = stdout.split('\n');
var targets = [];
for (var i = 0, l = lines.length; i < l; i++) {
var line = lines[i];
if (line.match(/Installed packages/)) {
parsing_installed_packages = true;
} else if (line.match(/Available Packages/) || line.match(/Available Updates/)) {
// we are done working through installed packages, exit
break;
}
if (parsing_installed_packages) {
// Match stock android platform
if (line.match(/platforms;android-\d+/)) {
targets.push(line.match(/(android-\d+)/)[1]);
}
// Match Google APIs
if (line.match(/addon-google_apis-google-\d+/)) {
var description = lines[i + 1];
// munge description to match output from old android sdk tooling
var api_level = description.match(/Android (\d+)/); //[1];
if (api_level) {
targets.push('Google Inc.:Google APIs:' + api_level[1]);
}
}
// TODO: match anything else?
}
}
return targets;
});
}; };
module.exports.list_targets = function () { module.exports.list_targets = function() {
return module.exports.list_targets_with_avdmanager().catch(function (err) { return module.exports.list_targets_with_android()
// If there's an error, like avdmanager could not be found, we can try .catch(function(err) {
// as a last resort, to run `android`, in case this is a super old // there's a chance `android` no longer works.
// SDK installation. // lets see if `sdkmanager` is available and we can figure it out
if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) { var avail_regex = /"?android"? command is no longer available/;
return module.exports.list_targets_with_android(); if (err.code && ((err.stdout && err.stdout.match(avail_regex)) || (err.stderr && err.stderr.match(avail_regex)))) {
return module.exports.list_targets_with_sdkmanager();
} else throw err; } else throw err;
}).then(function (targets) { }).then(function(targets) {
if (targets.length === 0) { if (targets.length === 0) {
return Q.reject(new Error('No android targets (SDKs) installed!')); return Q.reject(new Error('No android targets (SDKs) installed!'));
} }

View File

@@ -19,10 +19,10 @@
under the License. under the License.
*/ */
var Q = require('q'); var Q = require('q'),
var path = require('path'); path = require('path'),
var fs = require('fs'); fs = require('fs'),
var nopt = require('nopt'); nopt = require('nopt');
var Adb = require('./Adb'); var Adb = require('./Adb');
@@ -31,11 +31,11 @@ var events = require('cordova-common').events;
var spawn = require('cordova-common').superspawn.spawn; var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
function parseOpts (options, resolvedTarget, projectRoot) { function parseOpts(options, resolvedTarget, projectRoot) {
options = options || {}; options = options || {};
options.argv = nopt({ options.argv = nopt({
gradle: Boolean, gradle: Boolean,
studio: Boolean, ant: Boolean,
prepenv: Boolean, prepenv: Boolean,
versionCode: String, versionCode: String,
minSdkVersion: String, minSdkVersion: String,
@@ -47,28 +47,24 @@ function parseOpts (options, resolvedTarget, projectRoot) {
keystoreType: String keystoreType: String
}, {}, options.argv, 0); }, {}, options.argv, 0);
// Android Studio Build method is the default
var ret = { var ret = {
buildType: options.release ? 'release' : 'debug', buildType: options.release ? 'release' : 'debug',
buildMethod: process.env.ANDROID_BUILD || 'studio', buildMethod: process.env.ANDROID_BUILD || 'gradle',
prepEnv: options.argv.prepenv, prepEnv: options.argv.prepenv,
arch: resolvedTarget && resolvedTarget.arch, arch: resolvedTarget && resolvedTarget.arch,
extraArgs: [] extraArgs: []
}; };
if (options.argv.gradle || options.argv.studio) { if (options.argv.ant || options.argv.gradle)
ret.buildMethod = options.argv.studio ? 'studio' : 'gradle'; ret.buildMethod = options.argv.ant ? 'ant' : 'gradle';
}
// This comes from cordova/run
if (options.studio) ret.buildMethod = 'studio';
if (options.gradle) ret.buildMethod = 'gradle';
if (options.nobuild) ret.buildMethod = 'none'; if (options.nobuild) ret.buildMethod = 'none';
if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); } 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.minSdkVersion)
ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion);
if (options.argv.gradleArg) { if (options.argv.gradleArg) {
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg); ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
@@ -76,10 +72,12 @@ function parseOpts (options, resolvedTarget, projectRoot) {
var packageArgs = {}; 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) { ['alias','storePassword','password','keystoreType'].forEach(function (flagName) {
if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; } if (options.argv[flagName])
packageArgs[flagName] = options.argv[flagName];
}); });
var buildConfig = options.buildConfig; var buildConfig = options.buildConfig;
@@ -90,20 +88,20 @@ function parseOpts (options, resolvedTarget, projectRoot) {
if (!fs.existsSync(buildConfig)) { if (!fs.existsSync(buildConfig)) {
throw new Error('Specified build config file does not exist: ' + 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 buildjson = fs.readFileSync(buildConfig, 'utf8');
var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
if (config.android && config.android[ret.buildType]) { if (config.android && config.android[ret.buildType]) {
var androidInfo = config.android[ret.buildType]; var androidInfo = config.android[ret.buildType];
if (androidInfo.keystore && !packageArgs.keystore) { if(androidInfo.keystore && !packageArgs.keystore) {
if (androidInfo.keystore.substr(0, 1) === '~') { if(androidInfo.keystore.substr(0,1) === '~') {
androidInfo.keystore = process.env.HOME + androidInfo.keystore.substr(1); androidInfo.keystore = process.env.HOME + androidInfo.keystore.substr(1);
} }
packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore); packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore);
events.emit('log', 'Reading the keystore from: ' + packageArgs.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]; packageArgs[key] = packageArgs[key] || androidInfo[key];
}); });
} }
@@ -114,8 +112,8 @@ function parseOpts (options, resolvedTarget, projectRoot) {
packageArgs.password, packageArgs.keystoreType); packageArgs.password, packageArgs.keystoreType);
} }
if (!ret.packageInfo) { if(!ret.packageInfo) {
if (Object.keys(packageArgs).length > 0) { if(Object.keys(packageArgs).length > 0) {
events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.'); events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.');
} }
} }
@@ -127,10 +125,11 @@ function parseOpts (options, resolvedTarget, projectRoot) {
* Builds the project with the specifed options * Builds the project with the specifed options
* Returns a promise. * Returns a promise.
*/ */
module.exports.runClean = function (options) { module.exports.runClean = function(options) {
var opts = parseOpts(options, null, this.root); var opts = parseOpts(options, null, this.root);
var builder = builders.getBuilder(opts.buildMethod); var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts).then(function () { return builder.prepEnv(opts)
.then(function() {
return builder.clean(opts); return builder.clean(opts);
}); });
}; };
@@ -147,16 +146,17 @@ module.exports.runClean = function (options) {
* @return {Promise<Object>} Promise, resolved with built packages * @return {Promise<Object>} Promise, resolved with built packages
* information. * information.
*/ */
module.exports.run = function (options, optResolvedTarget) { module.exports.run = function(options, optResolvedTarget) {
var opts = parseOpts(options, optResolvedTarget, this.root); var opts = parseOpts(options, optResolvedTarget, this.root);
console.log(opts.buildMethod);
var builder = builders.getBuilder(opts.buildMethod); var builder = builders.getBuilder(opts.buildMethod);
return builder.prepEnv(opts).then(function () { return builder.prepEnv(opts)
.then(function() {
if (opts.prepEnv) { if (opts.prepEnv) {
events.emit('verbose', 'Build file successfully prepared.'); events.emit('verbose', 'Build file successfully prepared.');
return; return;
} }
return builder.build(opts).then(function () { return builder.build(opts)
.then(function() {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch); var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t')); events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
return { return {
@@ -172,31 +172,38 @@ module.exports.run = function (options, optResolvedTarget) {
* Detects the architecture of a device/emulator * Detects the architecture of a device/emulator
* Returns "arm" or "x86". * Returns "arm" or "x86".
*/ */
module.exports.detectArchitecture = function (target) { module.exports.detectArchitecture = function(target) {
function helper () { function helper() {
return Adb.shell(target, 'cat /proc/cpuinfo').then(function (output) { return Adb.shell(target, 'cat /proc/cpuinfo')
.then(function(output) {
return /intel/i.exec(output) ? 'x86' : 'arm'; return /intel/i.exec(output) ? 'x86' : 'arm';
}); });
} }
// It sometimes happens (at least on OS X), that this command will hang forever. // 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. // 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)) { if (/timed out/.exec('' + err)) {
// adb kill-server doesn't seem to do the trick. // adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually // Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines. // 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.'); events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']).then(function () { return spawn('killall', ['adb'])
return helper().then(null, function () { .then(function() {
return helper()
.then(null, function() {
// The double kill is sadly often necessary, at least on mac. // 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.'); 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 spawn('killall', ['adb'])
return helper().then(null, function () { .then(function() {
return helper()
.then(null, function() {
return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.')); return 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. // For non-killall OS's.
return Q.reject(err); return Q.reject(err);
}); });
@@ -205,10 +212,10 @@ module.exports.detectArchitecture = function (target) {
}); });
}; };
module.exports.findBestApkForArchitecture = function (buildResults, arch) { module.exports.findBestApkForArchitecture = function(buildResults, arch) {
var paths = buildResults.apkPaths.filter(function (p) { var paths = buildResults.apkPaths.filter(function(p) {
var apkName = path.basename(p); var apkName = path.basename(p);
if (buildResults.buildType === 'debug') { if (buildResults.buildType == 'debug') {
return /-debug/.exec(apkName); return /-debug/.exec(apkName);
} }
return !/-debug/.exec(apkName); return !/-debug/.exec(apkName);
@@ -228,7 +235,7 @@ module.exports.findBestApkForArchitecture = function (buildResults, arch) {
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType); 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 = { this.keystore = {
'name': 'key.store', 'name': 'key.store',
'value': keystore 'value': keystore
@@ -258,10 +265,10 @@ function PackageInfo (keystore, alias, storePassword, password, keystoreType) {
} }
PackageInfo.prototype = { PackageInfo.prototype = {
toProperties: function () { toProperties: function() {
var self = this; var self = this;
var result = ''; var result = '';
Object.keys(self).forEach(function (key) { Object.keys(self).forEach(function(key) {
result += self[key].name; result += self[key].name;
result += '='; result += '=';
result += self[key].value.replace(/\\/g, '\\\\'); result += self[key].value.replace(/\\/g, '\\\\');
@@ -271,7 +278,7 @@ 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('Usage: ' + path.relative(process.cwd(), path.join('../build')) + ' [flags] [Signed APK flags]');
console.log('Flags:'); console.log('Flags:');
console.log(' \'--debug\': will build project in debug mode (default)'); console.log(' \'--debug\': will build project in debug mode (default)');

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

@@ -16,55 +16,83 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
/* eslint no-self-assign: 0 */
/* eslint no-unused-vars: 0 */
var Q = require('q'); var Q = require('q');
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var shell = require('shelljs'); var shell = require('shelljs');
var events = require('cordova-common').events; var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError;
function GenericBuilder (projectDir) { function GenericBuilder (projectDir) {
this.root = projectDir || path.resolve(__dirname, '../../..'); this.root = projectDir || path.resolve(__dirname, '../../..');
this.binDirs = { this.binDirs = {
studio: path.join(this.root, 'app', 'build', 'outputs', 'apk'), ant: path.join(this.root, hasCustomRules(this.root) ? 'ant-build' : 'bin'),
gradle: path.join(this.root, 'build', 'outputs', 'apk') gradle: path.join(this.root, 'build', 'outputs', 'apk')
}; };
} }
GenericBuilder.prototype.prepEnv = function () { function hasCustomRules(projectRoot) {
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
}
GenericBuilder.prototype.prepEnv = function() {
return Q(); return Q();
}; };
GenericBuilder.prototype.build = function () { GenericBuilder.prototype.build = function() {
events.emit('log', 'Skipping build...'); events.emit('log', 'Skipping build...');
return Q(null); return Q(null);
}; };
GenericBuilder.prototype.clean = function () { GenericBuilder.prototype.clean = function() {
return Q(); return Q();
}; };
GenericBuilder.prototype.findOutputApks = function (build_type, arch) { GenericBuilder.prototype.findOutputApks = function(build_type, arch) {
var self = this; var self = this;
return Object.keys(this.binDirs).reduce(function (result, builderName) { return Object.keys(this.binDirs)
.reduce(function (result, builderName) {
var binDir = self.binDirs[builderName]; var binDir = self.binDirs[builderName];
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch)); return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
}, []).sort(apkSorter); }, [])
.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; module.exports = GenericBuilder;
function apkSorter (fileA, fileB) { 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 // De-prioritize unsigned builds
var unsignedRE = /-unsigned/; var unsignedRE = /-unsigned/;
if (unsignedRE.exec(fileA)) { if (unsignedRE.exec(fileA)) {
@@ -73,22 +101,16 @@ function apkSorter (fileA, fileB) {
return -1; return -1;
} }
var timeDiff = fs.statSync(fileB).mtime - fs.statSync(fileA).mtime; var timeDiff = fs.statSync(fileA).mtime - fs.statSync(fileB).mtime;
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff; return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
} }
function findOutputApksHelper (dir, build_type, arch) { function findOutputApksHelper(dir, build_type, arch) {
var shellSilent = shell.config.silent; var shellSilent = shell.config.silent;
shell.config.silent = true; shell.config.silent = true;
// list directory recursively var ret = shell.ls(path.join(dir, '*.apk'))
var ret = shell.ls('-R', dir).map(function (file) { .filter(function(candidate) {
// 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); var apkName = path.basename(candidate);
// Need to choose between release and debug .apk. // Need to choose between release and debug .apk.
if (build_type === 'debug') { if (build_type === 'debug') {
@@ -98,7 +120,8 @@ function findOutputApksHelper (dir, build_type, arch) {
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName); return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
} }
return true; return true;
}).sort(apkSorter); })
.sort(apkSorter);
shellSilent = shellSilent; shellSilent = shellSilent;
@@ -108,15 +131,15 @@ function findOutputApksHelper (dir, build_type, arch) {
// Assume arch-specific build if newest apk has -x86 or -arm. // Assume arch-specific build if newest apk has -x86 or -arm.
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0])); var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
// And show only arch-specific ones (or non-arch-specific) // And show only arch-specific ones (or non-arch-specific)
ret = ret.filter(function (p) { ret = ret.filter(function(p) {
/* jshint -W018 */ /*jshint -W018 */
return !!/-x86|-arm/.exec(path.basename(p)) === archSpecific; return !!/-x86|-arm/.exec(path.basename(p)) == archSpecific;
/* jshint +W018 */ /*jshint +W018 */
}); });
if (archSpecific && ret.length > 1 && arch) { if (archSpecific && ret.length > 1 && arch) {
ret = ret.filter(function (p) { ret = ret.filter(function(p) {
return path.basename(p).indexOf('-' + arch) !== -1; return path.basename(p).indexOf('-' + arch) != -1;
}); });
} }

View File

@@ -22,7 +22,7 @@ var fs = require('fs');
var util = require('util'); var util = require('util');
var path = require('path'); var path = require('path');
var shell = require('shelljs'); var shell = require('shelljs');
var superspawn = require('cordova-common').superspawn; var spawn = require('cordova-common').superspawn.spawn;
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var check_reqs = require('../check_reqs'); var check_reqs = require('../check_reqs');
@@ -37,15 +37,15 @@ var TEMPLATE =
function GradleBuilder (projectRoot) { function GradleBuilder (projectRoot) {
GenericBuilder.call(this, projectRoot); GenericBuilder.call(this, projectRoot);
this.binDirs = { gradle: this.binDirs.gradle }; this.binDirs = {gradle: this.binDirs.gradle};
} }
util.inherits(GradleBuilder, GenericBuilder); util.inherits(GradleBuilder, GenericBuilder);
GradleBuilder.prototype.getArgs = function (cmd, opts) { GradleBuilder.prototype.getArgs = function(cmd, opts) {
if (cmd === 'release') { if (cmd == 'release') {
cmd = 'cdvBuildRelease'; cmd = 'cdvBuildRelease';
} else if (cmd === 'debug') { } else if (cmd == 'debug') {
cmd = 'cdvBuildDebug'; cmd = 'cdvBuildDebug';
} }
var args = [cmd, '-b', path.join(this.root, 'build.gradle')]; var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
@@ -69,89 +69,46 @@ GradleBuilder.prototype.getArgs = function (cmd, opts) {
* This returns a promise * This returns a promise
*/ */
GradleBuilder.prototype.runGradleWrapper = function (gradle_cmd, gradle_file) { GradleBuilder.prototype.runGradleWrapper = function(gradle_cmd) {
var gradlePath = path.join(this.root, 'gradlew'); var gradlePath = path.join(this.root, 'gradlew');
gradle_file = path.join(this.root, (gradle_file || 'wrapper.gradle')); var wrapperGradle = path.join(this.root, 'wrapper.gradle');
if (fs.existsSync(gradlePath)) { if(fs.existsSync(gradlePath)) {
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows //Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
} else { } else {
return superspawn.spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', gradle_file], { stdio: 'pipe' }) return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
.progress(function (stdio) {
suppressJavaOptionsInfo(stdio);
});
} }
}; };
/*
* We need to kill this in a fire.
*/
GradleBuilder.prototype.readProjectProperties = function () {
function findAllUniq (data, r) {
var s = {};
var m;
while ((m = r.exec(data))) {
s[m[1]] = 1;
}
return Object.keys(s);
}
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
return {
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
};
};
GradleBuilder.prototype.extractRealProjectNameFromManifest = function () {
var manifestPath = path.join(this.root, 'AndroidManifest.xml');
var manifestData = fs.readFileSync(manifestPath, 'utf8');
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
if (!m) {
throw new CordovaError('Could not find package name in ' + manifestPath);
}
var packageName = m[1];
var lastDotIndex = packageName.lastIndexOf('.');
return packageName.substring(lastDotIndex + 1);
};
// Makes the project buildable, minus the gradle wrapper. // Makes the project buildable, minus the gradle wrapper.
GradleBuilder.prototype.prepBuildFiles = function () { GradleBuilder.prototype.prepBuildFiles = function() {
// Update the version of build.gradle in each dependent library. // Update the version of build.gradle in each dependent library.
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle'); var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
var propertiesObj = this.readProjectProperties(); var propertiesObj = this.readProjectProperties();
var subProjects = propertiesObj.libs; var subProjects = propertiesObj.libs;
var checkAndCopy = function(subProject, root) {
// Check and copy the gradle file into the subproject. var subProjectGradle = path.join(root, subProject, 'build.gradle');
// Called by the loop below this function def. // This is the future-proof way of checking if a file exists
var checkAndCopy = function (subProject, root) { // This must be synchronous to satisfy a Travis test
var subProjectGradle = path.join(root, subProject, 'build.gradle'); try {
// This is the future-proof way of checking if a file exists fs.accessSync(subProjectGradle, fs.F_OK);
// This must be synchronous to satisfy a Travis test } catch (e) {
try { shell.cp('-f', pluginBuildGradle, subProjectGradle);
fs.accessSync(subProjectGradle, fs.F_OK); }
} catch (e) {
shell.cp('-f', pluginBuildGradle, subProjectGradle);
}
}; };
// Some dependencies on Android don't use gradle, or don't have default
// gradle files. This copies a dummy gradle file into them
for (var i = 0; i < subProjects.length; ++i) { for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib' && subProjects[i] !== 'app') { if (subProjects[i] !== 'CordovaLib') {
checkAndCopy(subProjects[i], this.root); checkAndCopy(subProjects[i], this.root);
} }
} }
var name = this.extractRealProjectNameFromManifest(); var name = this.extractRealProjectNameFromManifest();
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149 //Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function (p) { var settingsGradlePaths = subProjects.map(function(p){
var realDir = p.replace(/[/\\]/g, ':'); var realDir=p.replace(/[/\\]/g, ':');
var libName = realDir.replace(name + '-', ''); var libName=realDir.replace(name+'-','');
var str = 'include ":' + libName + '"\n'; var str='include ":'+libName+'"\n';
if (realDir.indexOf(name + '-') !== -1) { str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n'; } if(realDir.indexOf(name+'-')!==-1)
str+='project(":'+libName+'").projectDir = new File("'+p+'")\n';
return str; return str;
}); });
@@ -163,33 +120,30 @@ GradleBuilder.prototype.prepBuildFiles = function () {
var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8'); var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
var depsList = ''; var depsList = '';
var root = this.root; var root = this.root;
var insertExclude = function(p) {
// Cordova Plugins can be written as library modules that would use Cordova as a var gradlePath = path.join(root, p, 'build.gradle');
// dependency. Because we need to make sure that Cordova is compiled only once for var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
// dexing, we make sure to exclude CordovaLib from these modules if(projectGradleFile.indexOf('CordovaLib') != -1) {
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'; depsList += '{\n exclude module:("CordovaLib")\n }\n';
} else { }
depsList += '\n'; else {
} depsList +='\n';
}
}; };
subProjects.forEach(function(p) {
subProjects.forEach(function (p) {
console.log('Subproject Path: ' + p); console.log('Subproject Path: ' + p);
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', ''); var libName=p.replace(/[/\\]/g, ':').replace(name+'-','');
depsList += ' implementation(project(path: "' + libName + '"))'; depsList += ' debugCompile(project(path: "' + libName + '", configuration: "debug"))';
insertExclude(p);
depsList += ' releaseCompile(project(path: "' + libName + '", configuration: "release"))';
insertExclude(p); insertExclude(p);
}); });
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390 // For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
var SYSTEM_LIBRARY_MAPPINGS = [ var SYSTEM_LIBRARY_MAPPINGS = [
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'], [/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+'] [/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
]; ];
propertiesObj.systemLibs.forEach(function (p) { propertiesObj.systemLibs.forEach(function(p) {
var mavenRef; var mavenRef;
// It's already in gradle form if it has two ':'s // It's already in gradle form if it has two ':'s
if (/:.*:/.exec(p)) { if (/:.*:/.exec(p)) {
@@ -208,25 +162,23 @@ GradleBuilder.prototype.prepBuildFiles = function () {
} }
depsList += ' compile "' + mavenRef + '"\n'; depsList += ' compile "' + mavenRef + '"\n';
}); });
// This code is dangerous and actually writes gradle declarations directly into the build.gradle
// Try not to mess with this if possible
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2'); buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
var includeList = ''; var includeList = '';
propertiesObj.gradleIncludes.forEach(function (includePath) { propertiesObj.gradleIncludes.forEach(function(includePath) {
includeList += 'apply from: "' + includePath + '"\n'; includeList += 'apply from: "' + includePath + '"\n';
}); });
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2'); 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); fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
}; };
GradleBuilder.prototype.prepEnv = function (opts) { GradleBuilder.prototype.prepEnv = function(opts) {
var self = this; var self = this;
return check_reqs.check_gradle().then(function (gradlePath) { return check_reqs.check_gradle()
.then(function(gradlePath) {
return self.runGradleWrapper(gradlePath); return self.runGradleWrapper(gradlePath);
}).then(function () { }).then(function() {
return self.prepBuildFiles(); return self.prepBuildFiles();
}).then(function () { }).then(function() {
// We now copy the gradle out of the framework // We now copy the gradle out of the framework
// This is a dirty patch to get the build working // This is a dirty patch to get the build working
/* /*
@@ -246,12 +198,12 @@ GradleBuilder.prototype.prepEnv = function (opts) {
// 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. // 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. // For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/; var distributionUrlRegex = /distributionUrl.*zip/;
/* jshint -W069 */ /*jshint -W069 */
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-4.1-all.zip'; var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-3.3-all.zip';
/* jshint +W069 */ /*jshint +W069 */
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties'); var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.chmod('u+w', gradleWrapperPropertiesPath); shell.chmod('u+w', gradleWrapperPropertiesPath);
shell.sed('-i', distributionUrlRegex, 'distributionUrl=' + distributionUrl, gradleWrapperPropertiesPath); shell.sed('-i', distributionUrlRegex, 'distributionUrl='+distributionUrl, gradleWrapperPropertiesPath);
var propertiesFile = opts.buildType + SIGNING_PROPERTIES; var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
var propertiesFilePath = path.join(self.root, propertiesFile); var propertiesFilePath = path.join(self.root, propertiesFile);
@@ -267,37 +219,53 @@ GradleBuilder.prototype.prepEnv = function (opts) {
* Builds the project with gradle. * Builds the project with gradle.
* Returns a promise. * Returns a promise.
*/ */
GradleBuilder.prototype.build = function (opts) { GradleBuilder.prototype.build = function(opts) {
var wrapper = path.join(this.root, 'gradlew'); var wrapper = path.join(this.root, 'gradlew');
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts); var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
return superspawn.spawn(wrapper, args, { stdio: 'pipe' }) return spawn(wrapper, args, {stdio: 'pipe'})
.progress(function (stdio) { .progress(function (stdio){
suppressJavaOptionsInfo(stdio); if (stdio.stderr) {
}).catch(function (error) { /*
if (error.toString().indexOf('failed to find target with hash string') >= 0) { * Workaround for the issue with Java printing some unwanted information to
return check_reqs.check_android_target(error).then(function () { * stderr instead of stdout.
// If due to some odd reason - check_android_target succeeds * This function suppresses 'Picked up _JAVA_OPTIONS' message from being
// we should still fail here. * printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
return Q.reject(error); * explanation.
}); */
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
} }
return Q.reject(error); 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) { GradleBuilder.prototype.clean = function(opts) {
var builder = this; var builder = this;
var wrapper = path.join(this.root, 'gradlew'); var wrapper = path.join(this.root, 'gradlew');
var args = builder.getArgs('clean', opts); var args = builder.getArgs('clean', opts);
return Q().then(function () { return Q().then(function() {
return superspawn.spawn(wrapper, args, { stdio: 'inherit' }); return spawn(wrapper, args, {stdio: 'inherit'});
}).then(function () { })
.then(function () {
shell.rm('-rf', path.join(builder.root, 'out')); shell.rm('-rf', path.join(builder.root, 'out'));
['debug', 'release'].forEach(function (config) { ['debug', 'release'].forEach(function(config) {
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES); var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
if (isAutoGenerated(propertiesFilePath)) { if(isAutoGenerated(propertiesFilePath)){
shell.rm('-f', propertiesFilePath); shell.rm('-f', propertiesFilePath);
} }
}); });
@@ -306,25 +274,6 @@ GradleBuilder.prototype.clean = function (opts) {
module.exports = GradleBuilder; module.exports = GradleBuilder;
function suppressJavaOptionsInfo (stdio) { function isAutoGenerated(file) {
if (stdio.stderr) {
/*
* Workaround for the issue with Java printing some unwanted information to
* stderr instead of stdout.
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
* explanation.
*/
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
if (suppressThisLine) {
return;
}
process.stderr.write(stdio.stderr);
} else {
process.stdout.write(stdio.stdout);
}
}
function isAutoGenerated (file) {
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0; return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
} }

View File

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

View File

@@ -20,8 +20,8 @@
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var knownBuilders = { var knownBuilders = {
ant: 'AntBuilder',
gradle: 'GradleBuilder', gradle: 'GradleBuilder',
studio: 'StudioBuilder',
none: 'GenericBuilder' none: 'GenericBuilder'
}; };
@@ -35,7 +35,8 @@ var knownBuilders = {
* @return {Builder} A builder instance for specified build type. * @return {Builder} A builder instance for specified build type.
*/ */
module.exports.getBuilder = function (builderType, projectRoot) { module.exports.getBuilder = function (builderType, projectRoot) {
if (!knownBuilders[builderType]) { throw new CordovaError('Builder ' + builderType + ' is not supported.'); } if (!knownBuilders[builderType])
throw new CordovaError('Builder ' + builderType + ' is not supported.');
try { try {
var Builder = require('./' + knownBuilders[builderType]); var Builder = require('./' + knownBuilders[builderType]);

View File

@@ -21,19 +21,19 @@
/* jshint sub:true */ /* jshint sub:true */
var shelljs = require('shelljs'); var shelljs = require('shelljs'),
var child_process = require('child_process'); child_process = require('child_process'),
var Q = require('q'); Q = require('q'),
var path = require('path'); path = require('path'),
var fs = require('fs'); fs = require('fs'),
var os = require('os'); os = require('os'),
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'); REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'),
var PROJECT_ROOT = path.join(__dirname, '..', '..'); PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn; var superspawn = require('cordova-common').superspawn;
var android_sdk = require('./android_sdk'); var android_sdk = require('./android_sdk');
function forgivingWhichSync (cmd) { function forgivingWhichSync(cmd) {
try { try {
return fs.realpathSync(shelljs.which(cmd)); return fs.realpathSync(shelljs.which(cmd));
} catch (e) { } catch (e) {
@@ -41,9 +41,9 @@ function forgivingWhichSync (cmd) {
} }
} }
function tryCommand (cmd, errMsg, catchStderr) { function tryCommand(cmd, errMsg, catchStderr) {
var d = Q.defer(); var d = Q.defer();
child_process.exec(cmd, function (err, stdout, stderr) { child_process.exec(cmd, function(err, stdout, stderr) {
if (err) d.reject(new CordovaError(errMsg)); if (err) d.reject(new CordovaError(errMsg));
// Sometimes it is necessary to return an stderr instead of stdout in case of success, since // 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 // some commands prints theirs output to stderr instead of stdout. 'javac' is the example
@@ -52,18 +52,18 @@ function tryCommand (cmd, errMsg, catchStderr) {
return d.promise; return d.promise;
} }
module.exports.isWindows = function () { module.exports.isWindows = function() {
return (os.platform() === 'win32'); return (os.platform() == 'win32');
}; };
module.exports.isDarwin = function () { module.exports.isDarwin = function() {
return (os.platform() === 'darwin'); return (os.platform() == 'darwin');
}; };
// Get valid target from framework/project.properties if run from this repo // 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 // Otherwise get target from project.properties file within a generated cordova-android project
module.exports.get_target = function () { module.exports.get_target = function() {
function extractFromFile (filePath) { function extractFromFile(filePath) {
var target = shelljs.grep(/\btarget=/, filePath); var target = shelljs.grep(/\btarget=/, filePath);
if (!target) { if (!target) {
throw new Error('Could not find android target within: ' + filePath); throw new Error('Could not find android target within: ' + filePath);
@@ -83,82 +83,77 @@ module.exports.get_target = function () {
}; };
// Returns a promise. Called only by build and clean commands. // Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function () { module.exports.check_ant = function() {
return superspawn.spawn('ant', ['-version']).then(function (output) { return superspawn.spawn('ant', ['-version'])
.then(function(output) {
// Parse Ant version from command output // Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1]; return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
}).catch(function (err) { }).catch(function(err) {
if (err) { throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
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 androidStudioPath;
var i = 0; var i = 0;
var foundStudio = false; var foundStudio = false;
var program_dir; 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.isDarwin()) {
if (module.exports.isWindows()) { program_dir = fs.readdirSync('/Applications');
while (i < program_dir.length && !foundStudio) {
var result = child_process.spawnSync(path.join(__dirname, 'getASPath.bat')); if (program_dir[i].startsWith('Android Studio')) {
// console.log('result.stdout =' + result.stdout.toString()); //TODO: Check for a specific Android Studio version, make sure it's not Canary
// console.log('result.stderr =' + result.stderr.toString()); androidStudioPath = path.join('/Applications', program_dir[i], 'Contents', 'gradle');
foundStudio = true;
if (result.stderr.toString().length > 0) { } else { ++i; }
var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/'; }
if (fs.existsSync(androidPath)) { } else if (module.exports.isWindows()) {
program_dir = fs.readdirSync(androidPath); var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
while (i < program_dir.length && !foundStudio) { if (fs.existsSync(androidPath)) {
if (program_dir[i].startsWith('Android Studio')) { program_dir = fs.readdirSync(androidPath);
foundStudio = true; while (i < program_dir.length && !foundStudio) {
androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle'); if (program_dir[i].startsWith('Android Studio')) {
} else { ++i; } 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)) { if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
var dirs = fs.readdirSync(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'); return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
} }
} else { } else {
// OK, let's try to check for Gradle! //OK, let's try to check for Gradle!
return forgivingWhichSync('gradle'); return forgivingWhichSync('gradle');
} }
}; };
// Returns a promise. Called only by build and clean commands. // 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 sdkDir = process.env['ANDROID_HOME'];
var d = Q.defer(); 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' + 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.')); 'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
}
var gradlePath = module.exports.get_gradle_wrapper(); 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' + 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' + 'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio')); 'in your path, or install Android Studio'));
}
return d.promise; return d.promise;
}; };
// Returns a promise. // Returns a promise.
module.exports.check_java = function () { module.exports.check_java = function() {
var javacPath = forgivingWhichSync('javac'); var javacPath = forgivingWhichSync('javac');
var hasJavaHome = !!process.env['JAVA_HOME']; var hasJavaHome = !!process.env['JAVA_HOME'];
return Q().then(function () { return Q().then(function() {
if (hasJavaHome) { if (hasJavaHome) {
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh). // Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
if (!javacPath) { if (!javacPath) {
@@ -168,14 +163,13 @@ module.exports.check_java = function () {
if (javacPath) { if (javacPath) {
// OS X has a command for finding JAVA_HOME. // OS X has a command for finding JAVA_HOME.
var find_java = '/usr/libexec/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)) { 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(); process.env['JAVA_HOME'] = stdout.trim();
}).catch(function (err) { }).catch(function(err) {
if (err) { throw new CordovaError(default_java_error_msg);
throw new CordovaError(default_java_error_msg);
}
}); });
} else { } else {
// See if we can derive it from javac's location. // See if we can derive it from javac's location.
@@ -206,7 +200,7 @@ module.exports.check_java = function () {
} }
} }
} }
}).then(function () { }).then(function() {
var msg = var msg =
'Failed to run "javac -version", make sure that you have a JDK installed.\n' + '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'; 'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
@@ -215,8 +209,9 @@ module.exports.check_java = function () {
} }
// We use tryCommand with catchStderr = true, because // We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout // javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true).then(function (output) { return tryCommand('javac -version', msg, true)
// Let's check for at least Java 8, and keep it future proof so we can support Java 10 .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); var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
return match && match[1]; return match && match[1];
}); });
@@ -224,13 +219,13 @@ module.exports.check_java = function () {
}; };
// Returns a promise. // Returns a promise.
module.exports.check_android = function () { module.exports.check_android = function() {
return Q().then(function () { return Q().then(function() {
var androidCmdPath = forgivingWhichSync('android'); var androidCmdPath = forgivingWhichSync('android');
var adbInPath = forgivingWhichSync('adb'); var adbInPath = forgivingWhichSync('adb');
var avdmanagerInPath = forgivingWhichSync('avdmanager'); var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']); var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
function maybeSetAndroidHome (value) { function maybeSetAndroidHome(value) {
if (!hasAndroidHome && fs.existsSync(value)) { if (!hasAndroidHome && fs.existsSync(value)) {
hasAndroidHome = true; hasAndroidHome = true;
process.env['ANDROID_HOME'] = value; process.env['ANDROID_HOME'] = value;
@@ -270,10 +265,10 @@ module.exports.check_android = function () {
if (androidCmdPath) { if (androidCmdPath) {
parentDir = path.dirname(androidCmdPath); parentDir = path.dirname(androidCmdPath);
grandParentDir = path.dirname(parentDir); 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); maybeSetAndroidHome(grandParentDir);
} else { } 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' + '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.'); '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) { if (adbInPath) {
parentDir = path.dirname(adbInPath); parentDir = path.dirname(adbInPath);
grandParentDir = path.dirname(parentDir); grandParentDir = path.dirname(parentDir);
if (path.basename(parentDir) === 'platform-tools') { if (path.basename(parentDir) == 'platform-tools') {
maybeSetAndroidHome(grandParentDir); maybeSetAndroidHome(grandParentDir);
} else { } 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' + '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.'); '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) { if (avdmanagerInPath) {
parentDir = path.dirname(avdmanagerInPath); parentDir = path.dirname(avdmanagerInPath);
grandParentDir = path.dirname(parentDir); 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)); maybeSetAndroidHome(path.dirname(grandParentDir));
} else { } 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' + '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.'); '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']) { 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.'); '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'])) { if (!fs.existsSync(process.env['ANDROID_HOME'])) {
@@ -335,19 +330,20 @@ module.exports.getAbsoluteAndroidCmd = function () {
return cmd.replace(/(\s)/g, '\\$1'); 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: // valid_target can look like:
// android-19 // android-19
// android-L // android-L
// Google Inc.:Google APIs:20 // Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20 // Google Inc.:Glass Development Kit Preview:20
var desired_api_level = module.exports.get_target(); 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) { if (targets.indexOf(desired_api_level) >= 0) {
return targets; return targets;
} }
var androidCmd = module.exports.getAbsoluteAndroidCmd(); 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' + 'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
'You will require:\n' + 'You will require:\n' +
'1. "SDK Platform" for API level ' + desired_api_level + '\n' + '1. "SDK Platform" for API level ' + desired_api_level + '\n' +
@@ -361,21 +357,23 @@ module.exports.check_android_target = function (originalError) {
}; };
// Returns a promise. // Returns a promise.
module.exports.run = function () { module.exports.run = function() {
return Q.all([this.check_java(), this.check_android()]).then(function (values) { return Q.all([this.check_java(), this.check_android()])
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']); .then(function(values) {
console.log('JAVA_HOME=' + process.env['JAVA_HOME']); console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
if (!values[0]) { if (!values[0]) {
throw new CordovaError('Requirements check failed for JDK 1.8 or greater'); throw new CordovaError('Requirements check failed for JDK 1.8 or greater');
} }
if (!values[1]) { if (!values[1]) {
throw new CordovaError('Requirements check failed for Android SDK'); throw new CordovaError('Requirements check failed for Android SDK');
} }
}); });
}; };
/** /**
* Object thar represents one of requirements for current platform. * Object thar represents one of requirements for current platform.
* @param {String} id The unique identifier for this requirements. * @param {String} id The unique identifier for this requirements.
@@ -389,7 +387,7 @@ var Requirement = function (id, name, version, installed) {
this.name = name; this.name = name;
this.installed = installed || false; this.installed = installed || false;
this.metadata = { this.metadata = {
version: version version: version,
}; };
}; };
@@ -399,7 +397,7 @@ var Requirement = function (id, name, version, installed) {
* *
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled. * @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 = [ var requirements = [
new Requirement('java', 'Java JDK'), new Requirement('java', 'Java JDK'),
@@ -419,13 +417,15 @@ module.exports.check_all = function () {
return checkFns.reduce(function (promise, checkFn, idx) { return checkFns.reduce(function (promise, checkFn, idx) {
// Update each requirement with results // Update each requirement with results
var requirement = requirements[idx]; var requirement = requirements[idx];
return promise.then(checkFn).then(function (version) { return promise.then(checkFn)
.then(function (version) {
requirement.installed = true; requirement.installed = true;
requirement.metadata.version = version; requirement.metadata.version = version;
}, function (err) { }, function (err) {
requirement.metadata.reason = err instanceof Error ? err.message : 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 // When chain is completed, return requirements array to upstream API
return requirements; return requirements;
}); });

View File

@@ -19,8 +19,8 @@
under the License. under the License.
*/ */
var Q = require('q'); var Q = require('q'),
var build = require('./build'); build = require('./build');
var path = require('path'); var path = require('path');
var Adb = require('./Adb'); var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest'); var AndroidManifest = require('./AndroidManifest');
@@ -32,16 +32,18 @@ var events = require('cordova-common').events;
* Returns a promise for the list of the device ID's found * Returns a promise for the list of the device ID's found
* @param lookHarder When true, try restarting adb if no devices are found. * @param lookHarder When true, try restarting adb if no devices are found.
*/ */
module.exports.list = function (lookHarder) { module.exports.list = function(lookHarder) {
return Adb.devices().then(function (list) { return Adb.devices()
.then(function(list) {
if (list.length === 0 && lookHarder) { if (list.length === 0 && lookHarder) {
// adb kill-server doesn't seem to do the trick. // adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually // Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines. // 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.'); events.emit('verbose', 'Restarting adb to see if more devices are detected.');
return Adb.devices(); return Adb.devices();
}, function () { }, function() {
// For non-killall OS's. // For non-killall OS's.
return list; return list;
}); });
@@ -50,8 +52,9 @@ module.exports.list = function (lookHarder) {
}); });
}; };
module.exports.resolveTarget = function (target) { module.exports.resolveTarget = function(target) {
return this.list(true).then(function (device_list) { return this.list(true)
.then(function(device_list) {
if (!device_list || !device_list.length) { if (!device_list || !device_list.length) {
return Q.reject(new CordovaError('Failed to deploy to device, no devices found.')); return Q.reject(new CordovaError('Failed to deploy to device, no devices found.'));
} }
@@ -62,7 +65,8 @@ module.exports.resolveTarget = function (target) {
return Q.reject('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 }; return { target: target, arch: arch, isEmulator: false };
}); });
}); });
@@ -73,39 +77,43 @@ module.exports.resolveTarget = function (target) {
* and launches it. * and launches it.
* Returns a promise. * Returns a promise.
*/ */
module.exports.install = function (target, buildResults) { module.exports.install = function(target, buildResults) {
return Q().then(function () { return Q().then(function() {
if (target && typeof target === 'object') { if (target && typeof target == 'object') {
return target; return target;
} }
return module.exports.resolveTarget(target); return module.exports.resolveTarget(target);
}).then(function (resolvedTarget) { }).then(function(resolvedTarget) {
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch); 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 pkgName = manifest.getPackageId();
var launchName = pkgName + '/.' + manifest.getActivity().getName(); var launchName = pkgName + '/.' + manifest.getActivity().getName();
events.emit('log', 'Using apk: ' + apk_path); events.emit('log', 'Using apk: ' + apk_path);
events.emit('log', 'Package name: ' + pkgName); 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 // CB-9557 CB-10157 only uninstall and reinstall app if the one that
// is already installed on device was signed w/different certificate // 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 ' + events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
'installed app already signed with different key'); 'installed app already signed with different key');
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app // 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. // or the app doesn't installed at all, so no error catching needed.
return Adb.uninstall(resolvedTarget.target, pkgName).then(function () { return Adb.uninstall(resolvedTarget.target, pkgName)
.then(function() {
return Adb.install(resolvedTarget.target, apk_path, {replace: true}); return Adb.install(resolvedTarget.target, apk_path, {replace: true});
}); });
}).then(function () { })
// unlock screen .then(function() {
//unlock screen
return Adb.shell(resolvedTarget.target, 'input keyevent 82'); return Adb.shell(resolvedTarget.target, 'input keyevent 82');
}).then(function () { }).then(function() {
return Adb.start(resolvedTarget.target, launchName); return Adb.start(resolvedTarget.target, launchName);
}).then(function () { }).then(function() {
events.emit('log', 'LAUNCH SUCCESS'); events.emit('log', 'LAUNCH SUCCESS');
}); });
}); });

View File

@@ -21,9 +21,8 @@
/* jshint sub:true */ /* jshint sub:true */
var android_versions = require('android-versions'); var retry = require('./retry');
var retry = require('./retry'); var build = require('./build');
var build = require('./build');
var path = require('path'); var path = require('path');
var Adb = require('./Adb'); var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest'); var AndroidManifest = require('./AndroidManifest');
@@ -34,20 +33,20 @@ var shelljs = require('shelljs');
var android_sdk = require('./android_sdk'); var android_sdk = require('./android_sdk');
var check_reqs = require('./check_reqs'); var check_reqs = require('./check_reqs');
var Q = require('q'); var Q = require('q');
var os = require('os'); var os = require('os');
var fs = require('fs'); var fs = require('fs');
var child_process = require('child_process'); var child_process = require('child_process');
// constants // constants
var ONE_SECOND = 1000; // in milliseconds var ONE_SECOND = 1000; // in milliseconds
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
var NUM_INSTALL_RETRIES = 3; var NUM_INSTALL_RETRIES = 3;
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
var EXEC_KILL_SIGNAL = 'SIGKILL'; var EXEC_KILL_SIGNAL = 'SIGKILL';
function forgivingWhichSync (cmd) { function forgivingWhichSync(cmd) {
try { try {
return fs.realpathSync(shelljs.which(cmd)); return fs.realpathSync(shelljs.which(cmd));
} catch (e) { } catch (e) {
@@ -56,7 +55,8 @@ function forgivingWhichSync (cmd) {
} }
module.exports.list_images_using_avdmanager = function () { 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 response = output.split('\n');
var emulator_list = []; var emulator_list = [];
for (var i = 1; i < response.length; i++) { for (var i = 1; i < response.length; i++) {
@@ -108,15 +108,16 @@ module.exports.list_images_using_avdmanager = function () {
/* To just return a list of names use this /* To just return a list of names use this
if (response[i].match(/Name:\s/)) { if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', ''); emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
} */ }*/
} }
return emulator_list; return emulator_list;
}); });
}; };
module.exports.list_images_using_android = function () { module.exports.list_images_using_android = function() {
return superspawn.spawn('android', ['list', 'avd']).then(function (output) { return superspawn.spawn('android', ['list', 'avds'])
.then(function(output) {
var response = output.split('\n'); var response = output.split('\n');
var emulator_list = []; var emulator_list = [];
for (var i = 1; i < response.length; i++) { for (var i = 1; i < response.length; i++) {
@@ -151,7 +152,7 @@ module.exports.list_images_using_android = function () {
/* To just return a list of names use this /* To just return a list of names use this
if (response[i].match(/Name:\s/)) { if (response[i].match(/Name:\s/)) {
emulator_list.push(response[i].split('Name: ')[1].replace('\r', ''); emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
} */ }*/
} }
return emulator_list; return emulator_list;
@@ -169,30 +170,26 @@ module.exports.list_images_using_android = function () {
skin : <skin> skin : <skin>
} }
*/ */
module.exports.list_images = function () { module.exports.list_images = function() {
return Q.fcall(function () { if (forgivingWhichSync('android')) {
if (forgivingWhichSync('avdmanager')) { return module.exports.list_images_using_android()
return module.exports.list_images_using_avdmanager(); .catch(function(err) {
} else if (forgivingWhichSync('android')) { // try to use `avdmanager` in case `android` reports it is no longer available.
return module.exports.list_images_using_android(); // this likely means the target machine is using a newer version of
} else { // the android sdk, and possibly `avdmanager` is available.
return Q().then(function () { if (err.code == 1 && err.stdout.indexOf('android command is no longer available')) {
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?'); return module.exports.list_images_using_avdmanager();
}); } else {
} throw err;
}).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);
avd.target = 'Android ' + level.semver + ' (API level ' + api_level + ')';
}
} }
return avd;
}); });
}); } else if (forgivingWhichSync('avdmanager')) {
return module.exports.list_images_using_avdmanager();
} 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?');
});
}
}; };
/** /**
@@ -200,19 +197,20 @@ module.exports.list_images = function () {
* or undefined if no avds exist. * or undefined if no avds exist.
* Returns a promise. * Returns a promise.
*/ */
module.exports.best_image = function () { module.exports.best_image = function() {
return this.list_images().then(function (images) { return this.list_images()
.then(function(images) {
// Just return undefined if there is no images // Just return undefined if there is no images
if (images.length === 0) return; if (images.length === 0) return;
var closest = 9999; var closest = 9999;
var best = images[0]; 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) { for (var i in images) {
var target = images[i].target; var target = images[i].target;
if (target && target.indexOf('API level') > -1) { if(target) {
var num = parseInt(target.split('(API level ')[1].replace(')', '')); var num = target.split('(API level ')[1].replace(')', '');
if (num === project_target) { if (num == project_target) {
return images[i]; return images[i];
} else if (project_target - num < closest && project_target > num) { } else if (project_target - num < closest && project_target > num) {
closest = project_target - num; closest = project_target - num;
@@ -225,18 +223,18 @@ module.exports.best_image = function () {
}; };
// Returns a promise. // Returns a promise.
module.exports.list_started = function () { module.exports.list_started = function() {
return Adb.devices({emulators: true}); return Adb.devices({emulators: true});
}; };
// Returns a promise. // Returns a promise.
// TODO: we should remove this, there's a more robust method under android_sdk.js module.exports.list_targets = function() {
module.exports.list_targets = function () { return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}).then(function (output) { .then(function(output) {
var target_out = output.split('\n'); var target_out = output.split('\n');
var targets = []; var targets = [];
for (var i = target_out.length; i >= 0; i--) { 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]); targets.push(targets[i].split(' ')[1]);
} }
} }
@@ -251,8 +249,9 @@ module.exports.list_targets = function () {
module.exports.get_available_port = function () { module.exports.get_available_port = function () {
var self = this; var self = this;
return self.list_started().then(function (emulators) { return self.list_started()
for (var p = 5584; p >= 5554; p -= 2) { .then(function (emulators) {
for (var p = 5584; p >= 5554; p-=2) {
if (emulators.indexOf('emulator-' + p) === -1) { if (emulators.indexOf('emulator-' + p) === -1) {
events.emit('verbose', 'Found available port: ' + p); events.emit('verbose', 'Found available port: ' + p);
return p; return p;
@@ -272,13 +271,14 @@ module.exports.get_available_port = function () {
* *
* Returns a promise. * Returns a promise.
*/ */
module.exports.start = function (emulator_ID, boot_timeout) { module.exports.start = function(emulator_ID, boot_timeout) {
var self = this; var self = this;
return Q().then(function () { return Q().then(function() {
if (emulator_ID) return Q(emulator_ID); 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) { if (best && best.name) {
events.emit('warn', 'No emulator specified, defaulting to ' + best.name); events.emit('warn', 'No emulator specified, defaulting to ' + best.name);
return best.name; return best.name;
@@ -290,8 +290,9 @@ module.exports.start = function (emulator_ID, boot_timeout) {
'2. Create an AVD by running: ' + androidCmd + ' avd\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')); 'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n'));
}); });
}).then(function (emulatorId) { }).then(function(emulatorId) {
return self.get_available_port().then(function (port) { return self.get_available_port()
.then(function (port) {
// Figure out the directory the emulator binary runs in, and set the cwd to that directory. // 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 // Workaround for https://code.google.com/p/android/issues/detail?id=235461
var emulator_dir = path.dirname(shelljs.which('emulator')); var emulator_dir = path.dirname(shelljs.which('emulator'));
@@ -305,17 +306,20 @@ module.exports.start = function (emulator_ID, boot_timeout) {
events.emit('log', 'Waiting for emulator to start...'); events.emit('log', 'Waiting for emulator to start...');
return self.wait_for_emulator(port); return self.wait_for_emulator(port);
}); });
}).then(function (emulatorId) { }).then(function(emulatorId) {
if (!emulatorId) { return Q.reject(new CordovaError('Failed to start emulator')); } 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)...'); 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) { if (success) {
events.emit('log', 'BOOT COMPLETE'); events.emit('log','BOOT COMPLETE');
// unlock screen //unlock screen
return Adb.shell(emulatorId, 'input keyevent 82').then(function () { return Adb.shell(emulatorId, 'input keyevent 82')
// return the new emulator id for the started emulators .then(function() {
//return the new emulator id for the started emulators
return emulatorId; return emulatorId;
}); });
} else { } else {
@@ -330,19 +334,20 @@ module.exports.start = function (emulator_ID, boot_timeout) {
* Waits for an emulator to boot on a given port. * Waits for an emulator to boot on a given port.
* Returns this emulator's ID in a promise. * 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; var self = this;
return Q().then(function () { return Q().then(function() {
var emulator_id = 'emulator-' + port; 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) { if (output.indexOf('1') >= 0) {
return emulator_id; return emulator_id;
} }
return self.wait_for_emulator(port); return self.wait_for_emulator(port);
}, function (error) { }, function (error) {
if ((error && error.message && if (error && error.message &&
(error.message.indexOf('not found') > -1)) || (error.message.indexOf('not found') > -1) ||
(error.message.indexOf('device offline') > -1)) { error.message.indexOf('device offline') > -1) {
// emulator not yet started, continue waiting // emulator not yet started, continue waiting
return self.wait_for_emulator(port); return self.wait_for_emulator(port);
} else { } else {
@@ -350,7 +355,7 @@ module.exports.wait_for_emulator = function (port) {
throw error; throw error;
} }
}); });
}); });
}; };
/* /*
@@ -358,9 +363,10 @@ module.exports.wait_for_emulator = function (port) {
* promise that resolves to a boolean indicating success. Not specifying a * promise that resolves to a boolean indicating success. Not specifying a
* time_remaining or passing a negative value will cause it to wait forever * 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; 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/)) { if (output.match(/android\.process\.acore/)) {
return true; return true;
} else if (time_remaining === 0) { } else if (time_remaining === 0) {
@@ -369,7 +375,7 @@ module.exports.wait_for_boot = function (emulator_id, time_remaining) {
process.stdout.write('.'); process.stdout.write('.');
// Check at regular intervals // Check at regular intervals
return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function () { 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; var updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining;
return self.wait_for_boot(emulator_id, updated_time); return self.wait_for_boot(emulator_id, updated_time);
}); });
@@ -382,31 +388,33 @@ module.exports.wait_for_boot = function (emulator_id, time_remaining) {
* TODO : Enter the stdin input required to complete the creation of an avd. * TODO : Enter the stdin input required to complete the creation of an avd.
* Returns a promise. * Returns a promise.
*/ */
module.exports.create_image = function (name, target) { module.exports.create_image = function(name, target) {
console.log('Creating new avd named ' + name); console.log('Creating new avd named ' + name);
if (target) { 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('ERROR : Failed to create emulator image : ');
console.error(' Do you have the latest android targets including ' + target + '?'); console.error(' Do you have the latest android targets including ' + target + '?');
console.error(error); console.error(error);
}); });
} else { } else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.'); 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]])
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () { .then(function() {
// TODO: This seems like another error case, even though it always happens. // 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('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Ensure you have targets available by running the "android" command'); console.error('Ensure you have targets available by running the "android" command');
return Q.reject(); return Q.reject();
}, function (error) { }, function(error) {
console.error('ERROR : Failed to create emulator image : '); console.error('ERROR : Failed to create emulator image : ');
console.error(error); console.error(error);
}); });
} }
}; };
module.exports.resolveTarget = function (target) { module.exports.resolveTarget = function(target) {
return this.list_started().then(function (emulator_list) { return this.list_started()
.then(function(emulator_list) {
if (emulator_list.length < 1) { if (emulator_list.length < 1) {
return Q.reject('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.');
} }
@@ -417,8 +425,9 @@ module.exports.resolveTarget = function (target) {
return Q.reject('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 build.detectArchitecture(target)
return {target: target, arch: arch, isEmulator: true}; .then(function(arch) {
return {target:target, arch:arch, isEmulator:true};
}); });
}); });
}; };
@@ -429,20 +438,15 @@ module.exports.resolveTarget = function (target) {
* If no started emulators are found, error out. * If no started emulators are found, error out.
* Returns a promise. * Returns a promise.
*/ */
module.exports.install = function (givenTarget, buildResults) { module.exports.install = function(givenTarget, buildResults) {
var target; var target;
// We need to find the proper path to the Android Manifest var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
var manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml');
if (buildResults.buildMethod === 'gradle') {
manifestPath = path.join(__dirname, '../../AndroidManifest.xml');
}
var manifest = new AndroidManifest(manifestPath);
var pkgName = manifest.getPackageId(); var pkgName = manifest.getPackageId();
// resolve the target emulator // resolve the target emulator
return Q().then(function () { return Q().then(function () {
if (givenTarget && typeof givenTarget === 'object') { if (givenTarget && typeof givenTarget == 'object') {
return givenTarget; return givenTarget;
} else { } else {
return module.exports.resolveTarget(givenTarget); return module.exports.resolveTarget(givenTarget);
@@ -456,12 +460,13 @@ module.exports.install = function (givenTarget, buildResults) {
}).then(function () { }).then(function () {
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app // 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. // or the app doesn't installed at all, so no error catching needed.
return Q.when().then(function () { return Q.when()
.then(function() {
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch); var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
var execOptions = { var execOptions = {
cwd: os.tmpdir(), cwd: os.tmpdir(),
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
killSignal: EXEC_KILL_SIGNAL killSignal: EXEC_KILL_SIGNAL
}; };
@@ -472,12 +477,12 @@ module.exports.install = function (givenTarget, buildResults) {
// A special function to call adb install in specific environment w/ specific options. // 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 // Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119
// to workaround sporadic emulator hangs // to workaround sporadic emulator hangs
function adbInstallWithOptions (target, apk, opts) { function adbInstallWithOptions(target, apk, opts) {
events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...'); events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
var command = 'adb -s ' + target + ' install -r "' + apk + '"'; var command = 'adb -s ' + target + ' install -r "' + apk + '"';
return Q.promise(function (resolve, reject) { return Q.promise(function (resolve, reject) {
child_process.exec(command, opts, function (err, stdout, stderr) { child_process.exec(command, opts, function(err, stdout, stderr) {
if (err) reject(new CordovaError('Error executing "' + command + '": ' + 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 // 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. // message to stdout, so we have to use RegExp matching to detect installation failure.
@@ -497,23 +502,27 @@ module.exports.install = function (givenTarget, buildResults) {
} }
function installPromise () { 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 // CB-9557 CB-10157 only uninstall and reinstall app if the one that
// is already installed on device was signed w/different certificate // 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 ' + events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
'currently installed app was signed with different key'); 'currently installed app was signed with different key');
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app // 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. // 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 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'); 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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,28 +19,28 @@
under the License. under the License.
*/ */
var path = require('path'); var path = require('path'),
var os = require('os'); os = require('os'),
var Q = require('q'); Q = require('q'),
var child_process = require('child_process'); child_process = require('child_process'),
var ROOT = path.join(__dirname, '..', '..'); ROOT = path.join(__dirname, '..', '..');
/* /*
* Starts running logcat in the shell. * Starts running logcat in the shell.
* Returns a promise. * Returns a promise.
*/ */
module.exports.run = function () { module.exports.run = function() {
var d = Q.defer(); 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 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')); console.log(out.join('\n'));
}); });
adb.stderr.on('data', console.error); adb.stderr.on('data', console.error);
adb.on('close', function (code) { adb.on('close', function(code) {
if (code > 0) { if (code > 0) {
d.reject('Failed to run logcat command.'); d.reject('Failed to run logcat command.');
} else d.resolve(); } else d.resolve();
@@ -49,7 +49,7 @@ module.exports.run = function () {
return d.promise; 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('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log')));
console.log('Gives the logcat output on the command line.'); console.log('Gives the logcat output on the command line.');
process.exit(0); process.exit(0);

View File

@@ -20,10 +20,8 @@
buildscript { buildscript {
repositories { repositories {
mavenCentral()
jcenter() jcenter()
maven {
url "https://maven.google.com"
}
} }
// Switch the Android Gradle plugin version requirement depending on the // Switch the Android Gradle plugin version requirement depending on the

View File

@@ -1,4 +1,7 @@
/* /*
*
* Copyright 2013 Anis Kadri
*
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
@@ -23,26 +26,15 @@ var events = require('cordova-common').events;
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var handlers = { var handlers = {
'source-file': { 'source-file':{
install: function (obj, plugin, project, options) { install:function(obj, plugin, project, options) {
if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id)); 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)); if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));
var dest = path.join(obj.targetDir, path.basename(obj.src)); var dest = path.join(obj.targetDir, path.basename(obj.src));
// TODO: This code needs to be replaced, since the core plugins need to be re-mapped to a different location in if(options && options.android_studio === true) {
// a later plugins release. This is for legacy plugins to work with Cordova. dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
if (options && options.android_studio === true) {
// If a Java file is using the new directory structure, don't penalize it
if (!obj.targetDir.includes('app/src/main')) {
if (obj.src.endsWith('.java')) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
} else if (obj.src.endsWith('.xml')) {
// We are making a huge assumption here that XML files will be going to res/xml or values/xml
dest = path.join('app/src/main', obj.targetDir, path.basename(obj.src));
}
}
} }
if (options && options.force) { if (options && options.force) {
@@ -51,50 +43,42 @@ var handlers = {
copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
} }
}, },
uninstall: function (obj, plugin, project, options) { uninstall:function(obj, plugin, project, options) {
var dest = path.join(obj.targetDir, path.basename(obj.src)); var dest = path.join(obj.targetDir, path.basename(obj.src));
if (options && options.android_studio === true) { if(options && options.android_studio === true) {
dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src)); dest = path.join('app/src/main/java', obj.targetDir.substring(4), path.basename(obj.src));
} }
deleteJava(project.projectDir, dest); deleteJava(project.projectDir, dest);
} }
}, },
'lib-file': { 'lib-file':{
install: function (obj, plugin, project, options) { install:function(obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src)); var dest = path.join('libs', path.basename(obj.src));
if (options && options.android_studio === true) { if(options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src)); dest = path.join('app/libs', path.basename(obj.src));
} }
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
}, },
uninstall: function (obj, plugin, project, options) { uninstall:function(obj, plugin, project, options) {
var dest = path.join('libs', path.basename(obj.src)); var dest = path.join('libs', path.basename(obj.src));
if (options && options.android_studio === true) { if(options && options.android_studio === true) {
dest = path.join('app/libs', path.basename(obj.src)); dest = path.join('app/libs', path.basename(obj.src));
} }
removeFile(project.projectDir, dest); removeFile(project.projectDir, dest);
} }
}, },
'resource-file': { 'resource-file':{
install: function (obj, plugin, project, options) { install:function(obj, plugin, project, options) {
var dest = path.normalize(obj.target); copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
if (options && options.android_studio === true) {
dest = path.join('app/src/main', dest);
}
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
}, },
uninstall: function (obj, plugin, project, options) { uninstall:function(obj, plugin, project, options) {
var dest = path.normalize(obj.target); removeFile(project.projectDir, path.normalize(obj.target));
if (options && options.android_studio === true) {
dest = path.join('app/src/main', dest);
}
removeFile(project.projectDir, dest);
} }
}, },
'framework': { 'framework': {
install: function (obj, plugin, project, options) { install:function(obj, plugin, project, options) {
var src = obj.src; var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id)); if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
@@ -111,15 +95,15 @@ var handlers = {
subDir = src; subDir = src;
} }
if (obj.type === 'gradleReference') { if (obj.type == 'gradleReference') {
project.addGradleReference(parentDir, subDir); project.addGradleReference(parentDir, subDir);
} else if (obj.type === 'sys') { } else if (obj.type == 'sys') {
project.addSystemLibrary(parentDir, subDir); project.addSystemLibrary(parentDir, subDir);
} else { } else {
project.addSubProject(parentDir, subDir); project.addSubProject(parentDir, subDir);
} }
}, },
uninstall: function (obj, plugin, project, options) { uninstall:function(obj, plugin, project, options) {
var src = obj.src; var src = obj.src;
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id)); if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
@@ -141,17 +125,17 @@ var handlers = {
subDir = src; subDir = src;
} }
if (obj.type === 'gradleReference') { if (obj.type == 'gradleReference') {
project.removeGradleReference(parentDir, subDir); project.removeGradleReference(parentDir, subDir);
} else if (obj.type === 'sys') { } else if (obj.type == 'sys') {
project.removeSystemLibrary(parentDir, subDir); project.removeSystemLibrary(parentDir, subDir);
} else { } else {
project.removeSubProject(parentDir, subDir); project.removeSubProject(parentDir, subDir);
} }
} }
}, },
asset: { asset:{
install: function (obj, plugin, project, options) { install:function(obj, plugin, project, options) {
if (!obj.src) { if (!obj.src) {
throw new CordovaError(generateAttributeError('src', 'asset', plugin.id)); throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
} }
@@ -165,7 +149,7 @@ var handlers = {
copyFile(plugin.dir, obj.src, project.platformWww, obj.target); 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; var target = obj.target || obj.src;
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id)); if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
@@ -183,7 +167,7 @@ var handlers = {
install: function (obj, plugin, project, options) { install: function (obj, plugin, project, options) {
// Copy the plugin's files into the www directory. // Copy the plugin's files into the www directory.
var moduleSource = path.resolve(plugin.dir, obj.src); 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. // Read in the file, prepend the cordova.define, and write it back out.
var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
@@ -222,7 +206,7 @@ module.exports.getInstaller = function (type) {
events.emit('verbose', '<' + type + '> is not supported for android plugins'); 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) { if (handlers[type] && handlers[type].uninstall) {
return handlers[type].uninstall; return handlers[type].uninstall;
} }
@@ -237,19 +221,21 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// check that src path is inside plugin directory // check that src path is inside plugin directory
var real_path = fs.realpathSync(src); var real_path = fs.realpathSync(src);
var real_plugin_path = fs.realpathSync(plugin_dir); 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); dest = path.resolve(project_dir, dest);
// check that dest path is located in project directory // 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)); shell.mkdir('-p', path.dirname(dest));
if (link) { if (link) {
symlinkFileOrDirTree(src, dest); symlinkFileOrDirTree(src, dest);
} else if (fs.statSync(src).isDirectory()) { } else if (fs.statSync(src).isDirectory()) {
// XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq // 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 { } else {
shell.cp('-f', src, dest); shell.cp('-f', src, dest);
} }
@@ -258,22 +244,24 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
// Same as copy file but throws error if target exists // Same as copy file but throws error if target exists
function copyNewFile (plugin_dir, src, project_dir, dest, link) { function copyNewFile (plugin_dir, src, project_dir, dest, link) {
var target_path = path.resolve(project_dir, dest); 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); copyFile(plugin_dir, src, project_dir, dest, !!link);
} }
function symlinkFileOrDirTree (src, dest) { function symlinkFileOrDirTree(src, dest) {
if (fs.existsSync(dest)) { if (fs.existsSync(dest)) {
shell.rm('-Rf', dest); shell.rm('-Rf', dest);
} }
if (fs.statSync(src).isDirectory()) { if (fs.statSync(src).isDirectory()) {
shell.mkdir('-p', dest); 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)); symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
}); });
} else { }
else {
fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest); fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
} }
} }
@@ -304,8 +292,8 @@ function removeFileAndParents (baseDir, destFile, stopper) {
// check if directory is empty // check if directory is empty
var curDir = path.dirname(file); var curDir = path.dirname(file);
while (curDir !== path.resolve(baseDir, stopper)) { while(curDir !== path.resolve(baseDir, stopper)) {
if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) { if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
fs.rmdirSync(curDir); fs.rmdirSync(curDir);
curDir = path.resolve(curDir, '..'); curDir = path.resolve(curDir, '..');
} else { } else {
@@ -315,6 +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; return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
} }

View File

@@ -16,7 +16,6 @@
specific language governing permissions and limitations specific language governing permissions and limitations
under the License. under the License.
*/ */
/* eslint no-useless-escape: 0 */
var Q = require('q'); var Q = require('q');
var fs = require('fs'); var fs = require('fs');
@@ -24,7 +23,6 @@ var path = require('path');
var shell = require('shelljs'); var shell = require('shelljs');
var events = require('cordova-common').events; var events = require('cordova-common').events;
var AndroidManifest = require('./AndroidManifest'); var AndroidManifest = require('./AndroidManifest');
var checkReqs = require('./check_reqs');
var xmlHelpers = require('cordova-common').xmlHelpers; var xmlHelpers = require('cordova-common').xmlHelpers;
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var ConfigParser = require('cordova-common').ConfigParser; var ConfigParser = require('cordova-common').ConfigParser;
@@ -42,14 +40,17 @@ module.exports.prepare = function (cordovaProject, options) {
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations); this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
// Update own www dir with project's www assets and plugins' assets and js-files // 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. // update project according to config.xml changes.
return updateProjectAccordingTo(self._config, self.locations); return updateProjectAccordingTo(self._config, self.locations);
}).then(function () { })
.then(function () {
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res)); updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateSplashes(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)); updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
}).then(function () { })
.then(function () {
events.emit('verbose', 'Prepared android project successfully'); events.emit('verbose', 'Prepared android project successfully');
}); });
}; };
@@ -90,7 +91,7 @@ module.exports.clean = function (options) {
* represents current project's configuration. When returned, the * represents current project's configuration. When returned, the
* configuration is already dumped to appropriate config.xml file. * 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); 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 // First cleanup current config and merge project's one into own
@@ -105,7 +106,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
// Merge changes from app's config.xml into platform's one // Merge changes from app's config.xml into platform's one
var config = new ConfigParser(locations.configXml); var config = new ConfigParser(locations.configXml);
xmlHelpers.mergeXml(sourceConfig.doc.getroot(), xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
config.doc.getroot(), 'android', /* clobber= */true); config.doc.getroot(), 'android', /*clobber=*/true);
config.write(); config.write();
return config; return config;
@@ -114,7 +115,7 @@ function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
/** /**
* Logs all file operations via the verbose event stream, indented. * Logs all file operations via the verbose event stream, indented.
*/ */
function logFileOp (message) { function logFileOp(message) {
events.emit('verbose', ' ' + message); events.emit('verbose', ' ' + message);
} }
@@ -127,7 +128,7 @@ function logFileOp (message) {
* @param {Object} destinations An object that contains destination * @param {Object} destinations An object that contains destination
* paths for www files. * paths for www files.
*/ */
function updateWww (cordovaProject, destinations) { function updateWww(cordovaProject, destinations) {
var sourceDirs = [ var sourceDirs = [
path.relative(cordovaProject.root, cordovaProject.locations.www), path.relative(cordovaProject.root, cordovaProject.locations.www),
path.relative(cordovaProject.root, destinations.platformWww) path.relative(cordovaProject.root, destinations.platformWww)
@@ -150,7 +151,7 @@ function updateWww (cordovaProject, destinations) {
/** /**
* Cleans all files from the platform 'www' directory. * Cleans all files from the platform 'www' directory.
*/ */
function cleanWww (projectRoot, locations) { function cleanWww(projectRoot, locations) {
var targetDir = path.relative(projectRoot, locations.www); var targetDir = path.relative(projectRoot, locations.www);
events.emit('verbose', 'Cleaning ' + targetDir); events.emit('verbose', 'Cleaning ' + targetDir);
@@ -166,26 +167,19 @@ function cleanWww (projectRoot, locations) {
* be used to update project * be used to update project
* @param {Object} locations A map of locations for this platform * @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 // Update app name by editing res/values/strings.xml
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
var name = platformConfig.name(); var name = platformConfig.name();
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
strings.find('string[@name="app_name"]').text = name.replace(/\'/g, '\\\''); strings.find('string[@name="app_name"]').text = name.replace(/\'/g, '\\\'');
var shortName = platformConfig.shortName && platformConfig.shortName();
if (shortName && shortName !== name) {
strings.find('string[@name="launcher_name"]').text = shortName.replace(/\'/g, '\\\'');
}
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); events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);
// Java packages cannot support dashes // 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 manifest = new AndroidManifest(locations.manifest);
var manifestId = manifest.getPackageId(); var orig_pkg = manifest.getPackageId();
manifest.getActivity() manifest.getActivity()
.setOrientation(platformConfig.getPreference('orientation')) .setOrientation(platformConfig.getPreference('orientation'))
@@ -193,41 +187,36 @@ function updateProjectAccordingTo (platformConfig, locations) {
manifest.setVersionName(platformConfig.version()) manifest.setVersionName(platformConfig.version())
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version())) .setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
.setPackageId(androidPkgName) .setPackageId(pkg)
.setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android')) .setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
.setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android')) .setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
.setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android')) .setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
.write(); .write();
// Java file paths shouldn't be hard coded var javaPattern = path.join(locations.root, 'src', orig_pkg.replace(/\./g, '/'), '*.java');
var javaPattern = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'), '*.java'); var java_files = shell.ls(javaPattern).filter(function(f) {
var java_files = shell.ls(javaPattern).filter(function (f) {
return shell.grep(/extends\s+CordovaActivity/g, f); return shell.grep(/extends\s+CordovaActivity/g, f);
}); });
if (java_files.length === 0) { if (java_files.length === 0) {
throw new CordovaError('No Java files found that extend CordovaActivity.'); 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]); 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.mkdir('-p', path.dirname(destFile));
shell.sed(/package [\w\.]*;/, 'package ' + androidPkgName + ';', java_files[0]).to(destFile); shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile);
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile); events.emit('verbose', 'Wrote out Android package name "' + pkg + '" to ' + destFile);
var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin() ? if (orig_pkg !== pkg) {
manifestId.toUpperCase() !== androidPkgName.toUpperCase() :
manifestId !== androidPkgName;
if (removeOrigPkg) {
// If package was name changed we need to remove old java with main activity // 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 // remove any empty directories
var currentDir = path.dirname(java_files[0]); var currentDir = path.dirname(java_files[0]);
var sourcesRoot = path.resolve(locations.root, 'src'); var sourcesRoot = path.resolve(locations.root, 'src');
while (currentDir !== sourcesRoot) { while(currentDir !== sourcesRoot) {
if (fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) { if(fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
fs.rmdirSync(currentDir); fs.rmdirSync(currentDir);
currentDir = path.resolve(currentDir, '..'); currentDir = path.resolve(currentDir, '..');
} else { } else {
@@ -240,7 +229,7 @@ function updateProjectAccordingTo (platformConfig, locations) {
// Consturct the default value for versionCode as // Consturct the default value for versionCode as
// PATCH + MINOR * 100 + MAJOR * 10000 // PATCH + MINOR * 100 + MAJOR * 10000
// see http://developer.android.com/tools/publishing/versioning.html // see http://developer.android.com/tools/publishing/versioning.html
function default_versionCode (version) { function default_versionCode(version) {
var nums = version.split('-')[0].split('.'); var nums = version.split('-')[0].split('.');
var versionCode = 0; var versionCode = 0;
if (+nums[0]) { if (+nums[0]) {
@@ -257,7 +246,7 @@ function default_versionCode (version) {
return versionCode; return versionCode;
} }
function getImageResourcePath (resourcesDir, type, density, name, sourceName) { function getImageResourcePath(resourcesDir, type, density, name, sourceName) {
if (/\.9\.png$/.test(sourceName)) { if (/\.9\.png$/.test(sourceName)) {
name = name.replace(/\.png$/, '.9.png'); name = name.replace(/\.png$/, '.9.png');
} }
@@ -265,7 +254,7 @@ function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
return resourcePath; return resourcePath;
} }
function updateSplashes (cordovaProject, platformResourcesDir) { function updateSplashes(cordovaProject, platformResourcesDir) {
var resources = cordovaProject.projectConfig.getSplashScreens('android'); var resources = cordovaProject.projectConfig.getSplashScreens('android');
// if there are "splash" elements in config.xml // if there are "splash" elements in config.xml
@@ -281,7 +270,7 @@ function updateSplashes (cordovaProject, platformResourcesDir) {
if (!resource.density) { if (!resource.density) {
return; return;
} }
if (resource.density === 'mdpi') { if (resource.density == 'mdpi') {
hadMdpi = true; hadMdpi = true;
} }
var targetPath = getImageResourcePath( var targetPath = getImageResourcePath(
@@ -301,7 +290,7 @@ function updateSplashes (cordovaProject, platformResourcesDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp); resourceMap, { rootDir: cordovaProject.root }, logFileOp);
} }
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) { function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) {
var resources = projectConfig.getSplashScreens('android'); var resources = projectConfig.getSplashScreens('android');
if (resources.length > 0) { if (resources.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png'); var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png');
@@ -313,7 +302,7 @@ function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
} }
} }
function updateIcons (cordovaProject, platformResourcesDir) { function updateIcons(cordovaProject, platformResourcesDir) {
var icons = cordovaProject.projectConfig.getIcons('android'); var icons = cordovaProject.projectConfig.getIcons('android');
// if there are icon elements in config.xml // if there are icon elements in config.xml
@@ -337,7 +326,7 @@ function updateIcons (cordovaProject, platformResourcesDir) {
}; };
// find the best matching icon for a given density or size // find the best matching icon for a given density or size
// @output android_icons // @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 // do I have a platform icon for that density already
var density = icon.density || sizeToDensityMap[icon_size]; var density = icon.density || sizeToDensityMap[icon_size];
if (!density) { if (!density) {
@@ -352,7 +341,7 @@ function updateIcons (cordovaProject, platformResourcesDir) {
}; };
// iterate over all icon elements to find the default icon and call parseIcon // 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 icon = icons[i];
var size = icon.width; var size = icon.width;
if (!size) { if (!size) {
@@ -389,7 +378,7 @@ function updateIcons (cordovaProject, platformResourcesDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp); resourceMap, { rootDir: cordovaProject.root }, logFileOp);
} }
function cleanIcons (projectRoot, projectConfig, platformResourcesDir) { function cleanIcons(projectRoot, projectConfig, platformResourcesDir) {
var icons = projectConfig.getIcons('android'); var icons = projectConfig.getIcons('android');
if (icons.length > 0) { if (icons.length > 0) {
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'icon.png'); var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'icon.png');
@@ -404,16 +393,18 @@ function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
/** /**
* Gets a map containing resources of a specified name from all drawable folders in a directory. * 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 = {}; 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); var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
pathMap[imagePath] = null; pathMap[imagePath] = null;
}); });
return pathMap; return pathMap;
} }
function updateFileResources (cordovaProject, platformDir) {
function updateFileResources(cordovaProject, platformDir) {
var files = cordovaProject.projectConfig.getFileResources('android'); var files = cordovaProject.projectConfig.getFileResources('android');
// if there are resource-file elements in config.xml // if there are resource-file elements in config.xml
@@ -423,7 +414,7 @@ function updateFileResources (cordovaProject, platformDir) {
} }
var resourceMap = {}; var resourceMap = {};
files.forEach(function (res) { files.forEach(function(res) {
var targetPath = path.join(platformDir, res.target); var targetPath = path.join(platformDir, res.target);
resourceMap[targetPath] = res.src; resourceMap[targetPath] = res.src;
}); });
@@ -433,20 +424,20 @@ function updateFileResources (cordovaProject, platformDir) {
resourceMap, { rootDir: cordovaProject.root }, logFileOp); 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) { if (files.length > 0) {
events.emit('verbose', 'Cleaning resource files at ' + platformDir); events.emit('verbose', 'Cleaning resource files at ' + platformDir);
var resourceMap = {}; var resourceMap = {};
files.forEach(function (res) { files.forEach(function(res) {
var filePath = path.join(platformDir, res.target); var filePath = path.join(platformDir, res.target);
resourceMap[filePath] = null; resourceMap[filePath] = null;
}); });
FileUpdater.updatePaths( FileUpdater.updatePaths(
resourceMap, { resourceMap, { rootDir: projectRoot, all: true}, logFileOp);
rootDir: projectRoot, all: true}, logFileOp);
} }
} }
@@ -461,7 +452,7 @@ function cleanFileResources (projectRoot, projectConfig, platformDir) {
* default value, if there is no such preference. The default value is * default value, if there is no such preference. The default value is
* 'singleTop' * 'singleTop'
*/ */
function findAndroidLaunchModePreference (platformConfig) { function findAndroidLaunchModePreference(platformConfig) {
var launchMode = platformConfig.getPreference('AndroidLaunchMode'); var launchMode = platformConfig.getPreference('AndroidLaunchMode');
if (!launchMode) { if (!launchMode) {
// Return a default value // Return a default value

View File

@@ -45,12 +45,12 @@ module.exports.retryPromise = function (attemts_left, promiseFunction) {
return promiseFunction.apply(undefined, promiseFunctionArguments).then( return promiseFunction.apply(undefined, promiseFunctionArguments).then(
// on success pass results through // on success pass results through
function onFulfilled (value) { function onFulfilled(value) {
return value; return value;
}, },
// on rejection either retry, or throw the error // on rejection either retry, or throw the error
function onRejected (error) { function onRejected(error) {
attemts_left -= 1; attemts_left -= 1;

View File

@@ -21,14 +21,14 @@
/* jshint loopfunc:true */ /* jshint loopfunc:true */
var path = require('path'); var path = require('path'),
var build = require('./build'); build = require('./build'),
var emulator = require('./emulator'); emulator = require('./emulator'),
var device = require('./device'); device = require('./device'),
var Q = require('q'); Q = require('q'),
var events = require('cordova-common').events; events = require('cordova-common').events;
function getInstallTarget (runOptions) { function getInstallTarget(runOptions) {
var install_target; var install_target;
if (runOptions.target) { if (runOptions.target) {
install_target = runOptions.target; install_target = runOptions.target;
@@ -51,15 +51,17 @@ function getInstallTarget (runOptions) {
* *
* @return {Promise} * @return {Promise}
*/ */
module.exports.run = function (runOptions) { module.exports.run = function(runOptions) {
var self = this; var self = this;
var install_target = getInstallTarget(runOptions); var install_target = getInstallTarget(runOptions);
return Q().then(function () { return Q()
.then(function() {
if (!install_target) { if (!install_target) {
// no target given, deploy to device if available, otherwise use the emulator. // 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) { if (device_list.length > 0) {
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.'); events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
install_target = device_list[0]; install_target = device_list[0];
@@ -69,31 +71,36 @@ module.exports.run = function (runOptions) {
} }
}); });
} }
}).then(function () { }).then(function() {
if (install_target === '--device') { if (install_target == '--device') {
return device.resolveTarget(null); return device.resolveTarget(null);
} else if (install_target === '--emulator') { } else if (install_target == '--emulator') {
// Give preference to any already started emulators. Else, start one. // 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(); return started && started.length > 0 ? started[0] : emulator.start();
}).then(function (emulatorId) { }).then(function(emulatorId) {
return emulator.resolveTarget(emulatorId); return emulator.resolveTarget(emulatorId);
}); });
} }
// They specified a specific device/emulator ID. // 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) { if (devices.indexOf(install_target) > -1) {
return device.resolveTarget(install_target); 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) { if (started_emulators.indexOf(install_target) > -1) {
return emulator.resolveTarget(install_target); 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. // if target emulator isn't started, then start it.
for (var avd in avds) { for (var avd in avds) {
if (avds[avd].name === install_target) { if (avds[avd].name == install_target) {
return emulator.start(install_target).then(function (emulatorId) { return emulator.start(install_target)
.then(function(emulatorId) {
return emulator.resolveTarget(emulatorId); return emulator.resolveTarget(emulatorId);
}); });
} }
@@ -102,14 +109,16 @@ module.exports.run = function (runOptions) {
}); });
}); });
}); });
}).then(function (resolvedTarget) { }).then(function(resolvedTarget) {
// Better just call self.build, but we're doing some processing of // Better just call self.build, but we're doing some processing of
// build results (according to platformApi spec) so they are in different // build results (according to platformApi spec) so they are in different
// format than emulator.install expects. // format than emulator.install expects.
// TODO: Update emulator/device.install to handle this change // TODO: Update emulator/device.install to handle this change
return build.run.call(self, runOptions, resolvedTarget).then(function (buildResults) { return build.run.call(self, runOptions, resolvedTarget)
.then(function(buildResults) {
if (resolvedTarget.isEmulator) { if (resolvedTarget.isEmulator) {
return emulator.wait_for_boot(resolvedTarget.target).then(function () { return emulator.wait_for_boot(resolvedTarget.target)
.then(function () {
return emulator.install(resolvedTarget, buildResults); return emulator.install(resolvedTarget, buildResults);
}); });
} }
@@ -118,7 +127,7 @@ module.exports.run = function (runOptions) {
}); });
}; };
module.exports.help = function () { module.exports.help = function() {
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + ' [options]'); console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + ' [options]');
console.log('Build options :'); console.log('Build options :');
console.log(' --debug : Builds project in debug mode'); console.log(' --debug : Builds project in debug mode');

View File

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

View File

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

View File

@@ -1,324 +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()
maven {
url "https://maven.google.com"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
}
}
// Allow plugins to declare Maven dependencies via build-extras.gradle.
allprojects {
repositories {
mavenCentral();
jcenter()
}
}
task wrapper(type: Wrapper) {
gradleVersion = '4.1.0'
}
// 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 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()
//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 ? null : defaultMinSdkVersion
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('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 = { var app = {
// Application Constructor // Application Constructor
initialize: function () { initialize: function() {
this.bindEvents(); this.bindEvents();
}, },
// Bind Event Listeners // Bind Event Listeners
// //
// Bind any events that are required on startup. Common events are: // Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'. // 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function () { bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false); document.addEventListener('deviceready', this.onDeviceReady, false);
}, },
// deviceready Event Handler // deviceready Event Handler
// //
// The scope of 'this' is the event. In order to call the 'receivedEvent' // The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);' // function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function () { onDeviceReady: function() {
app.receivedEvent('deviceready'); app.receivedEvent('deviceready');
}, },
// Update DOM on a Received Event // Update DOM on a Received Event
receivedEvent: function (id) { receivedEvent: function(id) {
var parentElement = document.getElementById(id); var parentElement = document.getElementById(id);
var listeningElement = parentElement.querySelector('.listening'); var listeningElement = parentElement.querySelector('.listening');
var receivedElement = parentElement.querySelector('.received'); var receivedElement = parentElement.querySelector('.received');
@@ -48,4 +48,4 @@ var app = {
} }
}; };
app.initialize(); app.initialize();

View File

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

View File

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

View File

@@ -1,13 +1,15 @@
# This file was originally created by the Android Tools, but is now # This file is automatically generated by Android Tools.
# used by cordova-android to manage the state of the various third party # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
# libraries used in your application #
# 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 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. # Project target.
target=This_gets_replaced 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -19,5 +19,5 @@
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.apache.cordova" android:versionName="1.0" android:versionCode="1"> package="org.apache.cordova" android:versionName="1.0" android:versionCode="1">
<uses-sdk android:minSdkVersion="16" /> <uses-sdk android:minSdkVersion="14" />
</manifest> </manifest>

View File

@@ -24,14 +24,12 @@ ext {
buildscript { buildscript {
repositories { repositories {
mavenCentral()
jcenter() jcenter()
maven {
url "https://maven.google.com"
}
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.0' classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
} }
@@ -42,7 +40,7 @@ apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray' apply plugin: 'com.jfrog.bintray'
group = 'org.apache.cordova' group = 'org.apache.cordova'
version = '7.0.0' version = '6.2.2'
android { android {
compileSdkVersion cdvCompileSdkVersion compileSdkVersion cdvCompileSdkVersion
@@ -50,8 +48,8 @@ android {
publishNonDefault true publishNonDefault true
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_6
} }
sourceSets { sourceSets {
@@ -129,9 +127,9 @@ bintray {
licenses = ['Apache-2.0'] licenses = ['Apache-2.0']
labels = ['android', 'cordova', 'phonegap'] labels = ['android', 'cordova', 'phonegap']
version { version {
name = '7.0.0' name = '6.2.2'
released = new Date() released = new Date()
vcsTag = '7.0.0' vcsTag = '6.2.2'
} }
} }
} }

View File

@@ -29,11 +29,7 @@ String doEnsureValueExists(filePath, props, key) {
String doGetProjectTarget() { String doGetProjectTarget() {
def props = new Properties() def props = new Properties()
def propertiesFile = 'project.properties'; file('project.properties').withReader { reader ->
if(!(file(propertiesFile).exists())) {
propertiesFile = '../project.properties';
}
file(propertiesFile).withReader { reader ->
props.load(reader) props.load(reader)
} }
return doEnsureValueExists('project.properties', props, 'target') return doEnsureValueExists('project.properties', props, 'target')

View File

@@ -1,6 +1,6 @@
#Thu Nov 09 10:50:25 PST 2017 #Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

View File

@@ -10,7 +10,7 @@
# Indicates whether an apk should be generated for each density. # Indicates whether an apk should be generated for each density.
split.density=false split.density=false
# Project target. # Project target.
target=android-26 target=android-25
apk-configurations= apk-configurations=
renderscript.opt.level=O0 renderscript.opt.level=O0
android.library=true android.library=true

View File

@@ -1,70 +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.
*/
package org.apache.cordova;
/*
* This is a utility class that allows us to get the BuildConfig variable, which is required
* for the use of different providers. This is not guaranteed to work, and it's better for this
* to be set in the build step in config.xml
*
*/
import android.app.Activity;
import android.content.Context;
import java.lang.reflect.Field;
public class BuildHelper {
private static String TAG="BuildHelper";
/*
* This needs to be implemented if you wish to use the Camera Plugin or other plugins
* that read the Build Configuration.
*
* Thanks to Phil@Medtronic and Graham Borland for finding the answer and posting it to
* StackOverflow. This is annoying as hell! However, this method does not work with
* ProGuard, and you should use the config.xml to define the application_id
*
*/
public static Object getBuildConfigValue(Context ctx, String key)
{
try
{
Class<?> clazz = Class.forName(ctx.getPackageName() + ".BuildConfig");
Field field = clazz.getField(key);
return field.get(null);
} catch (ClassNotFoundException e) {
LOG.d(TAG, "Unable to get the BuildConfig, is this built with ANT?");
e.printStackTrace();
} catch (NoSuchFieldException e) {
LOG.d(TAG, key + " is not a valid field. Check your build.gradle");
} catch (IllegalAccessException e) {
LOG.d(TAG, "Illegal Access Exception: Let's print a stack trace.");
e.printStackTrace();
}
return null;
}
}

View File

@@ -319,7 +319,6 @@ public class CordovaActivity extends Activity {
/** /**
* Called when view focus is changed * Called when view focus is changed
*/ */
@SuppressLint("InlinedApi")
@Override @Override
public void onWindowFocusChanged(boolean hasFocus) { public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus); super.onWindowFocusChanged(hasFocus);

View File

@@ -18,8 +18,6 @@
*/ */
package org.apache.cordova; package org.apache.cordova;
import android.annotation.SuppressLint;
import java.security.SecureRandom; import java.security.SecureRandom;
import org.json.JSONArray; import org.json.JSONArray;
@@ -112,9 +110,6 @@ public class CordovaBridge {
} }
/** Called by cordova.js to initialize the bridge. */ /** Called by cordova.js to initialize the bridge. */
//On old Androids SecureRandom isn't really secure, this is the least of your problems if
//you're running Android 4.3 and below in 2017
@SuppressLint("TrulyRandom")
int generateBridgeSecret() { int generateBridgeSecret() {
SecureRandom randGen = new SecureRandom(); SecureRandom randGen = new SecureRandom();
expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE); expectedBridgeSecret = randGen.nextInt(Integer.MAX_VALUE);

View File

@@ -22,12 +22,10 @@ import java.security.Principal;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import android.annotation.SuppressLint;
import android.webkit.ClientCertRequest; import android.webkit.ClientCertRequest;
/** /**
* Implementation of the ICordovaClientCertRequest for Android WebView. * Implementation of the ICordovaClientCertRequest for Android WebView.
*
*/ */
public class CordovaClientCertRequest implements ICordovaClientCertRequest { public class CordovaClientCertRequest implements ICordovaClientCertRequest {
@@ -40,7 +38,6 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
/** /**
* Cancel this request * Cancel this request
*/ */
@SuppressLint("NewApi")
public void cancel() public void cancel()
{ {
request.cancel(); request.cancel();
@@ -49,7 +46,6 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
/* /*
* Returns the host name of the server requesting the certificate. * Returns the host name of the server requesting the certificate.
*/ */
@SuppressLint("NewApi")
public String getHost() public String getHost()
{ {
return request.getHost(); return request.getHost();
@@ -58,7 +54,6 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
/* /*
* Returns the acceptable types of asymmetric keys (can be null). * Returns the acceptable types of asymmetric keys (can be null).
*/ */
@SuppressLint("NewApi")
public String[] getKeyTypes() public String[] getKeyTypes()
{ {
return request.getKeyTypes(); return request.getKeyTypes();
@@ -67,7 +62,6 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
/* /*
* Returns the port number of the server requesting the certificate. * Returns the port number of the server requesting the certificate.
*/ */
@SuppressLint("NewApi")
public int getPort() public int getPort()
{ {
return request.getPort(); return request.getPort();
@@ -76,7 +70,6 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
/* /*
* Returns the acceptable certificate issuers for the certificate matching the private key (can be null). * Returns the acceptable certificate issuers for the certificate matching the private key (can be null).
*/ */
@SuppressLint("NewApi")
public Principal[] getPrincipals() public Principal[] getPrincipals()
{ {
return request.getPrincipals(); return request.getPrincipals();
@@ -85,7 +78,6 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
/* /*
* Ignore the request for now. Do not remember user's choice. * Ignore the request for now. Do not remember user's choice.
*/ */
@SuppressLint("NewApi")
public void ignore() public void ignore()
{ {
request.ignore(); request.ignore();
@@ -97,7 +89,6 @@ public class CordovaClientCertRequest implements ICordovaClientCertRequest {
* @param privateKey The privateKey * @param privateKey The privateKey
* @param chain The certificate chain * @param chain The certificate chain
*/ */
@SuppressLint("NewApi")
public void proceed(PrivateKey privateKey, X509Certificate[] chain) public void proceed(PrivateKey privateKey, X509Certificate[] chain)
{ {
request.proceed(privateKey, chain); request.proceed(privateKey, chain);

View File

@@ -19,7 +19,6 @@
package org.apache.cordova; package org.apache.cordova;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaPlugin;
@@ -52,18 +51,10 @@ public interface CordovaInterface {
/** /**
* Get the Android activity. * Get the Android activity.
* *
* If a custom engine lives outside of the Activity's lifecycle the return value may be null.
*
* @return the Activity * @return the Activity
*/ */
public abstract Activity getActivity(); public abstract Activity getActivity();
/**
* Get the Android context.
*
* @return the Context
*/
public Context getContext();
/** /**
* Called when a message is sent to plugin. * Called when a message is sent to plugin.

View File

@@ -19,9 +19,7 @@
package org.apache.cordova; package org.apache.cordova;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
@@ -86,11 +84,6 @@ public class CordovaInterfaceImpl implements CordovaInterface {
return activity; return activity;
} }
@Override
public Context getContext() {
return activity;
}
@Override @Override
public Object onMessage(String id, Object data) { public Object onMessage(String id, Object data) {
if ("exit".equals(id)) { if ("exit".equals(id)) {
@@ -228,7 +221,6 @@ public class CordovaInterfaceImpl implements CordovaInterface {
requestPermissions(plugin, requestCode, permissions); requestPermissions(plugin, requestCode, permissions);
} }
@SuppressLint("NewApi")
public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) { public void requestPermissions(CordovaPlugin plugin, int requestCode, String [] permissions) {
int mappedRequestCode = permissionResultCallbacks.registerCallback(plugin, requestCode); int mappedRequestCode = permissionResultCallbacks.registerCallback(plugin, requestCode);
getActivity().requestPermissions(permissions, mappedRequestCode); getActivity().requestPermissions(permissions, mappedRequestCode);

View File

@@ -31,7 +31,7 @@ import android.webkit.WebChromeClient.CustomViewCallback;
* are not expected to implement it. * are not expected to implement it.
*/ */
public interface CordovaWebView { public interface CordovaWebView {
public static final String CORDOVA_VERSION = "7.0.0"; public static final String CORDOVA_VERSION = "6.2.2";
void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences); void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences);

View File

@@ -18,7 +18,6 @@
*/ */
package org.apache.cordova; package org.apache.cordova;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@@ -92,7 +91,6 @@ public class CordovaWebViewImpl implements CordovaWebView {
init(cordova, new ArrayList<PluginEntry>(), new CordovaPreferences()); init(cordova, new ArrayList<PluginEntry>(), new CordovaPreferences());
} }
@SuppressLint("Assert")
@Override @Override
public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences) { public void init(CordovaInterface cordova, List<PluginEntry> pluginEntries, CordovaPreferences preferences) {
if (this.cordova != null) { if (this.cordova != null) {

View File

@@ -1,87 +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.
*/
package org.apache.cordova;
import java.util.Arrays;
import org.json.JSONException;
import android.content.pm.PackageManager;
/**
* This class provides reflective methods for permission requesting and checking so that plugins
* written for cordova-android 5.0.0+ can still compile with earlier cordova-android versions.
*/
public class PermissionHelper {
private static final String LOG_TAG = "CordovaPermissionHelper";
/**
* Requests a "dangerous" permission for the application at runtime. This is a helper method
* alternative to cordovaInterface.requestPermission() that does not require the project to be
* built with cordova-android 5.0.0+
*
* @param plugin The plugin the permission is being requested for
* @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult()
* along with the result of the permission request
* @param permission The permission to be requested
*/
public static void requestPermission(CordovaPlugin plugin, int requestCode, String permission) {
PermissionHelper.requestPermissions(plugin, requestCode, new String[] {permission});
}
/**
* Requests "dangerous" permissions for the application at runtime. This is a helper method
* alternative to cordovaInterface.requestPermissions() that does not require the project to be
* built with cordova-android 5.0.0+
*
* @param plugin The plugin the permissions are being requested for
* @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult()
* along with the result of the permissions request
* @param permissions The permissions to be requested
*/
public static void requestPermissions(CordovaPlugin plugin, int requestCode, String[] permissions) {
plugin.cordova.requestPermissions(plugin, requestCode, permissions);
}
/**
* Checks at runtime to see if the application has been granted a permission. This is a helper
* method alternative to cordovaInterface.hasPermission() that does not require the project to
* be built with cordova-android 5.0.0+
*
* @param plugin The plugin the permission is being checked against
* @param permission The permission to be checked
*
* @return True if the permission has already been granted and false otherwise
*/
public static boolean hasPermission(CordovaPlugin plugin, String permission) {
return plugin.cordova.hasPermission(permission);
}
private static void deliverPermissionResult(CordovaPlugin plugin, int requestCode, String[] permissions) {
// Generate the request results
int[] requestResults = new int[permissions.length];
Arrays.fill(requestResults, PackageManager.PERMISSION_GRANTED);
try {
plugin.onRequestPermissionResult(requestCode, permissions, requestResults);
} catch (JSONException e) {
LOG.e(LOG_TAG, "JSONException when delivering permissions results", e);
}
}
}

View File

@@ -110,11 +110,7 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() { nativeToJsMessageQueue.addBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode(new NativeToJsMessageQueue.OnlineEventsBridgeMode.OnlineEventsBridgeModeDelegate() {
@Override @Override
public void setNetworkAvailable(boolean value) { public void setNetworkAvailable(boolean value) {
//sometimes this can be called after calling webview.destroy() on destroy() webView.setNetworkAvailable(value);
//thus resulting in a NullPointerException
if(webView!=null) {
webView.setNetworkAvailable(value);
}
} }
@Override @Override
public void runOnUiThread(Runnable r) { public void runOnUiThread(Runnable r) {
@@ -254,9 +250,6 @@ public class SystemWebViewEngine implements CordovaWebViewEngine {
} }
} }
// Yeah, we know, which is why we makes ure that we don't do this if the bridge is
// below JELLYBEAN_MR1. It'd be great if lint was just a little smarter.
@SuppressLint("AddJavascriptInterface")
private static void exposeJsInterface(WebView webView, CordovaBridge bridge) { private static void exposeJsInterface(WebView webView, CordovaBridge bridge) {
if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) { if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) {
LOG.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old."); LOG.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old.");

31
node_modules/abbrev/LICENSE generated vendored
View File

@@ -1,8 +1,3 @@
This software is dual-licensed under the ISC and MIT licenses.
You may use this software under EITHER of the following licenses.
----------
The ISC License The ISC License
Copyright (c) Isaac Z. Schlueter and Contributors Copyright (c) Isaac Z. Schlueter and Contributors
@@ -18,29 +13,3 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
----------
Copyright Isaac Z. Schlueter and Contributors
All rights reserved.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

31
node_modules/abbrev/package.json generated vendored
View File

@@ -10,23 +10,23 @@
"spec": ">=1.0.0 <2.0.0", "spec": ">=1.0.0 <2.0.0",
"type": "range" "type": "range"
}, },
"/Users/jbowser/cordova/cordova-android/node_modules/nopt" "/Users/steveng/repo/cordova/cordova-android/node_modules/nopt"
] ]
], ],
"_from": "abbrev@>=1.0.0 <2.0.0", "_from": "abbrev@>=1.0.0 <2.0.0",
"_id": "abbrev@1.1.1", "_id": "abbrev@1.1.0",
"_inCache": true, "_inCache": true,
"_location": "/abbrev", "_location": "/abbrev",
"_nodeVersion": "8.5.0", "_nodeVersion": "8.0.0-pre",
"_npmOperationalInternal": { "_npmOperationalInternal": {
"host": "s3://npm-registry-packages", "host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/abbrev-1.1.1.tgz_1506566833068_0.05750026390887797" "tmp": "tmp/abbrev-1.1.0.tgz_1487054000015_0.9229173036292195"
}, },
"_npmUser": { "_npmUser": {
"name": "isaacs", "name": "isaacs",
"email": "i@izs.me" "email": "i@izs.me"
}, },
"_npmVersion": "5.4.2", "_npmVersion": "4.3.0",
"_phantomChildren": {}, "_phantomChildren": {},
"_requested": { "_requested": {
"raw": "abbrev@1", "raw": "abbrev@1",
@@ -40,11 +40,11 @@
"_requiredBy": [ "_requiredBy": [
"/nopt" "/nopt"
], ],
"_resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "_resolved": "http://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz",
"_shasum": "f8f2c887ad10bf67f634f005b6987fed3179aac8", "_shasum": "d0554c2256636e2f56e7c2e5ad183f859428d81f",
"_shrinkwrap": null, "_shrinkwrap": null,
"_spec": "abbrev@1", "_spec": "abbrev@1",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/nopt", "_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/nopt",
"author": { "author": {
"name": "Isaac Z. Schlueter", "name": "Isaac Z. Schlueter",
"email": "i@izs.me" "email": "i@izs.me"
@@ -59,22 +59,17 @@
}, },
"directories": {}, "directories": {},
"dist": { "dist": {
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "shasum": "d0554c2256636e2f56e7c2e5ad183f859428d81f",
"shasum": "f8f2c887ad10bf67f634f005b6987fed3179aac8", "tarball": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz"
"tarball": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
}, },
"files": [ "files": [
"abbrev.js" "abbrev.js"
], ],
"gitHead": "a9ee72ebc8fe3975f1b0c7aeb3a8f2a806a432eb", "gitHead": "7136d4d95449dc44115d4f78b80ec907724f64e0",
"homepage": "https://github.com/isaacs/abbrev-js#readme", "homepage": "https://github.com/isaacs/abbrev-js#readme",
"license": "ISC", "license": "ISC",
"main": "abbrev.js", "main": "abbrev.js",
"maintainers": [ "maintainers": [
{
"name": "gabra",
"email": "jerry+1@npmjs.com"
},
{ {
"name": "isaacs", "name": "isaacs",
"email": "i@izs.me" "email": "i@izs.me"
@@ -93,5 +88,5 @@
"preversion": "npm test", "preversion": "npm test",
"test": "tap test.js --100" "test": "tap test.js --100"
}, },
"version": "1.1.1" "version": "1.1.0"
} }

View File

@@ -1,8 +0,0 @@
.git/
node_modules/
coverage/
build/
assets/
dist/
docs/
tests/

View File

@@ -1,28 +0,0 @@
{
"indent": 2,
"forin": true,
"noarg": true,
"bitwise": true,
"nonew": true,
"strict": true,
"browser": true,
"devel": true,
"node": false,
"jquery": false,
"esnext": false,
"moz": false,
"es3": false,
"asi": true,
"eqnull": true,
"debug": true,
"boss": true,
"evil": true,
"loopfunc": true,
"laxbreak": true,
"unused": true,
"undef": true
}

View File

@@ -1,3 +0,0 @@
language: node_js
node_js:
- "6.1.0"

View File

@@ -1,87 +0,0 @@
Android Versions
================
A node module to get Android versions by API level, NDK level, semantic version, or version name.
Versions are referenced from [source.android.com/source/build-numbers.html](https://source.android.com/source/build-numbers.html#platform-code-names-versions-api-levels-and-ndk-releases). The version for "Current Development Build" (`"CUR_DEVELOPMENT"`) is not included in the list of `VERSIONS`.
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[npm-image]: https://img.shields.io/npm/v/android-versions.svg?style=flat-square
[npm-url]: https://npmjs.org/package/android-versions
[travis-image]: https://img.shields.io/travis/dvoiss/android-versions.svg?style=flat-square
[travis-url]: https://travis-ci.org/dvoiss/android-versions
## Install
```bash
# NPM
npm install android-versions --save
# YARN
yarn add android-versions
```
## Usage
View the tests for more advanced usage.
```javascript
const android = require('android-versions')
```
#### Get by API level:
```javascript
console.log(android.get(23))
=> { api: 23, ndk: 8, semver: "6.0", name: "Marshmallow", versionCode: "M" }
```
#### Get by version:
```javascript
console.log(android.get("2.3.3"))
=> { api: 10, ndk: 5, semver: "2.3.3", name: "Gingerbread", versionCode: "GINGERBREAD_MR1" }
```
#### Get all by predicate:
```
android.getAll((version) => {
return version.ndk > 5 && version.api < 15
}).map((version) => version.versionCode)
=> [ "HONEYCOMB_MR1", "HONEYCOMB_MR2", "ICE_CREAM_SANDWICH" ]
```
#### Access a specific version with all info:
```
android.LOLLIPOP
=> { api: 21, ndk: 8, semver: "5.0", name: "Lollipop", versionCode: "LOLLIPOP" }
```
#### Access the complete reference of Android versions with all info:
```javascript
android.VERSIONS
=> {
BASE: { api: 1, ndk: 0, semver: "1.0", name: "(no code name)", versionCode: "BASE" },
...
N: { api: 24, ndk: 8, semver: "7.0", name: "Nougat", versionCode: "N" }
...
}
```
## Test
```bash
npm run test
```
## License
MIT

View File

@@ -1,153 +0,0 @@
/**
* Copyright (c) 2016, David Voiss <davidvoiss@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted, provided that the above copyright notice
* and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
/* jshint node: true */
"use strict";
/**
* A module to get Android versions by API level, NDK level, semantic version, or version name.
*
* Versions are referenced from here:
* {@link https://source.android.com/source/build-numbers.html#platform-code-names-versions-api-levels-and-ndk-releases}
* {@link https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/Build.java}
*
* The version for "Current Development Build" ("CUR_DEVELOPMENT") is not included.
*
* @module android-versions
*/
var VERSIONS = {
BASE: { api: 1, ndk: 0, semver: "1.0", name: "(no code name)", versionCode: "BASE" },
BASE_1_1: { api: 2, ndk: 0, semver: "1.1", name: "(no code name)", versionCode: "BASE_1_1" },
CUPCAKE: { api: 3, ndk: 1, semver: "1.5", name: "Cupcake", versionCode: "CUPCAKE" },
DONUT: { api: 4, ndk: 2, semver: "1.6", name: "Donut", versionCode: "DONUT" },
ECLAIR: { api: 5, ndk: 2, semver: "2.0", name: "Eclair", versionCode: "ECLAIR" },
ECLAIR_0_1: { api: 6, ndk: 2, semver: "2.0.1", name: "Eclair", versionCode: "ECLAIR_0_1" },
ECLAIR_MR1: { api: 7, ndk: 3, semver: "2.1", name: "Eclair", versionCode: "ECLAIR_MR1" },
FROYO: { api: 8, ndk: 4, semver: "2.2", name: "Froyo", versionCode: "FROYO" },
GINGERBREAD: { api: 9, ndk: 5, semver: "2.3", name: "Gingerbread", versionCode: "GINGERBREAD" },
GINGERBREAD_MR1: { api: 10, ndk: 5, semver: "2.3.3", name: "Gingerbread", versionCode: "GINGERBREAD_MR1" },
HONEYCOMB: { api: 11, ndk: 5, semver: "3.0", name: "Honeycomb", versionCode: "HONEYCOMB" },
HONEYCOMB_MR1: { api: 12, ndk: 6, semver: "3.1", name: "Honeycomb", versionCode: "HONEYCOMB_MR1" },
HONEYCOMB_MR2: { api: 13, ndk: 6, semver: "3.2", name: "Honeycomb", versionCode: "HONEYCOMB_MR2" },
ICE_CREAM_SANDWICH: { api: 14, ndk: 7, semver: "4.0", name: "Ice Cream Sandwich", versionCode: "ICE_CREAM_SANDWICH" },
ICE_CREAM_SANDWICH_MR1: { api: 15, ndk: 8, semver: "4.0.3", name: "Ice Cream Sandwich", versionCode: "ICE_CREAM_SANDWICH_MR1" },
JELLY_BEAN: { api: 16, ndk: 8, semver: "4.1", name: "Jellybean", versionCode: "JELLY_BEAN" },
JELLY_BEAN_MR1: { api: 17, ndk: 8, semver: "4.2", name: "Jellybean", versionCode: "JELLY_BEAN_MR1" },
JELLY_BEAN_MR2: { api: 18, ndk: 8, semver: "4.3", name: "Jellybean", versionCode: "JELLY_BEAN_MR2" },
KITKAT: { api: 19, ndk: 8, semver: "4.4", name: "KitKat", versionCode: "KITKAT" },
KITKAT_WATCH: { api: 20, ndk: 8, semver: "4.4", name: "KitKat Watch", versionCode: "KITKAT_WATCH" },
LOLLIPOP: { api: 21, ndk: 8, semver: "5.0", name: "Lollipop", versionCode: "LOLLIPOP" },
LOLLIPOP_MR1: { api: 22, ndk: 8, semver: "5.1", name: "Lollipop", versionCode: "LOLLIPOP_MR1" },
M: { api: 23, ndk: 8, semver: "6.0", name: "Marshmallow", versionCode: "M" },
N: { api: 24, ndk: 8, semver: "7.0", name: "Nougat", versionCode: "N" },
N_MR1: { api: 25, ndk: 8, semver: "7.1", name: "Nougat", versionCode: "N_MR1" },
O: { api: 26, ndk: 8, semver: "8.0.0", name: "Oreo", versionCode: "O" }
}
// This altSemVer accomodates the variations of semantic versions in the table above.
// For instance, Oreo is 8.0.0 while N is 7.0, searching for "8.0" or "8.0.0" will
// return Oreo, or searching for "7.0" or "7.0.0" will return N. "2.2.0" will return Froyo.
function getAlternateSemVer(semver) {
if (semver.match(/\d+.\d+.0/)) {
return semver.replace(/.\d+$/, '')
} else if (semver.match(/^\d+.\d+$/)) {
return semver + '.0'
} else {
return semver
}
}
// The default predicate compares against API level, semver, name, or code.
function getFromDefaultPredicate(arg) {
// Coerce arg to string for comparisons below.
arg = arg.toString()
return getFromPredicate(function(version) {
// Check API level before all else.
if (arg === version.api.toString()) {
return true
}
// Compare semver and alternate semver (see above).
var altSemVer = getAlternateSemVer(arg)
if (version.semver === arg || version.semver === altSemVer) {
return true
}
// Compare version name and code.
return arg === version.name || arg === version.versionCode
})
}
// The function to allow passing a predicate.
function getFromPredicate(predicate) {
if (predicate === null) {
return null
}
return Object.keys(VERSIONS).filter(function(version) {
return predicate(VERSIONS[version])
}).map(function(key) { return VERSIONS[key] })
}
/**
* The Android version codes available as keys for easier look-up.
*/
Object.keys(VERSIONS).forEach(function(name) {
exports[name] = VERSIONS[name]
})
/**
* The complete reference of Android versions for easier look-up.
*/
exports.VERSIONS = VERSIONS
/**
* Retrieve a single Android version.
*
* @param {object | Function} arg - The value or predicate to use to retrieve values.
*
* @return {object} An object representing the version found or null if none found.
*/
exports.get = function(arg) {
var result = exports.getAll(arg)
if (result === null || result.length === 0) {
return null
}
return result[0]
}
/**
* Retrieve all Android versions that meet the criteria of the argument.
*
* @param {object | Function} arg - The value or predicate to use to retrieve values.
*
* @return {object} An object representing the version found or null if none found.
*/
exports.getAll = function(arg) {
if (arg === null) {
return null
}
if (typeof arg === "function") {
return getFromPredicate(arg)
} else {
return getFromDefaultPredicate(arg)
}
}

View File

@@ -1,103 +0,0 @@
{
"_args": [
[
{
"raw": "android-versions@^1.2.1",
"scope": null,
"escapedName": "android-versions",
"name": "android-versions",
"rawSpec": "^1.2.1",
"spec": ">=1.2.1 <2.0.0",
"type": "range"
},
"/Users/jbowser/cordova/cordova-android"
]
],
"_from": "android-versions@>=1.2.1 <2.0.0",
"_id": "android-versions@1.2.1",
"_inCache": true,
"_location": "/android-versions",
"_nodeVersion": "8.0.0",
"_npmOperationalInternal": {
"host": "s3://npm-registry-packages",
"tmp": "tmp/android-versions-1.2.1.tgz_1505373302036_0.5689644906669855"
},
"_npmUser": {
"name": "dvoiss",
"email": "davidvoiss@gmail.com"
},
"_npmVersion": "5.4.0",
"_phantomChildren": {},
"_requested": {
"raw": "android-versions@^1.2.1",
"scope": null,
"escapedName": "android-versions",
"name": "android-versions",
"rawSpec": "^1.2.1",
"spec": ">=1.2.1 <2.0.0",
"type": "range"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.2.1.tgz",
"_shasum": "3f50baf693e73a512c3c5403542291cead900063",
"_shrinkwrap": null,
"_spec": "android-versions@^1.2.1",
"_where": "/Users/jbowser/cordova/cordova-android",
"author": {
"name": "dvoiss"
},
"bugs": {
"url": "https://github.com/dvoiss/android-versions/issues"
},
"dependencies": {},
"description": "Get the name, API level, version level, NDK level, or version code from any version of Android.",
"devDependencies": {
"jsdoc": "^3.4.0",
"jshint": "^2.9.2",
"tape": "^4.6.0"
},
"directories": {},
"dist": {
"integrity": "sha512-k6zlrtWbJ3tx1ZsyyJ0Bo3r6cqPA3JUnFGv7pnIaLr1XVxSi2Tcem2lg3kBebFp27v/A40tZqdlouPyakpyKrw==",
"shasum": "3f50baf693e73a512c3c5403542291cead900063",
"tarball": "https://registry.npmjs.org/android-versions/-/android-versions-1.2.1.tgz"
},
"gitHead": "7e2def6e70634a4ebcaaa639a4c4955ae2a566e7",
"homepage": "https://github.com/dvoiss/android-versions#readme",
"keywords": [
"android",
"version",
"versions",
"ndk",
"nougat",
"marshmallow",
"api",
"level"
],
"license": "MIT",
"main": "index.js",
"maintainers": [
{
"name": "dvoiss",
"email": "davidvoiss@gmail.com"
}
],
"name": "android-versions",
"optionalDependencies": {},
"pre-commit": [
"jshint"
],
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/dvoiss/android-versions.git"
},
"scripts": {
"docs": "jsdoc index.js -d ./docs/ -R README.md --debug",
"jshint": "jshint .",
"test": "tape tests/**/*.js"
},
"version": "1.2.1"
}

6
node_modules/ansi/package.json generated vendored
View File

@@ -10,7 +10,7 @@
"spec": ">=0.3.1 <0.4.0", "spec": ">=0.3.1 <0.4.0",
"type": "range" "type": "range"
}, },
"/Users/jbowser/cordova/cordova-android/node_modules/cordova-common" "/Users/steveng/repo/cordova/cordova-android/node_modules/cordova-common"
] ]
], ],
"_from": "ansi@>=0.3.1 <0.4.0", "_from": "ansi@>=0.3.1 <0.4.0",
@@ -36,11 +36,11 @@
"_requiredBy": [ "_requiredBy": [
"/cordova-common" "/cordova-common"
], ],
"_resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", "_resolved": "http://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz",
"_shasum": "0c42d4fb17160d5a9af1e484bace1c66922c1b21", "_shasum": "0c42d4fb17160d5a9af1e484bace1c66922c1b21",
"_shrinkwrap": null, "_shrinkwrap": null,
"_spec": "ansi@^0.3.1", "_spec": "ansi@^0.3.1",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/cordova-common", "_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/cordova-common",
"author": { "author": {
"name": "Nathan Rajlich", "name": "Nathan Rajlich",
"email": "nathan@tootallnate.net", "email": "nathan@tootallnate.net",

View File

@@ -1,4 +1,3 @@
'use strict';
module.exports = balanced; module.exports = balanced;
function balanced(a, b, str) { function balanced(a, b, str) {
if (a instanceof RegExp) a = maybeMatch(a, str); if (a instanceof RegExp) a = maybeMatch(a, str);

View File

@@ -2,49 +2,49 @@
"_args": [ "_args": [
[ [
{ {
"raw": "balanced-match@^1.0.0", "raw": "balanced-match@^0.4.1",
"scope": null, "scope": null,
"escapedName": "balanced-match", "escapedName": "balanced-match",
"name": "balanced-match", "name": "balanced-match",
"rawSpec": "^1.0.0", "rawSpec": "^0.4.1",
"spec": ">=1.0.0 <2.0.0", "spec": ">=0.4.1 <0.5.0",
"type": "range" "type": "range"
}, },
"/Users/jbowser/cordova/cordova-android/node_modules/brace-expansion" "/Users/steveng/repo/cordova/cordova-android/node_modules/brace-expansion"
] ]
], ],
"_from": "balanced-match@>=1.0.0 <2.0.0", "_from": "balanced-match@>=0.4.1 <0.5.0",
"_id": "balanced-match@1.0.0", "_id": "balanced-match@0.4.2",
"_inCache": true, "_inCache": true,
"_location": "/balanced-match", "_location": "/balanced-match",
"_nodeVersion": "7.8.0", "_nodeVersion": "4.4.7",
"_npmOperationalInternal": { "_npmOperationalInternal": {
"host": "s3://npm-registry-packages", "host": "packages-16-east.internal.npmjs.com",
"tmp": "tmp/balanced-match-1.0.0.tgz_1497251909645_0.8755026108119637" "tmp": "tmp/balanced-match-0.4.2.tgz_1468834991581_0.6590619895141572"
}, },
"_npmUser": { "_npmUser": {
"name": "juliangruber", "name": "juliangruber",
"email": "julian@juliangruber.com" "email": "julian@juliangruber.com"
}, },
"_npmVersion": "4.2.0", "_npmVersion": "2.15.8",
"_phantomChildren": {}, "_phantomChildren": {},
"_requested": { "_requested": {
"raw": "balanced-match@^1.0.0", "raw": "balanced-match@^0.4.1",
"scope": null, "scope": null,
"escapedName": "balanced-match", "escapedName": "balanced-match",
"name": "balanced-match", "name": "balanced-match",
"rawSpec": "^1.0.0", "rawSpec": "^0.4.1",
"spec": ">=1.0.0 <2.0.0", "spec": ">=0.4.1 <0.5.0",
"type": "range" "type": "range"
}, },
"_requiredBy": [ "_requiredBy": [
"/brace-expansion" "/brace-expansion"
], ],
"_resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "_resolved": "http://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
"_shasum": "89b4d199ab2bee49de164ea02b89ce462d71b767", "_shasum": "cb3f3e3c732dc0f01ee70b403f302e61d7709838",
"_shrinkwrap": null, "_shrinkwrap": null,
"_spec": "balanced-match@^1.0.0", "_spec": "balanced-match@^0.4.1",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/brace-expansion", "_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/brace-expansion",
"author": { "author": {
"name": "Julian Gruber", "name": "Julian Gruber",
"email": "mail@juliangruber.com", "email": "mail@juliangruber.com",
@@ -56,15 +56,14 @@
"dependencies": {}, "dependencies": {},
"description": "Match balanced character pairs, like \"{\" and \"}\"", "description": "Match balanced character pairs, like \"{\" and \"}\"",
"devDependencies": { "devDependencies": {
"matcha": "^0.7.0",
"tape": "^4.6.0" "tape": "^4.6.0"
}, },
"directories": {}, "directories": {},
"dist": { "dist": {
"shasum": "89b4d199ab2bee49de164ea02b89ce462d71b767", "shasum": "cb3f3e3c732dc0f01ee70b403f302e61d7709838",
"tarball": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" "tarball": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz"
}, },
"gitHead": "d701a549a7653a874eebce7eca25d3577dc868ac", "gitHead": "57c2ea29d89a2844ae3bdcc637c6e2cbb73725e2",
"homepage": "https://github.com/juliangruber/balanced-match", "homepage": "https://github.com/juliangruber/balanced-match",
"keywords": [ "keywords": [
"match", "match",
@@ -89,7 +88,6 @@
"url": "git://github.com/juliangruber/balanced-match.git" "url": "git://github.com/juliangruber/balanced-match.git"
}, },
"scripts": { "scripts": {
"bench": "make bench",
"test": "make test" "test": "make test"
}, },
"testling": { "testling": {
@@ -108,5 +106,5 @@
"android-browser/4.2..latest" "android-browser/4.2..latest"
] ]
}, },
"version": "1.0.0" "version": "0.4.2"
} }

View File

@@ -10,7 +10,7 @@
"spec": "0.0.8", "spec": "0.0.8",
"type": "version" "type": "version"
}, },
"/Users/jbowser/cordova/cordova-android/node_modules/plist" "/Users/steveng/repo/cordova/cordova-android/node_modules/plist"
] ]
], ],
"_from": "base64-js@0.0.8", "_from": "base64-js@0.0.8",
@@ -36,11 +36,11 @@
"_requiredBy": [ "_requiredBy": [
"/plist" "/plist"
], ],
"_resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", "_resolved": "http://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
"_shasum": "1101e9544f4a76b1bc3b26d452ca96d7a35e7978", "_shasum": "1101e9544f4a76b1bc3b26d452ca96d7a35e7978",
"_shrinkwrap": null, "_shrinkwrap": null,
"_spec": "base64-js@0.0.8", "_spec": "base64-js@0.0.8",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/plist", "_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/plist",
"author": { "author": {
"name": "T. Jameson Little", "name": "T. Jameson Little",
"email": "t.jameson.little@gmail.com" "email": "t.jameson.little@gmail.com"

File diff suppressed because it is too large Load Diff

View File

@@ -118,7 +118,7 @@ var bigInt = (function (undefined) {
} }
BigInteger.prototype.add = function (v) { BigInteger.prototype.add = function (v) {
var n = parseValue(v); var value, n = parseValue(v);
if (this.sign !== n.sign) { if (this.sign !== n.sign) {
return this.subtract(n.negate()); return this.subtract(n.negate());
} }
@@ -177,7 +177,7 @@ var bigInt = (function (undefined) {
} }
function subtractAny(a, b, sign) { function subtractAny(a, b, sign) {
var value; var value, isSmall;
if (compareAbs(a, b) >= 0) { if (compareAbs(a, b) >= 0) {
value = subtract(a,b); value = subtract(a,b);
} else { } else {
@@ -326,7 +326,7 @@ var bigInt = (function (undefined) {
} }
BigInteger.prototype.multiply = function (v) { BigInteger.prototype.multiply = function (v) {
var n = parseValue(v), var value, n = parseValue(v),
a = this.value, b = n.value, a = this.value, b = n.value,
sign = this.sign !== n.sign, sign = this.sign !== n.sign,
abs; abs;
@@ -826,24 +826,23 @@ var bigInt = (function (undefined) {
BigInteger.prototype.modInv = function (n) { BigInteger.prototype.modInv = function (n) {
var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR; var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR;
while (!newR.equals(bigInt.zero)) { while (!newR.equals(bigInt.zero)) {
q = r.divide(newR); q = r.divide(newR);
lastT = t; lastT = t;
lastR = r; lastR = r;
t = newT; t = newT;
r = newR; r = newR;
newT = lastT.subtract(q.multiply(newT)); newT = lastT.subtract(q.multiply(newT));
newR = lastR.subtract(q.multiply(newR)); newR = lastR.subtract(q.multiply(newR));
} }
if (!r.equals(1)) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime"); if (!r.equals(1)) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime");
if (t.compare(0) === -1) { if (t.compare(0) === -1) {
t = t.add(n); t = t.add(n);
} }
if (this.isNegative()) { if (this.isNegative()) {
return t.negate(); return t.negate();
} }
return t; return t;
}; }
SmallInteger.prototype.modInv = BigInteger.prototype.modInv; SmallInteger.prototype.modInv = BigInteger.prototype.modInv;
BigInteger.prototype.next = function () { BigInteger.prototype.next = function () {
@@ -873,7 +872,7 @@ var bigInt = (function (undefined) {
}; };
var powersOfTwo = [1]; var powersOfTwo = [1];
while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]); while (powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]);
var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1]; var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1];
function shift_isSmall(n) { function shift_isSmall(n) {
@@ -920,29 +919,31 @@ var bigInt = (function (undefined) {
var xSign = x.isNegative(), ySign = y.isNegative(); var xSign = x.isNegative(), ySign = y.isNegative();
var xRem = xSign ? x.not() : x, var xRem = xSign ? x.not() : x,
yRem = ySign ? y.not() : y; yRem = ySign ? y.not() : y;
var xDigit = 0, yDigit = 0; var xBits = [], yBits = [];
var xDivMod = null, yDivMod = null; var xStop = false, yStop = false;
var result = []; while (!xStop || !yStop) {
while (!xRem.isZero() || !yRem.isZero()) { if (xRem.isZero()) { // virtual sign extension for simulating two's complement
xDivMod = divModAny(xRem, highestPower2); xStop = true;
xDigit = xDivMod[1].toJSNumber(); xBits.push(xSign ? 1 : 0);
if (xSign) {
xDigit = highestPower2 - 1 - xDigit; // two's complement for negative numbers
} }
else if (xSign) xBits.push(xRem.isEven() ? 1 : 0); // two's complement for negative numbers
else xBits.push(xRem.isEven() ? 0 : 1);
yDivMod = divModAny(yRem, highestPower2); if (yRem.isZero()) {
yDigit = yDivMod[1].toJSNumber(); yStop = true;
if (ySign) { yBits.push(ySign ? 1 : 0);
yDigit = highestPower2 - 1 - yDigit; // two's complement for negative numbers
} }
else if (ySign) yBits.push(yRem.isEven() ? 1 : 0);
else yBits.push(yRem.isEven() ? 0 : 1);
xRem = xDivMod[0]; xRem = xRem.over(2);
yRem = yDivMod[0]; yRem = yRem.over(2);
result.push(fn(xDigit, yDigit));
} }
var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : bigInt(0); var result = [];
for (var i = result.length - 1; i >= 0; i -= 1) { for (var i = 0; i < xBits.length; i++) result.push(fn(xBits[i], yBits[i]));
sum = sum.multiply(highestPower2).add(bigInt(result[i])); var sum = bigInt(result.pop()).negate().times(bigInt(2).pow(result.length));
while (result.length) {
sum = sum.add(bigInt(result.pop()).times(bigInt(2).pow(result.length)));
} }
return sum; return sum;
} }
@@ -1021,8 +1022,8 @@ var bigInt = (function (undefined) {
a = parseValue(a); a = parseValue(a);
b = parseValue(b); b = parseValue(b);
var low = min(a, b), high = max(a, b); var low = min(a, b), high = max(a, b);
var range = high.subtract(low).add(1); var range = high.subtract(low);
if (range.isSmall) return low.add(Math.floor(Math.random() * range)); if (range.isSmall) return low.add(Math.round(Math.random() * range));
var length = range.value.length - 1; var length = range.value.length - 1;
var result = [], restricted = true; var result = [], restricted = true;
for (var i = length; i >= 0; i--) { for (var i = length; i >= 0; i--) {
@@ -1035,32 +1036,16 @@ var bigInt = (function (undefined) {
return low.add(typeof result === "number" ? new SmallInteger(result) : new BigInteger(result, false)); return low.add(typeof result === "number" ? new SmallInteger(result) : new BigInteger(result, false));
} }
var parseBase = function (text, base) { var parseBase = function (text, base) {
var length = text.length; var val = Integer[0], pow = Integer[1],
var i; length = text.length;
var absBase = Math.abs(base);
for(var i = 0; i < length; i++) {
var c = text[i].toLowerCase();
if(c === "-") continue;
if(/[a-z0-9]/.test(c)) {
if(/[0-9]/.test(c) && +c >= absBase) {
if(c === "1" && absBase === 1) continue;
throw new Error(c + " is not a valid digit in base " + base + ".");
} else if(c.charCodeAt(0) - 87 >= absBase) {
throw new Error(c + " is not a valid digit in base " + base + ".");
}
}
}
if (2 <= base && base <= 36) { if (2 <= base && base <= 36) {
if (length <= LOG_MAX_INT / Math.log(base)) { if (length <= LOG_MAX_INT / Math.log(base)) {
var result = parseInt(text, base);
if(isNaN(result)) {
throw new Error(c + " is not a valid digit in base " + base + ".");
}
return new SmallInteger(parseInt(text, base)); return new SmallInteger(parseInt(text, base));
} }
} }
base = parseValue(base); base = parseValue(base);
var digits = []; var digits = [];
var i;
var isNegative = text[0] === "-"; var isNegative = text[0] === "-";
for (i = isNegative ? 1 : 0; i < text.length; i++) { for (i = isNegative ? 1 : 0; i < text.length; i++) {
var c = text[i].toLowerCase(), var c = text[i].toLowerCase(),
@@ -1074,17 +1059,13 @@ var bigInt = (function (undefined) {
} }
else throw new Error(c + " is not a valid character"); else throw new Error(c + " is not a valid character");
} }
return parseBaseFromArray(digits, base, isNegative); digits.reverse();
}; for (i = 0; i < digits.length; i++) {
function parseBaseFromArray(digits, base, isNegative) {
var val = Integer[0], pow = Integer[1], i;
for (i = digits.length - 1; i >= 0; i--) {
val = val.add(digits[i].times(pow)); val = val.add(digits[i].times(pow));
pow = pow.times(base); pow = pow.times(base);
} }
return isNegative ? val.negate() : val; return isNegative ? val.negate() : val;
} };
function stringify(digit) { function stringify(digit) {
var v = digit.value; var v = digit.value;
@@ -1141,13 +1122,11 @@ var bigInt = (function (undefined) {
var sign = this.sign ? "-" : ""; var sign = this.sign ? "-" : "";
return sign + str; return sign + str;
}; };
SmallInteger.prototype.toString = function (radix) { SmallInteger.prototype.toString = function (radix) {
if (radix === undefined) radix = 10; if (radix === undefined) radix = 10;
if (radix != 10) return toBase(this, radix); if (radix != 10) return toBase(this, radix);
return String(this.value); return String(this.value);
}; };
BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function() { return this.toString(); }
BigInteger.prototype.valueOf = function () { BigInteger.prototype.valueOf = function () {
return +this.toString(); return +this.toString();
@@ -1230,11 +1209,6 @@ var bigInt = (function (undefined) {
Integer.lcm = lcm; Integer.lcm = lcm;
Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger; }; Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger; };
Integer.randBetween = randBetween; Integer.randBetween = randBetween;
Integer.fromArray = function (digits, base, isNegative) {
return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative);
};
return Integer; return Integer;
})(); })();
@@ -1242,10 +1216,3 @@ var bigInt = (function (undefined) {
if (typeof module !== "undefined" && module.hasOwnProperty("exports")) { if (typeof module !== "undefined" && module.hasOwnProperty("exports")) {
module.exports = bigInt; module.exports = bigInt;
} }
//amd check
if ( typeof define === "function" && define.amd ) {
define( "big-integer", [], function() {
return bigInt;
});
}

File diff suppressed because one or more lines are too long

11
node_modules/big-integer/README.md generated vendored
View File

@@ -211,7 +211,7 @@ Returns `true` if the number is prime, `false` otherwise.
#### `isProbablePrime([iterations])` #### `isProbablePrime([iterations])`
Returns `true` if the number is very likely to be prime, `false` otherwise. Returns `true` if the number is very likely to be positive, `false` otherwise.
Argument is optional and determines the amount of iterations of the test (default: `5`). The more iterations, the lower chance of getting a false positive. Argument is optional and determines the amount of iterations of the test (default: `5`). The more iterations, the lower chance of getting a false positive.
This uses the [Fermat primality test](https://en.wikipedia.org/wiki/Fermat_primality_test). This uses the [Fermat primality test](https://en.wikipedia.org/wiki/Fermat_primality_test).
@@ -421,13 +421,6 @@ Performs the bitwise XOR operation. The operands are treated as if they were rep
### Static Methods ### Static Methods
#### `fromArray(digits, base = 10, isNegative?)`
Constructs a bigInt from an array of digits in base `base`. The optional `isNegative` flag will make the number negative.
- `bigInt.fromArray([1, 2, 3, 4, 5], 10)` => `12345`
- `bigInt.fromArray([1, 0, 0], 2, true)` => `-4`
#### `gcd(a, b)` #### `gcd(a, b)`
Finds the greatest common denominator of `a` and `b`. Finds the greatest common denominator of `a` and `b`.
@@ -517,4 +510,4 @@ There are performance benchmarks that can be viewed from the `benchmarks/index.h
## License ## License
This project is public domain. For more details, read about the [Unlicense](http://unlicense.org/). This project is public domain. For more details, read about the [Unlicense](http://unlicense.org/).

View File

@@ -24,6 +24,7 @@
"bower_components", "bower_components",
"test", "test",
"coverage", "coverage",
"spec",
"tests" "tests"
] ]
} }

View File

@@ -10,17 +10,17 @@
"spec": ">=1.6.7 <2.0.0", "spec": ">=1.6.7 <2.0.0",
"type": "range" "type": "range"
}, },
"/Users/jbowser/cordova/cordova-android/node_modules/bplist-parser" "/Users/steveng/repo/cordova/cordova-android/node_modules/bplist-parser"
] ]
], ],
"_from": "big-integer@>=1.6.7 <2.0.0", "_from": "big-integer@>=1.6.7 <2.0.0",
"_id": "big-integer@1.6.26", "_id": "big-integer@1.6.19",
"_inCache": true, "_inCache": true,
"_location": "/big-integer", "_location": "/big-integer",
"_nodeVersion": "6.10.3", "_nodeVersion": "6.9.4",
"_npmOperationalInternal": { "_npmOperationalInternal": {
"host": "s3://npm-registry-packages", "host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/big-integer-1.6.26.tgz_1510889021794_0.842821853235364" "tmp": "tmp/big-integer-1.6.19.tgz_1491096256363_0.04815615131519735"
}, },
"_npmUser": { "_npmUser": {
"name": "peterolson", "name": "peterolson",
@@ -40,11 +40,11 @@
"_requiredBy": [ "_requiredBy": [
"/bplist-parser" "/bplist-parser"
], ],
"_resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.26.tgz", "_resolved": "http://registry.npmjs.org/big-integer/-/big-integer-1.6.19.tgz",
"_shasum": "3af1672fa62daf2d5ecafacf6e5aa0d25e02c1c8", "_shasum": "4a5e915e3188c8708f254b356196f28542acc1e0",
"_shrinkwrap": null, "_shrinkwrap": null,
"_spec": "big-integer@^1.6.7", "_spec": "big-integer@^1.6.7",
"_where": "/Users/jbowser/cordova/cordova-android/node_modules/bplist-parser", "_where": "/Users/steveng/repo/cordova/cordova-android/node_modules/bplist-parser",
"author": { "author": {
"name": "Peter Olson", "name": "Peter Olson",
"email": "peter.e.c.olson+npm@gmail.com" "email": "peter.e.c.olson+npm@gmail.com"
@@ -57,28 +57,23 @@
"dependencies": {}, "dependencies": {},
"description": "An arbitrary length integer library for Javascript", "description": "An arbitrary length integer library for Javascript",
"devDependencies": { "devDependencies": {
"@types/lodash": "^4.14.64",
"@types/node": "^7.0.22",
"coveralls": "^2.11.4", "coveralls": "^2.11.4",
"jasmine": "2.1.x", "jasmine": "2.1.x",
"jasmine-core": "^2.3.4", "jasmine-core": "^2.3.4",
"karma": "^0.13.3", "karma": "^0.13.3",
"karma-coverage": "^0.4.2", "karma-coverage": "^0.4.2",
"karma-jasmine": "^0.3.6", "karma-jasmine": "^0.3.6",
"karma-phantomjs-launcher": "^1.0.4", "karma-phantomjs-launcher": "~0.1"
"lodash": "^4.17.4",
"typescript": "^2.3.3",
"uglifyjs": "^2.4.10"
}, },
"directories": {}, "directories": {},
"dist": { "dist": {
"shasum": "3af1672fa62daf2d5ecafacf6e5aa0d25e02c1c8", "shasum": "4a5e915e3188c8708f254b356196f28542acc1e0",
"tarball": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.26.tgz" "tarball": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.19.tgz"
}, },
"engines": { "engines": {
"node": ">=0.6" "node": ">=0.6"
}, },
"gitHead": "b1c6e0e95eca0a0d19ebbb9cc81ec492448a9e8a", "gitHead": "f0f751478d6623a84a5ed9618d94937829bbd015",
"homepage": "https://github.com/peterolson/BigInteger.js#readme", "homepage": "https://github.com/peterolson/BigInteger.js#readme",
"keywords": [ "keywords": [
"math", "math",
@@ -107,9 +102,7 @@
"url": "git+ssh://git@github.com/peterolson/BigInteger.js.git" "url": "git+ssh://git@github.com/peterolson/BigInteger.js.git"
}, },
"scripts": { "scripts": {
"minify": "uglifyjs BigInteger.js -o BigInteger.min.js", "test": "karma start my.conf.js"
"test": "tsc && node_modules/.bin/karma start my.conf.js && node spec/tsDefinitions.js"
}, },
"typings": "./BigInteger.d.ts", "version": "1.6.19"
"version": "1.6.26"
} }

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