mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-01 02:12:58 +08:00
331 lines
12 KiB
JavaScript
331 lines
12 KiB
JavaScript
#!/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 shell = require('shelljs'),
|
|
exec = require('./exec'),
|
|
Q = require('q'),
|
|
path = require('path'),
|
|
appinfo = require('./appinfo'),
|
|
build = require('./build'),
|
|
ROOT = path.join(__dirname, '..', '..'),
|
|
child_process = require('child_process'),
|
|
new_emulator = 'cordova_emulator';
|
|
|
|
/**
|
|
* 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 exec('android list avds')
|
|
.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(/Path:\s/)) {
|
|
i++;
|
|
img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
|
|
}
|
|
if (response[i + 1].match(/\(API\slevel\s/)) {
|
|
i++;
|
|
img_obj['target'] = response[i].replace('\r', '');
|
|
}
|
|
if (response[i + 1].match(/ABI:\s/)) {
|
|
i++;
|
|
img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', '');
|
|
}
|
|
if (response[i + 1].match(/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;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Will return the closest avd to the projects target
|
|
* or undefined if no avds exist.
|
|
* Returns a promise.
|
|
*/
|
|
module.exports.best_image = function() {
|
|
var project_target = this.get_target().replace('android-', '');
|
|
return this.list_images()
|
|
.then(function(images) {
|
|
var closest = 9999;
|
|
var best = images[0];
|
|
for (i in images) {
|
|
var target = images[i].target;
|
|
if(target) {
|
|
var num = target.split('(API level ')[1].replace(')', '');
|
|
if (num == project_target) {
|
|
return images[i];
|
|
} else if (project_target - num < closest && project_target > num) {
|
|
var closest = project_target - num;
|
|
best = images[i];
|
|
}
|
|
}
|
|
}
|
|
return best;
|
|
});
|
|
}
|
|
|
|
// Returns a promise.
|
|
module.exports.list_started = function() {
|
|
return exec('adb devices')
|
|
.then(function(output) {
|
|
var response = output.split('\n');
|
|
var started_emulator_list = [];
|
|
for (var i = 1; i < response.length; i++) {
|
|
if (response[i].match(/device/) && response[i].match(/emulator/)) {
|
|
started_emulator_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
|
|
}
|
|
}
|
|
return started_emulator_list;
|
|
});
|
|
}
|
|
|
|
module.exports.get_target = function() {
|
|
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties'));
|
|
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
|
|
}
|
|
|
|
// Returns a promise.
|
|
module.exports.list_targets = function() {
|
|
return exec('android list targets')
|
|
.then(function(output) {
|
|
var target_out = output.split('\n');
|
|
var targets = [];
|
|
for (var i = target_out.length; i >= 0; i--) {
|
|
if(target_out[i].match(/id:/)) {
|
|
targets.push(targets[i].split(' ')[1]);
|
|
}
|
|
}
|
|
return targets;
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Starts an emulator with the given ID,
|
|
* and returns the started ID of that emulator.
|
|
* If no ID is given it will used the first image availible,
|
|
* if no image is availible it will error out (maybe create one?).
|
|
*
|
|
* Returns a promise.
|
|
*/
|
|
module.exports.start = function(emulator_ID) {
|
|
var self = this;
|
|
var emulator_id, num_started, started_emulators;
|
|
|
|
return self.list_started()
|
|
.then(function(list) {
|
|
started_emulators = list;
|
|
num_started = started_emulators.length;
|
|
if (typeof emulator_ID === 'undefined') {
|
|
return self.list_images()
|
|
.then(function(emulator_list) {
|
|
if (emulator_list.length > 0) {
|
|
return self.best_image()
|
|
.then(function(best) {
|
|
emulator_ID = best.name;
|
|
console.log('WARNING : no emulator specified, defaulting to ' + emulator_ID);
|
|
return emulator_ID;
|
|
});
|
|
} else {
|
|
return Q.reject('ERROR : No emulator images (avds) found, if you would like to create an\n' +
|
|
' avd follow the instructions provided here:\n' +
|
|
' http://developer.android.com/tools/devices/index.html\n' +
|
|
' Or run \'android create avd --name <name> --target <targetID>\'\n' +
|
|
' in on the command line.');
|
|
}
|
|
});
|
|
} else {
|
|
return Q(emulator_ID);
|
|
}
|
|
}).then(function() {
|
|
var cmd, args;
|
|
if(process.platform == 'win32' || process.platform == 'win64') {
|
|
cmd = '%comspec%';
|
|
args = ['/c', 'start', 'cmd', '/c', 'emulator', '-avd', emulator_ID];
|
|
} else {
|
|
cmd = 'emulator';
|
|
args = ['-avd', emulator_ID];
|
|
}
|
|
var proc = child_process.spawn(cmd, args, { stdio: 'inherit', detached: true });
|
|
proc.unref(); // Don't wait for it to finish, since the emulator will probably keep running for a long time.
|
|
}).then(function() {
|
|
// wait for emulator to start
|
|
console.log('Waiting for emulator...');
|
|
return self.wait_for_emulator(num_started);
|
|
}).then(function(new_started) {
|
|
if (new_started.length > 1) {
|
|
for (i in new_started) {
|
|
if (started_emulators.indexOf(new_started[i]) < 0) {
|
|
emulator_id = new_started[i];
|
|
}
|
|
}
|
|
} else {
|
|
emulator_id = new_started[0];
|
|
}
|
|
if (!emulator_id) return Q.reject('ERROR : Failed to start emulator, could not find new emulator');
|
|
|
|
//wait for emulator to boot up
|
|
process.stdout.write('Booting up emulator (this may take a while)...');
|
|
return self.wait_for_boot(emulator_id);
|
|
}).then(function() {
|
|
console.log('BOOT COMPLETE');
|
|
|
|
//unlock screen
|
|
return exec('adb -s ' + emulator_id + ' shell input keyevent 82');
|
|
}).then(function() {
|
|
//return the new emulator id for the started emulators
|
|
return emulator_id;
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Waits for the new emulator to apear on the started-emulator list.
|
|
* Returns a promise with a list of newly started emulators' IDs.
|
|
*/
|
|
module.exports.wait_for_emulator = function(num_running) {
|
|
var self = this;
|
|
return self.list_started()
|
|
.then(function(new_started) {
|
|
if (new_started.length > num_running) {
|
|
return new_started;
|
|
} else {
|
|
return Q.delay(1000).then(function() {
|
|
return self.wait_for_emulator(num_running);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Waits for the boot animation property of the emulator to switch to 'stopped'
|
|
*/
|
|
module.exports.wait_for_boot = function(emulator_id) {
|
|
var self = this;
|
|
return exec('adb -s ' + emulator_id + ' shell getprop init.svc.bootanim')
|
|
.then(function(output) {
|
|
if (output.match(/stopped/)) {
|
|
return;
|
|
} else {
|
|
process.stdout.write('.');
|
|
return Q.delay(3000).then(function() {
|
|
return self.wait_for_boot(emulator_id);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Create avd
|
|
* TODO : Enter the stdin input required to complete the creation of an avd.
|
|
* Returns a promise.
|
|
*/
|
|
module.exports.create_image = function(name, target) {
|
|
console.log('Creating avd named ' + name);
|
|
if (target) {
|
|
return exec('android create avd --name ' + name + ' --target ' + target)
|
|
.then(null, function(error) {
|
|
console.error('ERROR : Failed to create emulator image : ');
|
|
console.error(' Do you have the latest android targets including ' + target + '?');
|
|
console.error(create.output);
|
|
});
|
|
} else {
|
|
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
|
|
return exec('android create avd --name ' + name + ' --target ' + this.list_targets()[0])
|
|
.then(function() {
|
|
// TODO: This seems like another error case, even though it always happens.
|
|
console.error('ERROR : Unable to create an avd emulator, no targets found.');
|
|
console.error('Please insure you have targets availible by runing the "android" command');
|
|
return Q.reject();
|
|
}, function(error) {
|
|
console.error('ERROR : Failed to create emulator image : ');
|
|
console.error(error);
|
|
});
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Installs a previously built application on the emulator and launches it.
|
|
* If no target is specified, then it picks one.
|
|
* If no started emulators are found, error out.
|
|
* Returns a promise.
|
|
*/
|
|
module.exports.install = function(target) {
|
|
var self = this;
|
|
return this.list_started()
|
|
.then(function(emulator_list) {
|
|
if (emulator_list.length < 1) {
|
|
return Q.reject('No started emulators found, please start an emultor before deploying your project.');
|
|
}
|
|
|
|
// default emulator
|
|
target = typeof target !== 'undefined' ? target : emulator_list[0];
|
|
if (emulator_list.indexOf(target) < 0) {
|
|
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
|
|
}
|
|
|
|
console.log('Installing app on emulator...');
|
|
var apk_path = build.get_apk();
|
|
return exec('adb -s ' + target + ' install -r ' + apk_path);
|
|
}).then(function(output) {
|
|
if (output.match(/Failure/)) {
|
|
return Q.reject('Failed to install apk to emulator: ' + output);
|
|
}
|
|
return Q();
|
|
}, function(err) {
|
|
return Q.reject('Failed to install apk to emulator: ' + err);
|
|
}).then(function() {
|
|
//unlock screen
|
|
return exec('adb -s ' + target + ' shell input keyevent 82');
|
|
}).then(function() {
|
|
// launch the application
|
|
console.log('Launching application...');
|
|
var launchName = appinfo.getActivityName();
|
|
cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
|
|
return exec(cmd);
|
|
}).then(function(output) {
|
|
console.log('LAUNCH SUCCESS');
|
|
}, function(err) {
|
|
return Q.reject('Failed to launch app on emulator: ' + err);
|
|
});
|
|
}
|