summaryrefslogtreecommitdiff
path: root/StoneIsland/platforms/android/cordova/lib
diff options
context:
space:
mode:
Diffstat (limited to 'StoneIsland/platforms/android/cordova/lib')
-rwxr-xr-xStoneIsland/platforms/android/cordova/lib/android_sdk.js106
-rwxr-xr-xStoneIsland/platforms/android/cordova/lib/android_sdk_version.js64
-rw-r--r--StoneIsland/platforms/android/cordova/lib/builders/GradleBuilder.js39
-rw-r--r--StoneIsland/platforms/android/cordova/lib/check_reqs.js230
-rw-r--r--StoneIsland/platforms/android/cordova/lib/emulator.js131
-rwxr-xr-xStoneIsland/platforms/android/cordova/lib/list-devices2
-rwxr-xr-xStoneIsland/platforms/android/cordova/lib/list-emulator-images2
-rwxr-xr-xStoneIsland/platforms/android/cordova/lib/list-started-emulators2
-rw-r--r--StoneIsland/platforms/android/cordova/lib/prepare.js40
9 files changed, 444 insertions, 172 deletions
diff --git a/StoneIsland/platforms/android/cordova/lib/android_sdk.js b/StoneIsland/platforms/android/cordova/lib/android_sdk.js
new file mode 100755
index 00000000..a1a806a6
--- /dev/null
+++ b/StoneIsland/platforms/android/cordova/lib/android_sdk.js
@@ -0,0 +1,106 @@
+/*
+ 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'),
+ superspawn = require('cordova-common').superspawn;
+
+var suffix_number_regex = /(\d+)$/;
+// Used for sorting Android targets, example strings to sort:
+// android-19
+// android-L
+// Google Inc.:Google APIs:20
+// Google Inc.:Glass Development Kit Preview:20
+// The idea is to sort based on largest "suffix" number - meaning the bigger
+// the number at the end, the more recent the target, the closer to the
+// start of the array.
+function sort_by_largest_numerical_suffix(a, b) {
+ var suffix_a = a.match(suffix_number_regex);
+ var suffix_b = b.match(suffix_number_regex);
+ if (suffix_a && suffix_b) {
+ // If the two targets being compared have suffixes, return less than
+ // zero, or greater than zero, based on which suffix is larger.
+ return (parseInt(suffix_a[1]) > parseInt(suffix_b[1]) ? -1 : 1);
+ } else {
+ // If no suffix numbers were detected, leave the order as-is between
+ // elements a and b.
+ return 0;
+ }
+}
+
+module.exports.print_newest_available_sdk_target = function() {
+ return module.exports.list_targets()
+ .then(function(targets) {
+ targets.sort(sort_by_largest_numerical_suffix);
+ console.log(targets[0]);
+ });
+};
+
+module.exports.version_string_to_api_level = {
+ '4.0': 14,
+ '4.0.3': 15,
+ '4.1': 16,
+ '4.2': 17,
+ '4.3': 18,
+ '4.4': 19,
+ '4.4W': 20,
+ '5.0': 21,
+ '5.1': 22,
+ '6.0': 23,
+ '7.0': 24,
+ '7.1.1': 25
+};
+
+function parse_targets(output) {
+ var target_out = output.split('\n');
+ var targets = [];
+ for (var i = target_out.length - 1; i >= 0; i--) {
+ if(target_out[i].match(/id:/)) { // if "id:" is in the line...
+ targets.push(target_out[i].match(/"(.+)"/)[1]); //.. match whatever is in quotes.
+ }
+ }
+ return targets;
+}
+
+module.exports.list_targets_with_android = function() {
+ return superspawn.spawn('android', ['list', 'target'])
+ .then(parse_targets);
+};
+
+module.exports.list_targets_with_avdmanager = function() {
+ return superspawn.spawn('avdmanager', ['list', 'target'])
+ .then(parse_targets);
+};
+
+module.exports.list_targets = function() {
+ return module.exports.list_targets_with_avdmanager()
+ .catch(function(err) {
+ // If there's an error, like avdmanager could not be found, we can try
+ // as a last resort, to run `android`, in case this is a super old
+ // SDK installation.
+ if (err && (err.code == 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) {
+ return module.exports.list_targets_with_android();
+ } else throw err;
+ })
+ .then(function(targets) {
+ if (targets.length === 0) {
+ return Q.reject(new Error('No android targets (SDKs) installed!'));
+ }
+ return targets;
+ });
+};
diff --git a/StoneIsland/platforms/android/cordova/lib/android_sdk_version.js b/StoneIsland/platforms/android/cordova/lib/android_sdk_version.js
deleted file mode 100755
index 79af2727..00000000
--- a/StoneIsland/platforms/android/cordova/lib/android_sdk_version.js
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env node
-
-/*
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
-*/
-
-var child_process = require('child_process'),
- Q = require('q');
-
-var get_highest_sdk = function(results){
- var reg = /\d+/;
- var apiLevels = [];
- for(var i=0;i<results.length;i++){
- apiLevels[i] = parseInt(results[i].match(reg)[0]);
- }
- apiLevels.sort(function(a,b){return b-a;});
- console.log(apiLevels[0]);
-};
-
-var get_sdks = function() {
- var d = Q.defer();
- child_process.exec('android list targets', function(err, stdout, stderr) {
- if (err) d.reject(stderr);
- else d.resolve(stdout);
- });
-
- return d.promise.then(function(output) {
- var reg = /android-\d+/gi;
- var results = output.match(reg);
- if(results.length===0){
- return Q.reject(new Error('No android sdks installed.'));
- }else{
- get_highest_sdk(results);
- }
-
- return Q();
- }, function(stderr) {
- if (stderr.match(/command\snot\sfound/) || stderr.match(/'android' is not recognized/)) {
- return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
- } else {
- return Q.reject(new Error('An error occurred while listing Android targets'));
- }
- });
-};
-
-module.exports.run = function() {
- return Q.all([get_sdks()]);
-};
-
diff --git a/StoneIsland/platforms/android/cordova/lib/builders/GradleBuilder.js b/StoneIsland/platforms/android/cordova/lib/builders/GradleBuilder.js
index f415646e..5b5ce13d 100644
--- a/StoneIsland/platforms/android/cordova/lib/builders/GradleBuilder.js
+++ b/StoneIsland/platforms/android/cordova/lib/builders/GradleBuilder.js
@@ -65,6 +65,21 @@ GradleBuilder.prototype.getArgs = function(cmd, opts) {
return args;
};
+/*
+ * This returns a promise
+ */
+
+GradleBuilder.prototype.runGradleWrapper = function(gradle_cmd) {
+ var gradlePath = path.join(this.root, 'gradlew');
+ var wrapperGradle = path.join(this.root, 'wrapper.gradle');
+ if(fs.existsSync(gradlePath)) {
+ //Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
+ } else {
+ return spawn(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], {stdio: 'inherit'});
+ }
+};
+
+
// Makes the project buildable, minus the gradle wrapper.
GradleBuilder.prototype.prepBuildFiles = function() {
// Update the version of build.gradle in each dependent library.
@@ -159,17 +174,15 @@ GradleBuilder.prototype.prepBuildFiles = function() {
GradleBuilder.prototype.prepEnv = function(opts) {
var self = this;
return check_reqs.check_gradle()
- .then(function() {
- return self.prepBuildFiles();
- }).then(function() {
- // Copy the gradle wrapper on each build so that:
- // A) we don't require the Android SDK at project creation time, and
- // B) we always use the SDK's latest version of it.
- // check_reqs ensures that this is set.
- /*jshint -W069 */
- var sdkDir = process.env['ANDROID_HOME'];
- /*jshint +W069 */
- var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
+ .then(function(gradlePath) {
+ return self.runGradleWrapper(gradlePath);
+ }).then(function() {
+ return self.prepBuildFiles();
+ }).then(function() {
+ // We now copy the gradle out of the framework
+ // This is a dirty patch to get the build working
+ /*
+ var wrapperDir = path.join(self.root, 'CordovaLib');
if (process.platform == 'win32') {
shell.rm('-f', path.join(self.root, 'gradlew.bat'));
shell.cp(path.join(wrapperDir, 'gradlew.bat'), self.root);
@@ -180,13 +193,13 @@ GradleBuilder.prototype.prepEnv = function(opts) {
shell.rm('-rf', path.join(self.root, 'gradle', 'wrapper'));
shell.mkdir('-p', path.join(self.root, 'gradle'));
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(self.root, 'gradle'));
-
+*/
// If the gradle distribution URL is set, make sure it points to version we want.
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
// For some reason, using ^ and $ don't work. This does the job, though.
var distributionUrlRegex = /distributionUrl.*zip/;
/*jshint -W069 */
- var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-2.14.1-all.zip';
+ var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'https\\://services.gradle.org/distributions/gradle-3.3-all.zip';
/*jshint +W069 */
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.chmod('u+w', gradleWrapperPropertiesPath);
diff --git a/StoneIsland/platforms/android/cordova/lib/check_reqs.js b/StoneIsland/platforms/android/cordova/lib/check_reqs.js
index ac6fa4c1..1fd397ad 100644
--- a/StoneIsland/platforms/android/cordova/lib/check_reqs.js
+++ b/StoneIsland/platforms/android/cordova/lib/check_reqs.js
@@ -26,10 +26,12 @@ var shelljs = require('shelljs'),
Q = require('q'),
path = require('path'),
fs = require('fs'),
- ROOT = path.join(__dirname, '..', '..');
+ os = require('os'),
+ REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'),
+ PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError;
-
-var isWindows = process.platform == 'win32';
+var superspawn = require('cordova-common').superspawn;
+var android_sdk = require('./android_sdk');
function forgivingWhichSync(cmd) {
try {
@@ -50,7 +52,16 @@ function tryCommand(cmd, errMsg, catchStderr) {
return d.promise;
}
-// Get valid target from framework/project.properties
+module.exports.isWindows = function() {
+ return (os.platform() == 'win32');
+};
+
+module.exports.isDarwin = function() {
+ return (os.platform() == 'darwin');
+};
+
+// Get valid target from framework/project.properties if run from this repo
+// Otherwise get target from project.properties file within a generated cordova-android project
module.exports.get_target = function() {
function extractFromFile(filePath) {
var target = shelljs.grep(/\btarget=/, filePath);
@@ -59,38 +70,83 @@ module.exports.get_target = function() {
}
return target.split('=')[1].trim();
}
- if (fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
- return extractFromFile(path.join(ROOT, 'framework', 'project.properties'));
+ var repo_file = path.join(REPO_ROOT, 'framework', 'project.properties');
+ if (fs.existsSync(repo_file)) {
+ return extractFromFile(repo_file);
}
- if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
- // if no target found, we're probably in a project and project.properties is in ROOT.
- return extractFromFile(path.join(ROOT, 'project.properties'));
+ var project_file = path.join(PROJECT_ROOT, 'project.properties');
+ if (fs.existsSync(project_file)) {
+ // if no target found, we're probably in a project and project.properties is in PROJECT_ROOT.
+ return extractFromFile(project_file);
}
- throw new Error('Could not find android target. File missing: ' + path.join(ROOT, 'project.properties'));
+ throw new Error('Could not find android target in either ' + repo_file + ' nor ' + project_file);
};
// Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function() {
- return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.')
- .then(function (output) {
+ return superspawn.spawn('ant', ['-version'])
+ .then(function(output) {
// Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
+ }).catch(function(err) {
+ throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
});
};
+module.exports.get_gradle_wrapper = function() {
+ var androidStudioPath;
+ var i = 0;
+ var foundStudio = false;
+ var program_dir;
+ if (module.exports.isDarwin()) {
+ program_dir = fs.readdirSync('/Applications');
+ while (i < program_dir.length && !foundStudio) {
+ if (program_dir[i].startsWith('Android Studio')) {
+ //TODO: Check for a specific Android Studio version, make sure it's not Canary
+ androidStudioPath = path.join('/Applications', program_dir[i], 'Contents', 'gradle');
+ foundStudio = true;
+ } else { ++i; }
+ }
+ } else if (module.exports.isWindows()) {
+ var androidPath = path.join(process.env['ProgramFiles'], 'Android') + '/';
+ if (fs.existsSync(androidPath)) {
+ program_dir = fs.readdirSync(androidPath);
+ while (i < program_dir.length && !foundStudio) {
+ if (program_dir[i].startsWith('Android Studio')) {
+ foundStudio = true;
+ androidStudioPath = path.join(process.env['ProgramFiles'], 'Android', program_dir[i], 'gradle');
+ } else { ++i; }
+ }
+ }
+ }
+
+ if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
+ var dirs = fs.readdirSync(androidStudioPath);
+ if(dirs[0].split('-')[0] == 'gradle') {
+ return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
+ }
+ } else {
+ //OK, let's try to check for Gradle!
+ return forgivingWhichSync('gradle');
+ }
+};
+
// Returns a promise. Called only by build and clean commands.
module.exports.check_gradle = function() {
var sdkDir = process.env['ANDROID_HOME'];
+ var d = Q.defer();
if (!sdkDir)
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
- var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
- if (!fs.existsSync(wrapperDir)) {
- return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Might need to update your Android SDK.\n' +
- 'Looked here: ' + wrapperDir));
- }
- return Q.when();
+ var gradlePath = module.exports.get_gradle_wrapper();
+ if (gradlePath.length !== 0)
+ d.resolve(gradlePath);
+ else
+ d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
+ 'or on your system to install the gradle wrapper. Please include gradle \n' +
+ 'in your path, or install Android Studio'));
+ return d.promise;
};
// Returns a promise.
@@ -105,12 +161,15 @@ module.exports.check_java = function() {
}
} else {
if (javacPath) {
- var msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
// OS X has a command for finding JAVA_HOME.
- if (fs.existsSync('/usr/libexec/java_home')) {
- return tryCommand('/usr/libexec/java_home', msg)
+ var find_java = '/usr/libexec/java_home';
+ var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
+ if (fs.existsSync(find_java)) {
+ return superspawn.spawn(find_java)
.then(function(stdout) {
process.env['JAVA_HOME'] = stdout.trim();
+ }).catch(function(err) {
+ throw new CordovaError(default_java_error_msg);
});
} else {
// See if we can derive it from javac's location.
@@ -119,10 +178,10 @@ module.exports.check_java = function() {
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
process.env['JAVA_HOME'] = maybeJavaHome;
} else {
- throw new CordovaError(msg);
+ throw new CordovaError(default_java_error_msg);
}
}
- } else if (isWindows) {
+ } else if (module.exports.isWindows()) {
// Try to auto-detect java in the default install paths.
var oldSilent = shelljs.config.silent;
shelljs.config.silent = true;
@@ -142,28 +201,29 @@ module.exports.check_java = function() {
}
}
}).then(function() {
- var msg =
- 'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
- 'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
- if (process.env['JAVA_HOME']) {
- msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
- }
- // We use tryCommand with catchStderr = true, because
- // javac writes version info to stderr instead of stdout
- return tryCommand('javac -version', msg, true)
- .then(function (output) {
- //Let's check for at least Java 8, and keep it future proof so we can support Java 10
- var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
- return match && match[1];
- });
+ var msg =
+ 'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
+ 'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
+ if (process.env['JAVA_HOME']) {
+ msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
+ }
+ // We use tryCommand with catchStderr = true, because
+ // javac writes version info to stderr instead of stdout
+ return tryCommand('javac -version', msg, true)
+ .then(function (output) {
+ //Let's check for at least Java 8, and keep it future proof so we can support Java 10
+ var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
+ return match && match[1];
});
+ });
};
// Returns a promise.
module.exports.check_android = function() {
return Q().then(function() {
var androidCmdPath = forgivingWhichSync('android');
- var adbInPath = !!forgivingWhichSync('adb');
+ var adbInPath = forgivingWhichSync('adb');
+ var avdmanagerInPath = forgivingWhichSync('avdmanager');
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
function maybeSetAndroidHome(value) {
if (!hasAndroidHome && fs.existsSync(value)) {
@@ -171,8 +231,10 @@ module.exports.check_android = function() {
process.env['ANDROID_HOME'] = value;
}
}
- if (!hasAndroidHome && !androidCmdPath) {
- if (isWindows) {
+ // First ensure ANDROID_HOME is set
+ // If we have no hints (nothing in PATH), try a few default locations
+ if (!hasAndroidHome && !androidCmdPath && !adbInPath && !avdmanagerInPath) {
+ if (module.exports.isWindows()) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'sdk'));
@@ -182,7 +244,7 @@ module.exports.check_android = function() {
// Stand-alone installer
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk'));
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk'));
- } else if (process.platform == 'darwin') {
+ } else if (module.exports.isDarwin()) {
// Android Studio 1.0 installer
maybeSetAndroidHome(path.join(process.env['HOME'], 'Library', 'Android', 'sdk'));
// Android Studio pre-1.0 installer
@@ -197,26 +259,42 @@ module.exports.check_android = function() {
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk'));
}
}
- if (hasAndroidHome && !androidCmdPath) {
- process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
- }
- if (androidCmdPath && !hasAndroidHome) {
- var parentDir = path.dirname(androidCmdPath);
- var grandParentDir = path.dirname(parentDir);
- if (path.basename(parentDir) == 'tools') {
- process.env['ANDROID_HOME'] = path.dirname(parentDir);
- hasAndroidHome = true;
- } else if (fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
- process.env['ANDROID_HOME'] = grandParentDir;
- hasAndroidHome = true;
- } else {
- throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
- 'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' +
- 'Try reinstall Android SDK or update your PATH to include path to valid SDK directory.');
+ if (!hasAndroidHome) {
+ // If we dont have ANDROID_HOME, but we do have some tools on the PATH, try to infer from the tooling PATH.
+ var parentDir, grandParentDir;
+ if (androidCmdPath) {
+ parentDir = path.dirname(androidCmdPath);
+ grandParentDir = path.dirname(parentDir);
+ if (path.basename(parentDir) == 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
+ maybeSetAndroidHome(grandParentDir);
+ } else {
+ throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
+ 'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' +
+ 'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools directory.');
+ }
+ }
+ if (adbInPath) {
+ parentDir = path.dirname(adbInPath);
+ grandParentDir = path.dirname(parentDir);
+ if (path.basename(parentDir) == 'platform-tools') {
+ maybeSetAndroidHome(grandParentDir);
+ } else {
+ throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
+ 'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' +
+ 'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.');
+ }
+ }
+ if (avdmanagerInPath) {
+ parentDir = path.dirname(avdmanagerInPath);
+ grandParentDir = path.dirname(parentDir);
+ if (path.basename(parentDir) == 'bin' && path.basename(grandParentDir) == 'tools') {
+ maybeSetAndroidHome(path.dirname(grandParentDir));
+ } else {
+ throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
+ 'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
+ 'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
+ }
}
- }
- if (hasAndroidHome && !adbInPath) {
- process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
}
if (!process.env['ANDROID_HOME']) {
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
@@ -226,13 +304,27 @@ module.exports.check_android = function() {
throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] +
'\nTry update it manually to point to valid SDK directory.');
}
+ // Next let's make sure relevant parts of the SDK tooling is in our PATH
+ if (hasAndroidHome && !androidCmdPath) {
+ process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
+ }
+ if (hasAndroidHome && !adbInPath) {
+ process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
+ }
+ if (hasAndroidHome && !avdmanagerInPath) {
+ process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools', 'bin');
+ }
return hasAndroidHome;
});
};
+// TODO: is this actually needed?
module.exports.getAbsoluteAndroidCmd = function () {
var cmd = forgivingWhichSync('android');
- if (process.platform === 'win32') {
+ if (cmd.length === 0) {
+ cmd = forgivingWhichSync('sdkmanager');
+ }
+ if (module.exports.isWindows()) {
return '"' + cmd + '"';
}
return cmd.replace(/(\s)/g, '\\$1');
@@ -244,20 +336,17 @@ module.exports.check_android_target = function(originalError) {
// android-L
// Google Inc.:Google APIs:20
// Google Inc.:Glass Development Kit Preview:20
- var valid_target = module.exports.get_target();
- var msg = 'Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.';
- return tryCommand('android list targets --compact', msg)
- .then(function(output) {
- var targets = output.split('\n');
- if (targets.indexOf(valid_target) >= 0) {
+ var desired_api_level = module.exports.get_target();
+ return android_sdk.list_targets()
+ .then(function(targets) {
+ if (targets.indexOf(desired_api_level) >= 0) {
return targets;
}
-
var androidCmd = module.exports.getAbsoluteAndroidCmd();
- var msg = 'Please install Android target: "' + valid_target + '".\n\n' +
+ var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' +
'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
'You will require:\n' +
- '1. "SDK Platform" for ' + valid_target + '\n' +
+ '1. "SDK Platform" for API level ' + desired_api_level + '\n' +
'2. "Android SDK Platform-tools (latest)\n' +
'3. "Android SDK Build-tools" (latest)';
if (originalError) {
@@ -278,7 +367,6 @@ module.exports.run = function() {
throw new CordovaError('Requirements check failed for JDK 1.8 or greater');
}
-
if (!values[1]) {
throw new CordovaError('Requirements check failed for Android SDK');
}
diff --git a/StoneIsland/platforms/android/cordova/lib/emulator.js b/StoneIsland/platforms/android/cordova/lib/emulator.js
index ff1e261c..22209aa0 100644
--- a/StoneIsland/platforms/android/cordova/lib/emulator.js
+++ b/StoneIsland/platforms/android/cordova/lib/emulator.js
@@ -27,11 +27,15 @@ var path = require('path');
var Adb = require('./Adb');
var AndroidManifest = require('./AndroidManifest');
var events = require('cordova-common').events;
-var spawn = require('cordova-common').superspawn.spawn;
+var superspawn = require('cordova-common').superspawn;
var CordovaError = require('cordova-common').CordovaError;
+var shelljs = require('shelljs');
+var android_sdk = require('./android_sdk');
+var check_reqs = require('./check_reqs');
var Q = require('q');
var os = require('os');
+var fs = require('fs');
var child_process = require('child_process');
// constants
@@ -42,18 +46,77 @@ var NUM_INSTALL_RETRIES = 3;
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
var EXEC_KILL_SIGNAL = 'SIGKILL';
-/**
- * Returns a Promise for a list of emulator images in the form of objects
- * {
- name : <emulator_name>,
- path : <path_to_emulator_image>,
- target : <api_target>,
- abi : <cpu>,
- skin : <skin>
- }
- */
-module.exports.list_images = function() {
- return spawn('android', ['list', 'avds'])
+function forgivingWhichSync(cmd) {
+ try {
+ return fs.realpathSync(shelljs.which(cmd));
+ } catch (e) {
+ return '';
+ }
+}
+
+module.exports.list_images_using_avdmanager = function () {
+ return superspawn.spawn('avdmanager', ['list', 'avd'])
+ .then(function(output) {
+ var response = output.split('\n');
+ var emulator_list = [];
+ for (var i = 1; i < response.length; i++) {
+ // To return more detailed information use img_obj
+ var img_obj = {};
+ if (response[i].match(/Name:\s/)) {
+ img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
+ if (response[i + 1].match(/Device:\s/)) {
+ i++;
+ img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
+ }
+ if (response[i + 1].match(/Path:\s/)) {
+ i++;
+ img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
+ }
+ if (response[i + 1].match(/Target:\s/)) {
+ i++;
+ if (response[i + 1].match(/ABI:\s/)) {
+ img_obj['abi'] = response[i + 1].split('ABI: ')[1].replace('\r', '');
+ }
+ // This next conditional just aims to match the old output of `android list avd`
+ // We do so so that we don't have to change the logic when parsing for the
+ // best emulator target to spawn (see below in `best_image`)
+ // This allows us to transitionally support both `android` and `avdmanager` binaries,
+ // depending on what SDK version the user has
+ if (response[i + 1].match(/Based\son:\s/)) {
+ img_obj['target'] = response[i + 1].split('Based on:')[1];
+ if (img_obj['target'].match(/Tag\/ABI:\s/)) {
+ img_obj['target'] = img_obj['target'].split('Tag/ABI:')[0].replace('\r', '').trim();
+ if (img_obj['target'].indexOf('(') > -1) {
+ img_obj['target'] = img_obj['target'].substr(0, img_obj['target'].indexOf('(') - 1).trim();
+ }
+ }
+ var version_string = img_obj['target'].replace(/Android\s+/, '');
+
+ var api_level = android_sdk.version_string_to_api_level[version_string];
+ if (api_level) {
+ img_obj['target'] += ' (API level ' + api_level + ')';
+ }
+ }
+ }
+ if (response[i + 1].match(/Skin:\s/)) {
+ i++;
+ img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
+ }
+
+ emulator_list.push(img_obj);
+ }
+ /* To just return a list of names use this
+ if (response[i].match(/Name:\s/)) {
+ emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
+ }*/
+
+ }
+ return emulator_list;
+ });
+};
+
+module.exports.list_images_using_android = function() {
+ return superspawn.spawn('android', ['list', 'avd'])
.then(function(output) {
var response = output.split('\n');
var emulator_list = [];
@@ -97,6 +160,29 @@ module.exports.list_images = function() {
};
/**
+ * Returns a Promise for a list of emulator images in the form of objects
+ * {
+ name : <emulator_name>,
+ device : <device>,
+ path : <path_to_emulator_image>,
+ target : <api_target>,
+ abi : <cpu>,
+ skin : <skin>
+ }
+ */
+module.exports.list_images = function() {
+ if (forgivingWhichSync('avdmanager')) {
+ return module.exports.list_images_using_avdmanager();
+ } else if (forgivingWhichSync('android')) {
+ return module.exports.list_images_using_android();
+ } else {
+ return Q().then(function() {
+ throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
+ });
+ }
+};
+
+/**
* Will return the closest avd to the projects target
* or undefined if no avds exist.
* Returns a promise.
@@ -109,8 +195,7 @@ module.exports.best_image = function() {
var closest = 9999;
var best = images[0];
- // Loading check_reqs at run-time to avoid test-time vs run-time directory structure difference issue
- var project_target = require('./check_reqs').get_target().replace('android-', '');
+ var project_target = check_reqs.get_target().replace('android-', '');
for (var i in images) {
var target = images[i].target;
if(target) {
@@ -133,8 +218,9 @@ module.exports.list_started = function() {
};
// Returns a promise.
+// TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function() {
- return spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
+ return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
.then(function(output) {
var target_out = output.split('\n');
var targets = [];
@@ -189,8 +275,7 @@ module.exports.start = function(emulator_ID, boot_timeout) {
return best.name;
}
- // Loading check_reqs at run-time to avoid test-time vs run-time directory structure difference issue
- var androidCmd = require('./check_reqs').getAbsoluteAndroidCmd();
+ var androidCmd = check_reqs.getAbsoluteAndroidCmd();
return Q.reject(new CordovaError('No emulator images (avds) found.\n' +
'1. Download desired System Image by running: ' + androidCmd + ' sdk\n' +
'2. Create an AVD by running: ' + androidCmd + ' avd\n' +
@@ -199,10 +284,13 @@ module.exports.start = function(emulator_ID, boot_timeout) {
}).then(function(emulatorId) {
return self.get_available_port()
.then(function (port) {
+ // Figure out the directory the emulator binary runs in, and set the cwd to that directory.
+ // Workaround for https://code.google.com/p/android/issues/detail?id=235461
+ var emulator_dir = path.dirname(shelljs.which('emulator'));
var args = ['-avd', emulatorId, '-port', port];
// Don't wait for it to finish, since the emulator will probably keep running for a long time.
child_process
- .spawn('emulator', args, { stdio: 'inherit', detached: true })
+ .spawn('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
.unref();
// wait for emulator to start
@@ -294,7 +382,7 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
module.exports.create_image = function(name, target) {
console.log('Creating new avd named ' + name);
if (target) {
- return spawn('android', ['create', 'avd', '--name', name, '--target', target])
+ return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', target])
.then(null, function(error) {
console.error('ERROR : Failed to create emulator image : ');
console.error(' Do you have the latest android targets including ' + target + '?');
@@ -302,7 +390,8 @@ module.exports.create_image = function(name, target) {
});
} else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
- return spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
+ // TODO: there's a more robust method for finding targets in android_sdk.js
+ return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
.then(function() {
// TODO: This seems like another error case, even though it always happens.
console.error('ERROR : Unable to create an avd emulator, no targets found.');
diff --git a/StoneIsland/platforms/android/cordova/lib/list-devices b/StoneIsland/platforms/android/cordova/lib/list-devices
index fa84d7f6..8e22c7f2 100755
--- a/StoneIsland/platforms/android/cordova/lib/list-devices
+++ b/StoneIsland/platforms/android/cordova/lib/list-devices
@@ -22,7 +22,7 @@
var devices = require('./device');
// Usage support for when args are given
-require('../lib/check_reqs').check_android().then(function() {
+require('./check_reqs').check_android().then(function() {
devices.list().done(function(device_list) {
device_list && device_list.forEach(function(dev) {
console.log(dev);
diff --git a/StoneIsland/platforms/android/cordova/lib/list-emulator-images b/StoneIsland/platforms/android/cordova/lib/list-emulator-images
index 03c827fe..25e5c81a 100755
--- a/StoneIsland/platforms/android/cordova/lib/list-emulator-images
+++ b/StoneIsland/platforms/android/cordova/lib/list-emulator-images
@@ -22,7 +22,7 @@
var emulators = require('./emulator');
// Usage support for when args are given
-require('../lib/check_reqs').check_android().then(function() {
+require('./check_reqs').check_android().then(function() {
emulators.list_images().done(function(emulator_list) {
emulator_list && emulator_list.forEach(function(emu) {
console.log(emu.name);
diff --git a/StoneIsland/platforms/android/cordova/lib/list-started-emulators b/StoneIsland/platforms/android/cordova/lib/list-started-emulators
index a890dec6..43ebda25 100755
--- a/StoneIsland/platforms/android/cordova/lib/list-started-emulators
+++ b/StoneIsland/platforms/android/cordova/lib/list-started-emulators
@@ -22,7 +22,7 @@
var emulators = require('./emulator');
// Usage support for when args are given
-require('../lib/check_reqs').check_android().then(function() {
+require('./check_reqs').check_android().then(function() {
emulators.list_started().done(function(emulator_list) {
emulator_list && emulator_list.forEach(function(emu) {
console.log(emu);
diff --git a/StoneIsland/platforms/android/cordova/lib/prepare.js b/StoneIsland/platforms/android/cordova/lib/prepare.js
index 10a69ea3..504eb612 100644
--- a/StoneIsland/platforms/android/cordova/lib/prepare.js
+++ b/StoneIsland/platforms/android/cordova/lib/prepare.js
@@ -48,6 +48,7 @@ module.exports.prepare = function (cordovaProject, options) {
.then(function () {
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
+ updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
})
.then(function () {
events.emit('verbose', 'Prepared android project successfully');
@@ -72,6 +73,7 @@ module.exports.clean = function (options) {
cleanWww(projectRoot, self.locations);
cleanIcons(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
cleanSplashes(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
+ cleanFileResources(projectRoot, projectConfig, path.relative(projectRoot, self.locations.root));
});
};
@@ -401,6 +403,44 @@ function mapImageResources(rootDir, subDir, type, resourceName) {
return pathMap;
}
+
+function updateFileResources(cordovaProject, platformDir) {
+ var files = cordovaProject.projectConfig.getFileResources('android');
+
+ // if there are resource-file elements in config.xml
+ if (files.length === 0) {
+ events.emit('verbose', 'This app does not have additional resource files defined');
+ return;
+ }
+
+ var resourceMap = {};
+ files.forEach(function(res) {
+ var targetPath = path.join(platformDir, res.target);
+ resourceMap[targetPath] = res.src;
+ });
+
+ events.emit('verbose', 'Updating resource files at ' + platformDir);
+ FileUpdater.updatePaths(
+ resourceMap, { rootDir: cordovaProject.root }, logFileOp);
+}
+
+
+function cleanFileResources(projectRoot, projectConfig, platformDir) {
+ var files = projectConfig.getFileResources('android');
+ if (files.length > 0) {
+ events.emit('verbose', 'Cleaning resource files at ' + platformDir);
+
+ var resourceMap = {};
+ files.forEach(function(res) {
+ var filePath = path.join(platformDir, res.target);
+ resourceMap[filePath] = null;
+ });
+
+ FileUpdater.updatePaths(
+ resourceMap, { rootDir: projectRoot, all: true}, logFileOp);
+ }
+}
+
/**
* Gets and validates 'AndroidLaunchMode' prepference from config.xml. Returns
* preference value and warns if it doesn't seems to be valid