├── .gitignore ├── .jshintrc ├── .travis.yml ├── Gruntfile.js ├── README.md ├── example ├── Gruntfile.js ├── apk │ ├── AeroGear.apk │ └── AeroGearCordova.apk └── package.json ├── package.json ├── src ├── core │ ├── emulator-module.js │ └── wait_emulator_to_boot.sh └── lib │ ├── grunt-module.js │ ├── log-module.js │ └── string-module.js └── tasks └── grunt-android-emulator.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | nohup.out 3 | vert.x-2.0.0-final/ 4 | target/ 5 | avd/ 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "latedef": true, 5 | "noarg": true, 6 | "noempty": true, 7 | "undef": true, 8 | "sub": true, 9 | "trailing": true, 10 | "node": true 11 | } 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk7 3 | android: 4 | components: 5 | - tools 6 | - platform-tools 7 | - build-tools-23.0.2 8 | - android-21 9 | - extra-android-support 10 | - sys-img-armeabi-v7a-android-21 11 | env: 12 | matrix: 13 | - ANDROID_SDKS=android-21 ANDROID_TARGET=android-21 14 | before_script: 15 | - npm install -g grunt-cli 16 | - npm install 17 | - cd example 18 | - npm install 19 | - npm install ../ 20 | script: grunt 21 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JBoss, Home of Professional Open Source 3 | * Copyright Red Hat, Inc., and individual contributors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | module.exports = function(grunt) { 18 | 'use strict'; 19 | 20 | grunt.initConfig({ 21 | pkg: grunt.file.readJSON('package.json'), 22 | /* 23 | grunt_android_emulator: { 24 | emulators: [{ 25 | id: 'emulator-1', 26 | create: { 27 | '--name': 'testAVD', 28 | //'--sdcard': '10M', 29 | //'--snapshot': '', 30 | //'--path': 'avd', 31 | '--force': '', 32 | //'--skin': '', 33 | '--target': 'android-18', 34 | '--abi': 'armeabi-v7a' 35 | }, 36 | start: { 37 | '-port': '5556', 38 | //'-no-window': '', 39 | '-no-audio': '', 40 | // '-force-32bit': '' 41 | //'-no-boot-anim': '', 42 | //'-no-skin': '', 43 | //'-memory': '1024' 44 | //'-avd': 'testAVD' 45 | } 46 | }], 47 | apks: [{ 48 | id: "apk-1", 49 | path: "/home/test/apks/test.apk", 50 | activities: [{ 51 | id: "activity-1", 52 | package: "org.jboss.aerogear", 53 | name: "AeroGearMain" 54 | }] 55 | }] 56 | },*/ 57 | jshint: { 58 | all: { 59 | src: [ "Gruntfile.js", "src/**/*.js", "tasks/*.js" ], 60 | options: { 61 | jshintrc: ".jshintrc" 62 | } 63 | } 64 | } 65 | }); 66 | 67 | grunt.loadTasks('tasks'); 68 | grunt.loadNpmTasks('grunt-contrib-jshint'); 69 | grunt.registerTask('default', ['jshint']); 70 | }; 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-android-emulator 2 | [![Build Status](https://travis-ci.org/tolis-e/grunt-android-emulator.png?branch=0.1.0)](https://travis-ci.org/tolis-e/grunt-android-emulator) [![NPM version](https://badge.fury.io/js/grunt-android-emulator.png)](http://badge.fury.io/js/grunt-android-emulator) 3 | > This project contains a Grunt plugin which includes tasks to: 4 | 5 | * Create an Android emulator 6 | * Start an Android emulator 7 | * Stop an Android emulator 8 | * Unlock an Android emulator 9 | * Install an APK 10 | * Start an activity 11 | 12 | ## Getting Started 13 | This plugin requires Grunt `~0.4.1` 14 | 15 | If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command: 16 | 17 | ```shell 18 | npm install grunt-android-emulator --save-dev 19 | ``` 20 | 21 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 22 | 23 | ```js 24 | grunt.loadNpmTasks('grunt-android-emulator'); 25 | ``` 26 | 27 | ## create-android-emulator task 28 | > This task creates an Android emulator. You can execute it using: 29 | 30 | `grunt create-android-emulator:emulator-1` where `emulator-1` is a the emulator's id as shown in the below configuration. 31 | 32 | ```js 33 | grunt.initConfig({ 34 | grunt_android_emulator: { 35 | emulators: [{ 36 | id: 'emulator-1', 37 | create: { 38 | '--name': 'testAVD', 39 | //'--sdcard': '10M', 40 | //'--snapshot': '', 41 | //'--path': 'avd', 42 | '--force': '', 43 | //'--skin': '', 44 | '--target': 'android-18', 45 | '--abi': 'armeabi-v7a' 46 | } 47 | }] 48 | } 49 | }); 50 | ``` 51 | ### Options 52 | 53 | #### --name or -n 54 | Type: `String` 55 | Description: `Name of the new AVD` 56 | Usage: `[required]` 57 | 58 | #### --target or -t 59 | Type: `String` 60 | Description: `Target ID of the new AVD` 61 | Usage: `[required]` 62 | 63 | #### --path or -p 64 | Type: `String` 65 | Description: `Directory where the new AVD will be created` 66 | Usage: `[optional]` 67 | 68 | #### --force or -f 69 | Type: `-` 70 | Description: `Forces creation (overwrites an existing AVD)` 71 | Usage: `[optional]` 72 | 73 | #### --skin or -s 74 | Type: `String` 75 | Description: `Skin for the new AVD` 76 | Usage: `[optional]` 77 | 78 | #### --device or -d 79 | Type: `String` 80 | Description: `Device definition to use` 81 | Usage: `[optional]` 82 | 83 | #### --abi or -b 84 | Type: `String` 85 | Description: `The ABI to use for the AVD` 86 | Usage: `[optional]` 87 | 88 | #### --sdcard or -c 89 | Type: `String` 90 | Description: `Path to a shared SD card image, or size of a new sdcard for the new AVD` 91 | Usage: `[optional]` 92 | 93 | #### --snapshot or -a 94 | Type: `String` 95 | Description: `Place a snapshots file in the AVD, to enable persistence` 96 | Usage: `[optional]` 97 | 98 | ## start-android-emulator 99 | > This task starts an Android emulator. You can execute it using: 100 | 101 | `grunt start-android-emulator:emulator-1` where `emulator-1` is a the emulator's id as shown in the below configuration. 102 | 103 | ```js 104 | grunt.initConfig({ 105 | grunt_android_emulator: { 106 | emulators: [{ 107 | id: 'emulator-1', 108 | start: { 109 | '-port': '5556', 110 | //'-no-window': '', 111 | '-no-audio': '', 112 | // '-force-32bit': '' 113 | //'-no-boot-anim': '', 114 | //'-no-skin': '', 115 | //'-memory': '1024' 116 | //'-avd': 'testAVD' 117 | } 118 | }], 119 | } 120 | }); 121 | ``` 122 | ### Options 123 | 124 | #### -port 125 | Type: `String` 126 | Description: `TCP port that will be used for the console` 127 | Usage: `[optional]` 128 | Default: `5554` 129 | 130 | #### -memory 131 | Type: `String` 132 | Description: `Physical RAM size in MB` 133 | Usage: `[optional]` 134 | 135 | #### -avd 136 | Type: `String` 137 | Description: `AVD name` 138 | Usage: `[optional]` 139 | Default: `The --name or -n value of the emulator's create options` 140 | 141 | #### -no-audio 142 | Type: `-` 143 | Description: `Disable audio support` 144 | Usage: `[optional]` 145 | 146 | #### -no-skin 147 | Type: `-` 148 | Description: `Don't use any emulator skin` 149 | Usage: `[optional]` 150 | 151 | #### -no-boot-anim 152 | Type: `-` 153 | Description: `Disable animation for faster boot` 154 | Usage: `[optional]` 155 | 156 | #### -no-window 157 | Type: `-` 158 | Description: `Disable graphical window display` 159 | Usage: `[optional]` 160 | 161 | Execute `emulator -help` in a terminal to see a list of the available options. _Note that the -ports option is not supported_ 162 | 163 | ## stop-android-emulator 164 | > This task stops an Android emulator. You can execute it using: 165 | 166 | `grunt stop-android-emulator:emulator-1` where `emulator-1` is a the emulator's id. 167 | 168 | ## unlock-android-emulator 169 | > This task unlocks an Android emulator. You can execute it by using: 170 | 171 | `grunt unlock-android-emulator:emulator-1` where `emulator-1` is a the emulator's id. 172 | 173 | The plugin uses the `-port` option of the `start` options to find the emulator to stop or 5554 in case this option is not defined. 174 | 175 | ## install-apk task 176 | > This task installs an APK in an Android emulator. You can execute it using: 177 | 178 | `grunt install-apk:emulator-1:apk-1` where `emulator-1` is a the emulator's id and apk-1 is the APK's id as shown in the below configuration. 179 | 180 | ```js 181 | grunt.initConfig({ 182 | grunt_android_emulator: { 183 | emulators: [{ 184 | id: 'emulator-1', 185 | create: { 186 | '--name': 'testAVD', 187 | //'--sdcard': '10M', 188 | //'--snapshot': '', 189 | //'--path': 'avd', 190 | '--force': '', 191 | //'--skin': '', 192 | '--target': 'android-18', 193 | '--abi': 'armeabi-v7a' 194 | } 195 | }], 196 | apks: [{ 197 | id: "apk-1", 198 | path: "./apk/test.apk", 199 | activities: [{ 200 | id: "activity-1", 201 | packageName: "org.jboss.aerogear", 202 | name: "AeroGearMain" 203 | }] 204 | }] 205 | } 206 | }); 207 | ``` 208 | ### Options 209 | 210 | #### path 211 | Type: `String` 212 | Description: `Relative or absolute path of the APK` 213 | Usage: `[required]` 214 | 215 | ## start-activity task 216 | > This task starts an activity. You can execute it using: 217 | 218 | `grunt start-activity:emulator-1:apk-1:activity-1` where `emulator-1` is a the emulator's id, apk-1 is the APK's id and activity-1 is the activity's id as shown in the below configuration. 219 | 220 | ```js 221 | grunt.initConfig({ 222 | grunt_android_emulator: { 223 | emulators: [{ 224 | id: 'emulator-1', 225 | create: { 226 | '--name': 'testAVD', 227 | //'--sdcard': '10M', 228 | //'--snapshot': '', 229 | //'--path': 'avd', 230 | '--force': '', 231 | //'--skin': '', 232 | '--target': 'android-18', 233 | '--abi': 'armeabi-v7a' 234 | } 235 | }], 236 | apks: [{ 237 | id: "apk-1", 238 | path: "./apks/test.apk", 239 | activities: [{ 240 | id: "activity-1", 241 | packageName: "org.jboss.aerogear", 242 | name: "AeroGearMain" 243 | }] 244 | }] 245 | } 246 | }); 247 | ``` 248 | ### Options 249 | 250 | #### activities[x].id 251 | Type: `String` 252 | Description: `Identifier of the activity` 253 | Usage: `[required]` 254 | 255 | #### activities[x].packageName 256 | Type: `String` 257 | Description: `Package name` 258 | Usage: `[required]` 259 | 260 | #### activities[x].name 261 | Type: `String` 262 | Description: `Activity name` 263 | Usage: `[required]` 264 | 265 | ## Example 266 | > The [example](https://github.com/tolis-e/grunt-android-emulator/tree/master/example) folder contains a sample example which depicts how to use this plugin. 267 | 268 | ## Release History 269 | 270 | ### 0.1.6 271 | *Released 12 March 2017* 272 | 273 | * Fix bug during starting activities 274 | 275 | ### 0.1.5 276 | *Released 16 January 2016* 277 | 278 | * Update shelljs dependency NPM version 279 | 280 | ### 0.1.2 281 | *Released 08 September 2014* 282 | 283 | * Remove validation from create options --device option support 284 | 285 | ### 0.1.1 286 | *Released 13 December 2013* 287 | 288 | * Add APK installation and start activity support 289 | 290 | ### 0.1.0 291 | *Released 20 October 2013* 292 | 293 | * Initial release 294 | -------------------------------------------------------------------------------- /example/Gruntfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JBoss, Home of Professional Open Source 3 | * Copyright Red Hat, Inc., and individual contributors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | module.exports = function(grunt) { 18 | 'use strict'; 19 | 20 | grunt.initConfig({ 21 | pkg: grunt.file.readJSON('package.json'), 22 | grunt_android_emulator: { 23 | emulators: [{ 24 | id: 'emulator-1', 25 | create: { 26 | '--name': 'device-1', 27 | //'--sdcard': '10M', 28 | //'--snapshot': '', 29 | //'--path': 'avd', 30 | '--force': '', 31 | //'--skin': '', 32 | '--target': 'android-21', 33 | '--abi': 'armeabi-v7a', 34 | '--device': "'Nexus 7 2013'" 35 | }, 36 | start: { 37 | '-port': '5556', 38 | '-no-window': '', 39 | '-no-audio': '', 40 | '-no-skin': '' 41 | } 42 | },{ 43 | id: 'emulator-2', 44 | create: { 45 | '--name': 'device-2', 46 | //'--sdcard': '10M', 47 | //'--snapshot': '', 48 | //'--path': 'avd', 49 | '--force': '', 50 | //'--skin': '', 51 | '--target': 'android-21', 52 | '--abi': 'armeabi-v7a' 53 | }, 54 | start: { 55 | '-port': '5558', 56 | '-no-window': '', 57 | '-no-audio': '', 58 | '-no-skin': '' 59 | } 60 | }], 61 | apks: [{ 62 | id: "apk-1", 63 | path: "./apk/AeroGear.apk", 64 | activities: [{ 65 | id: "main", 66 | packageName: "org.jboss.aerogear", 67 | name: "AeroGear" 68 | }] 69 | },{ 70 | id: "apk-2", 71 | path: "./apk/AeroGearCordova.apk", 72 | activities:[{ 73 | id: "activity-1", 74 | packageName: "", 75 | name: "" 76 | },{ 77 | id: "activity-2", 78 | packageName: "org.jboss.aerogear.cordova", 79 | name: "AeroGearCordova" 80 | }] 81 | }] 82 | }, 83 | jshint: { 84 | all: { 85 | src: [ "Gruntfile.js" ], 86 | options: { 87 | jshintrc: "../.jshintrc" 88 | } 89 | } 90 | } 91 | }); 92 | 93 | grunt.loadNpmTasks('grunt-android-emulator'); 94 | grunt.loadNpmTasks('grunt-contrib-jshint'); 95 | grunt.registerTask('default', ['jshint', 96 | 'create-android-emulator:emulator-1', 97 | 'create-android-emulator:emulator-2', 98 | 'start-android-emulator:emulator-1', 99 | 'start-android-emulator:emulator-2', 100 | 'unlock-android-emulator:emulator-1', 101 | 'unlock-android-emulator:emulator-2', 102 | 'install-apk:emulator-1:apk-1', 103 | 'install-apk:emulator-2:apk-2', 104 | 'start-activity:emulator-1:apk-1:main', 105 | 'start-activity:emulator-2:apk-2:activity-2', 106 | 'stop-android-emulator:emulator-1', 107 | 'stop-android-emulator:emulator-2']); 108 | }; 109 | -------------------------------------------------------------------------------- /example/apk/AeroGear.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolis-e/grunt-android-emulator/fbbcbeb019345bca1d1bb063c95d4d497bf33c45/example/apk/AeroGear.apk -------------------------------------------------------------------------------- /example/apk/AeroGearCordova.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolis-e/grunt-android-emulator/fbbcbeb019345bca1d1bb063c95d4d497bf33c45/example/apk/AeroGearCordova.apk -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-android-emulator-example", 3 | "description": "Create, Start and Stop Android emulators, Install APK, Start activities Example", 4 | "title": "Grunt Android Emulator Example", 5 | "version": "0.1.1", 6 | "homepage": "https://github.com/tolis-e/grunt-android-emulator", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/tolis-e/grunt-android-emulator.git" 10 | }, 11 | "author": { 12 | "name": "Tolis Emmanouilidis", 13 | "url": "http://github.com/tolis-e/grunt-android-emulator" 14 | }, 15 | "maintainers": { 16 | "name": "Tolis Emmanouilidis", 17 | "email": "tolisemm@gmail.com" 18 | }, 19 | "licenses": [ 20 | { 21 | "type": "Apache-2.0", 22 | "url": "http://www.apache.org/licenses/LICENSE-2.0" 23 | } 24 | ], 25 | "keywords": [ 26 | "android", 27 | "android emulator", 28 | "emulator", 29 | "mobile" 30 | ], 31 | "devDependencies": { 32 | "grunt": "~0.4.5", 33 | "grunt-contrib-jshint": "~0.10.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-android-emulator", 3 | "description": "Create, Start, Unlock, Stop Android emulators, Install APK, Start Activities", 4 | "title": "Grunt Android Emulator", 5 | "version": "0.1.7", 6 | "homepage": "https://github.com/tolis-e/grunt-android-emulator", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/tolis-e/grunt-android-emulator.git" 10 | }, 11 | "author": { 12 | "name": "Tolis Emmanouilidis", 13 | "url": "http://github.com/tolis-e/grunt-android-emulator" 14 | }, 15 | "maintainers": { 16 | "name": "Tolis Emmanouilidis", 17 | "email": "tolisemm@gmail.com" 18 | }, 19 | "licenses": [ 20 | { 21 | "type": "Apache-2.0", 22 | "url": "http://www.apache.org/licenses/LICENSE-2.0" 23 | } 24 | ], 25 | "keywords": [ 26 | "android", 27 | "android emulator", 28 | "emulator", 29 | "mobile" 30 | ], 31 | "dependencies": { 32 | "shelljs": "~0.7.6" 33 | }, 34 | "devDependencies": { 35 | "grunt": "~0.4.5", 36 | "grunt-contrib-jshint": "~0.10.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/core/emulator-module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JBoss, Home of Professional Open Source 3 | * Copyright Red Hat, Inc., and individual contributors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | var grunt = require('grunt'), 18 | shell = require('shelljs'), 19 | StringModule = require('../lib/string-module').StringModule, 20 | Logger = require('../lib/log-module').Logger, 21 | path = require('path'); 22 | 23 | module.exports.AndroidEmulatorModule = 24 | { 25 | validateCreateOptions: function (options) 26 | { 27 | if (options) 28 | { 29 | var name = options['--name'] || options['-n'], 30 | target = options['--target'] || options['-t'], 31 | avdPath = options['--path'] || options['-p'], 32 | sdcard = options['--sdcard'] || options['-c'], 33 | errors; 34 | // name should not be empty 35 | if (!name || StringModule.trim(name) === '') 36 | { 37 | errors = [errors, ' name: \'', name, '\' '].join(''); 38 | } 39 | // target should not be empty 40 | if (!target || StringModule.trim(target) === '') 41 | { 42 | errors = [errors, ' target: \'', target, '\' '].join(''); 43 | } 44 | // validate path 45 | if (avdPath && StringModule.trim(avdPath) !== '') 46 | { 47 | var absoluteAvdPath = path.resolve(avdPath); 48 | if (!grunt.file.isDir(absoluteAvdPath)) 49 | { 50 | errors = [errors, ' path: \'', absoluteAvdPath, '\' '].join(''); 51 | } 52 | } 53 | if (sdcard && StringModule.trim(sdcard) !== '') 54 | { 55 | var sdcardPath = path.resolve(sdcard); 56 | if (!grunt.file.isFile(sdcardPath) && !(/^[0-9]+[K|M]{1}$/.test(sdcard))) 57 | { 58 | errors = [errors, ' sdcard: \'', sdcard, '\' '].join(''); 59 | } 60 | } 61 | 62 | return errors; 63 | } 64 | return 'create: configuration is missing in Gruntfile'; 65 | }, 66 | create: function (options, callbacks) 67 | { 68 | if (options) 69 | { 70 | var createEmulatorCmd = 'echo no | android create avd'; 71 | 72 | for (var id in options) 73 | { 74 | var value = StringModule.trim(options[id]); 75 | if (id) 76 | { 77 | if (id === '--path' || id === '-p' || ((id === '--sdcard' || id === '-c') && !(/^[0-9]+[K|M]{1}$/.test(value)))) 78 | { 79 | value = path.resolve(value); 80 | } 81 | createEmulatorCmd = [createEmulatorCmd, ' ', id, StringModule.trim(value) !== '' ? ' ' : '', value].join(''); 82 | } 83 | } 84 | 85 | Logger.info(['AndroidEmulatorModule: create: ', createEmulatorCmd].join('')); 86 | 87 | var createEmulator = shell.exec(createEmulatorCmd, function (code, output) { 88 | if (code === 0 && !/Error:/.test(output) && ( /Do you wish to create a custom hardware profile/.test(output) || /Created AVD/.test(output) )) 89 | { 90 | Logger.info('AndroidEmulatorModule: create: emulator was successfully created'); 91 | callbacks.success(); 92 | } 93 | else 94 | { 95 | Logger.error('AndroidEmulatorModule: create: error occured'); 96 | callbacks.error(); 97 | } 98 | }); 99 | } 100 | }, 101 | start: function (options, callbacks) 102 | { 103 | if (options) 104 | { 105 | var startEmulatorCmd = 'emulator'; 106 | 107 | for (var id in options) 108 | { 109 | if (id) 110 | { 111 | var value = StringModule.trim(options[id]); 112 | startEmulatorCmd = [startEmulatorCmd, ' ', id, value !== '' ? ' ' : '', value].join(''); 113 | } 114 | } 115 | 116 | Logger.info(['AndroidEmulatorModule: start: ', startEmulatorCmd].join('')); 117 | 118 | var startEmulator = shell.exec(startEmulatorCmd, function (statusCode, output) { 119 | if (statusCode !== 0) 120 | { 121 | callbacks.error(); 122 | } 123 | }); 124 | 125 | Logger.info('AndroidEmulatorModule: start: wait to boot'); 126 | var waitEmulatorToBootCmd = [__dirname, '/', 'wait_emulator_to_boot.sh', ' ', options['-port']].join(''), 127 | waitEmulatorToBootExec = shell.exec(waitEmulatorToBootCmd, function(code, output) { 128 | if (code === 0 && /successfully booted/.test(output)) 129 | { 130 | Logger.info('AndroidEmulatorModule: start: emulator booted successfully'); 131 | callbacks.success(); 132 | } 133 | else 134 | { 135 | Logger.error('AndroidEmulatorModule: start: emulator boot failed'); 136 | callbacks.error(); 137 | } 138 | }); 139 | } 140 | }, 141 | stop: function (options, callbacks) 142 | { 143 | if (options) 144 | { 145 | var stopEmulatorCmd = ['adb -s emulator-', options['-port'], ' emu kill'].join(''); 146 | 147 | Logger.info(['AndroidEmulatorModule: stop: ', stopEmulatorCmd].join('')); 148 | 149 | var stopEmulator = shell.exec(stopEmulatorCmd, function (statusCode, output) { 150 | if (statusCode === 0 && !/error/.test(output)) 151 | { 152 | Logger.info('AndroidEmulatorModule: stop: emulator successfully stopped'); 153 | callbacks.success(); 154 | } 155 | else 156 | { 157 | Logger.error('AndroidEmulatorModule: stop: emulator stop failed'); 158 | callbacks.error(); 159 | } 160 | }); 161 | } 162 | }, 163 | unlock: function (options, callbacks) 164 | { 165 | if (options) 166 | { 167 | var unlockEmulatorCmd = ['adb -s emulator-', 168 | options['-port'], 169 | ' shell input keyevent 82 && adb -s emulator-', 170 | options['-port'], 171 | ' shell input keyevent 4' 172 | ].join(''); 173 | 174 | Logger.info(['AndroidEmulatorModule: unlock: ', unlockEmulatorCmd].join('')); 175 | 176 | var unlockEmulator = shell.exec(unlockEmulatorCmd, function (statusCode, output) { 177 | if (statusCode === 0) 178 | { 179 | Logger.info('AndroidEmulatorModule: unlock: emulator successfully unlocked'); 180 | callbacks.success(); 181 | } 182 | else 183 | { 184 | Logger.error('AndroidEmulatorModule: stop: emulator unlock failed'); 185 | callbacks.error(); 186 | } 187 | }); 188 | } 189 | }, 190 | installAPK: function (options, callbacks) 191 | { 192 | if (options) 193 | { 194 | var installApkCmd = ['adb -s emulator-', 195 | options['-port'], 196 | ' install -r ', 197 | options['apkPath'] 198 | ].join(''); 199 | 200 | Logger.info(['AndroidEmulatorModule: installAPK: ', installApkCmd].join('')); 201 | 202 | var installAPK = shell.exec(installApkCmd, function (statusCode, output) { 203 | if (statusCode === 0) 204 | { 205 | Logger.info('AndroidEmulatorModule: installAPK: APK successfully installed'); 206 | callbacks.success(); 207 | } 208 | else 209 | { 210 | Logger.error('AndroidEmulatorModule: installAPK: APK installation failed'); 211 | callbacks.error(); 212 | } 213 | }); 214 | } 215 | }, 216 | startActivity: function (options, callbacks) 217 | { 218 | if (options) 219 | { 220 | var startActivityCmd = ['adb -s emulator-', 221 | options['-port'], 222 | ' shell am start -n ', 223 | options['packageName'], 224 | '/', 225 | options['packageName'], 226 | '.', 227 | options['activity'] 228 | ].join(''); 229 | 230 | Logger.info(['AndroidEmulatorModule: startActivity: ', startActivityCmd].join('')); 231 | 232 | var startActivity = shell.exec(startActivityCmd, function (statusCode, output) { 233 | if (statusCode === 0) 234 | { 235 | Logger.info('AndroidEmulatorModule: startActivity: activity successfully started'); 236 | callbacks.success(); 237 | } 238 | else 239 | { 240 | Logger.error('AndroidEmulatorModule: startActivity: activity failure'); 241 | callbacks.error(); 242 | } 243 | }); 244 | } 245 | } 246 | }; 247 | -------------------------------------------------------------------------------- /src/core/wait_emulator_to_boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | booted="" 4 | failures=0 5 | trials=0 6 | maxTrials=60 7 | until [[ "$booted" =~ "stopped" ]]; do 8 | let "trials += 1" 9 | echo "## trials: $trials" 10 | if [[ $trials -gt $maxTrials ]]; then 11 | echo "Could not boot emulator $1 after $maxTrials trials" 12 | exit 1 13 | fi 14 | echo "adb -s emulator-$1 shell getprop init.svc.bootanim" 15 | booted=`adb -s emulator-"$1" shell getprop init.svc.bootanim 2>&1` 16 | echo "$booted" 17 | if [[ "$booted" =~ "not found" ]]; then 18 | let "failures += 1" 19 | echo "## Not found: $failures" 20 | if [[ $failures -gt 20 ]]; then 21 | echo "Could not boot emulator $1" 22 | exit 1 23 | fi 24 | elif [[ "$booted" =~ "protocol fault" ]]; then 25 | echo "Restarting adb server" 26 | stop=`adb kill-server` 27 | sleep 2 28 | start=`adb start-server` 29 | echo "$start" 30 | fi 31 | sleep 6 32 | done 33 | echo "Android Emulator $1 successfully booted" 34 | -------------------------------------------------------------------------------- /src/lib/grunt-module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JBoss, Home of Professional Open Source 3 | * Copyright Red Hat, Inc., and individual contributors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | var grunt = require('grunt'), 18 | StringModule = require('./string-module').StringModule; 19 | 20 | module.exports.GruntModule = 21 | { 22 | getOption: function (id) 23 | { 24 | return id && StringModule.trim(id) !== '' ? grunt.config.get(id) : ''; 25 | }, 26 | setOption: function (id, val) 27 | { 28 | if (id && StringModule.trim(id) !== '' && val) 29 | { 30 | grunt.config.set(id, val); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/lib/log-module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JBoss, Home of Professional Open Source 3 | * Copyright Red Hat, Inc., and individual contributors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | var grunt = require('grunt'), 18 | Logger = { 19 | info: function (msg) { 20 | if (msg) 21 | { 22 | grunt.log.writeln(['[INFO]', ' ', msg].join('')); 23 | } 24 | return this; 25 | }, 26 | error: function (msg) { 27 | if (msg) 28 | { 29 | grunt.log.error(['[ERROR]', ' ', msg].join('')); 30 | } 31 | return this; 32 | } 33 | }; 34 | 35 | if (typeof module !== 'undefined') { 36 | module.exports.Logger = Logger; 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/string-module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JBoss, Home of Professional Open Source 3 | * Copyright Red Hat, Inc., and individual contributors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | module.exports.StringModule = 18 | { 19 | trim: function (val) 20 | { 21 | return val ? String(val).replace(/^\s+|\s+$/g,'') : val; 22 | }, 23 | endsWith: function (str, suffix) 24 | { 25 | return String(str).indexOf(suffix, str.length - suffix.length) !== -1; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /tasks/grunt-android-emulator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JBoss, Home of Professional Open Source 3 | * Copyright Red Hat, Inc., and individual contributors. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | module.exports = function (grunt) { 18 | 'use strict'; 19 | 20 | var GruntModule = require('../src/lib/grunt-module').GruntModule, 21 | StringModule = require('../src/lib/string-module').StringModule, 22 | Logger = require('../src/lib/log-module').Logger, 23 | AndroidEmulatorModule = require('../src/core/emulator-module').AndroidEmulatorModule, 24 | path = require('path'); 25 | 26 | // TODO: improve validation and avoid repeated code inside tasks 27 | 28 | grunt.registerTask('create-android-emulator', 'Create Android Emulator', function (id) { 29 | var done = this.async(), 30 | callbacks = { 31 | success: function () { 32 | done(true); 33 | }, 34 | error: function () { 35 | done(false); 36 | } 37 | }, 38 | options = {}, 39 | emulators = GruntModule.getOption('grunt_android_emulator.emulators'); 40 | 41 | if (!id || StringModule.trim(id) === '') 42 | { 43 | Logger.error(['task: create-android-emulator: invalid emulator id: ', id].join('')); 44 | callbacks.error(); 45 | return; 46 | } 47 | else if (!emulators || emulators.length < 1) 48 | { 49 | Logger.error('task: create-android-emulator: emulators configuration is missing in Gruntfile'); 50 | callbacks.error(); 51 | return; 52 | } 53 | else 54 | { 55 | for (var i=0, emulator; i