1 | /**
|
2 | * Detects Windows Phone devices.
|
3 | *
|
4 | * @module device
|
5 | *
|
6 | * @copyright
|
7 | * Copyright (c) 2014 by Appcelerator, Inc. All Rights Reserved.
|
8 | *
|
9 | * @license
|
10 | * Licensed under the terms of the Apache Public License.
|
11 | * Please see the LICENSE included with this distribution for details.
|
12 | */
|
13 |
|
14 | const
|
15 | appc = require('node-appc'),
|
16 | async = require('async'),
|
17 | fs = require('fs'),
|
18 | magik = require('./utilities').magik,
|
19 | path = require('path'),
|
20 | spawn = require('child_process').spawn,
|
21 | windowsphone = require('./windowsphone'),
|
22 | wptool = require('./wptool'),
|
23 | __ = appc.i18n(__dirname).__;
|
24 |
|
25 | var cache;
|
26 |
|
27 | exports.detect = detect;
|
28 | exports.connect = connect;
|
29 | exports.install = install;
|
30 |
|
31 | /**
|
32 | * Detects connected Windows Phone devices.
|
33 | *
|
34 | * @param {Object} [options] - An object containing various settings.
|
35 | * @param {Boolean} [options.bypassCache=false] - When true, re-detects all Windows Phone devices.
|
36 | * @param {Function} [callback(err, results)] - A function to call with the device information.
|
37 | *
|
38 | * @emits module:device#detected
|
39 | * @emits module:device#error
|
40 | *
|
41 | * @returns {EventEmitter}
|
42 | */
|
43 | function detect(options, callback) {
|
44 | return magik(options, callback, function (emitter, options, callback) {
|
45 | if (cache && !options.bypassCache) {
|
46 | emitter.emit('detected', cache);
|
47 | return callback(null, cache);
|
48 | }
|
49 |
|
50 | wptool.enumerate(options, function (err, results) {
|
51 | var result = {
|
52 | devices: [],
|
53 | issues: []
|
54 | };
|
55 |
|
56 | if (!err) {
|
57 | var tmp = {};
|
58 |
|
59 | Object.keys(results).forEach(function (wpsdk) {
|
60 | results[wpsdk].devices.forEach(function (dev) {
|
61 | if (!tmp[dev.udid]) {
|
62 | tmp[dev.udid] = result.devices.length+1;
|
63 | result.devices.push(dev);
|
64 | } else if (dev.wpsdk) {
|
65 | result.devices[tmp[dev.udid]-1] = dev;
|
66 | }
|
67 | });
|
68 | });
|
69 |
|
70 | cache = result;
|
71 | }
|
72 |
|
73 | emitter.emit('detected', result);
|
74 | callback(null, result);
|
75 | });
|
76 | });
|
77 | };
|
78 |
|
79 | /**
|
80 | * Tries to connect to the specified device and will error if there are no devices connected
|
81 | * or there are more than one connected devices.
|
82 | *
|
83 | * @param {String} udid - The UDID of the device to connect to or null if you want windowslib to pick one.
|
84 | * @param {Object} [options] - An object containing various settings.
|
85 | * @param {Boolean} [options.bypassCache=false] - When true, re-detects the environment configuration.
|
86 | * @param {Function} [callback(err)] - A function to call when the simulator has launched.
|
87 | *
|
88 | * @returns {EventEmitter}
|
89 | */
|
90 | function connect(udid, options, callback) {
|
91 | return wptool.connect(udid, options, callback);
|
92 | }
|
93 |
|
94 | /**
|
95 | * Installs the specified app to an Windows Phone device. If the device is not
|
96 | * connected or more than one is connected, then an error is returned. After the
|
97 | * app is installed, it will be automatically launched if possible and the options.skipLaunch flag isn't false
|
98 | *
|
99 | * @param {String} udid - The UDID of the device to install the app to or null if you want windowslib to pick one.
|
100 | * @param {String} appPath - The path to the Windows Phone app to install.
|
101 | * @param {Object} [options] - An object containing various settings.
|
102 | * @param {Boolean} [options.bypassCache=false] - When true, re-detects the environment configuration.
|
103 | * @param {Boolean} [options.skipLaunch=false] - When true, only installs the app, does not attempt to launch it.
|
104 | * @param {Number} [options.timeout] - Number of milliseconds to wait before timing out. Minimum of 1000 milliseconds.
|
105 | * @param {String} [options.wpsdk] - The Windows Phone SDK to use for the deploy tool. If not specified, it will autodetect.
|
106 | * @param {Function} [callback(err)] - A function to call when the app is installed. To know when the app gets launched, hook an event listener for 'launched' event
|
107 | *
|
108 | * @emits module:device#error
|
109 | * @emits module:device#installed
|
110 | * @emits module:device#launched
|
111 | *
|
112 | * @returns {EventEmitter}
|
113 | */
|
114 | function install(udid, appPath, options, callback) {
|
115 | return magik(options, callback, function (emitter, options, callback) {
|
116 | if (typeof appPath !== 'string' || !appPath) {
|
117 | var ex = new Error(__('Missing required "%s" argument', 'appPath'));
|
118 | emitter.emit('error', ex);
|
119 | return callback(ex);
|
120 | }
|
121 |
|
122 | if (!fs.existsSync(appPath)) {
|
123 | var ex = new Error(__('App path does not exist: ' + appPath));
|
124 | emitter.emit('error', ex);
|
125 | return callback(ex);
|
126 | }
|
127 |
|
128 | // detect devices, use cached listing!
|
129 | detect(options, function (err, devInfo) {
|
130 | if (err) {
|
131 | emitter.emit('error', err);
|
132 | return callback(err);
|
133 | }
|
134 |
|
135 | var devHandle;
|
136 | var devices = devInfo.devices;
|
137 |
|
138 | if (udid) {
|
139 | // validate the udid
|
140 | devices.some(function (dev) {
|
141 | if (dev.udid === udid) {
|
142 | devHandle = appc.util.mix({}, dev);
|
143 | return true;
|
144 | }
|
145 | return false;
|
146 | });
|
147 |
|
148 | if (!devHandle) {
|
149 | err = new Error(__('Unable to find a Windows Phone device with the UDID "%s"', udid));
|
150 | }
|
151 | } else if (devices.length) {
|
152 | devHandle = appc.util.mix({}, devices[0]);
|
153 | } else {
|
154 | // user experience!
|
155 | if (options.wpsdk) {
|
156 | err = new Error(__('Unable to find a Windows Phone %s device.', options.wpsdk));
|
157 | } else {
|
158 | err = new Error(__('Unable to find a Windows Phone device.'));
|
159 | }
|
160 | }
|
161 |
|
162 | if (err) {
|
163 | emitter.emit('error', err);
|
164 | return callback(err);
|
165 | }
|
166 |
|
167 | // connect to the device to see if it's connected...
|
168 | // this will add a second or two to the build time, but at least if there was
|
169 | // an error, we'll get a decent message
|
170 | wptool.connect(devHandle.udid, options)
|
171 | .on('error', function (err) {
|
172 | emitter.emit('error', err);
|
173 | return callback(err);
|
174 | })
|
175 | .on('connected', function (dev) {
|
176 | devHandle.ip = dev.ip; // copy the IP address we got from connecting
|
177 | devHandle.running = dev.running || true; // copy running status we got fromc onnecting
|
178 | // device is good to go, install the app!
|
179 | var timeout = options.timeout !== void 0 && Math.max(~~options.timeout, 1000), // minimum of 1 second
|
180 | mixedOptions = appc.util.mix({timeout: timeout}, options);
|
181 | wptool.install(devHandle, appPath, mixedOptions)
|
182 | .on('error', function (err) {
|
183 | emitter.emit('error', err);
|
184 | callback(err);
|
185 | }).on('installed', function () {
|
186 | emitter.emit('installed', devHandle);
|
187 | // If we're not going to launch, this is the end of the lifecycle here.
|
188 | if (mixedOptions.skipLaunch) {
|
189 | callback(null, devHandle);
|
190 | }
|
191 | }).on('launched', function () {
|
192 | emitter.emit('launched', devHandle);
|
193 | // Don't do callback until we launch if we're supposed to be launching
|
194 | if (!mixedOptions.skipLaunch) {
|
195 | callback(null, devHandle);
|
196 | }
|
197 | }).on('timeout', function (err) {
|
198 | err || (err = new Error(__('Timed out after %d milliseconds waiting to launch the device.', timeout)));
|
199 | emitter.emit('timeout', err);
|
200 | callback(err);
|
201 | });
|
202 | });
|
203 | });
|
204 | });
|
205 | }
|