|
| 1 | +import * as path from "path"; |
| 2 | +import * as fiberBootstrap from "../common/fiber-bootstrap"; |
| 3 | +import { createTable } from "../common/helpers"; |
| 4 | +import Future = require("fibers/future"); |
| 5 | + |
| 6 | +export class EmulatorPlatformService implements IEmulatorPlatformService { |
| 7 | + |
| 8 | + constructor( |
| 9 | + private $mobileHelper: Mobile.IMobileHelper, |
| 10 | + private $childProcess: IChildProcess, |
| 11 | + private $devicesService: Mobile.IDevicesService, |
| 12 | + private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, |
| 13 | + private $dispatcher: IFutureDispatcher, |
| 14 | + private $options: IOptions, |
| 15 | + private $logger: ILogger) {} |
| 16 | + |
| 17 | + public startEmulator(info: IEmulatorInfo): IFuture<void> { |
| 18 | + if (!info.isRunning) { |
| 19 | + |
| 20 | + if (this.$mobileHelper.isAndroidPlatform(info.platform)) { |
| 21 | + this.$options.avd = this.$options.device; |
| 22 | + this.$options.device = null; |
| 23 | + let platformsData: IPlatformsData = $injector.resolve("platformsData"); |
| 24 | + let platformData = platformsData.getPlatformData(info.platform); |
| 25 | + let emulatorServices = platformData.emulatorServices; |
| 26 | + emulatorServices.checkAvailability(); |
| 27 | + emulatorServices.checkDependencies().wait(); |
| 28 | + emulatorServices.startEmulator().wait(); |
| 29 | + this.$options.avd = null; |
| 30 | + return Future.fromResult(); |
| 31 | + } |
| 32 | + |
| 33 | + if (this.$mobileHelper.isiOSPlatform(info.platform)) { |
| 34 | + this.stopEmulator(info.platform).wait(); |
| 35 | + let future = new Future<void>(); |
| 36 | + this.$childProcess.exec(`open -a Simulator --args -CurrentDeviceUDID ${info.id}`).wait(); |
| 37 | + let timeoutFunc = () => { |
| 38 | + fiberBootstrap.run(() => { |
| 39 | + info = this.getEmulatorInfo("ios", info.id).wait(); |
| 40 | + if (info.isRunning) { |
| 41 | + this.$devicesService.initialize({ platform: info.platform, deviceId: info.id }).wait(); |
| 42 | + let device = this.$devicesService.getDeviceByIdentifier(info.id); |
| 43 | + device.applicationManager.checkForApplicationUpdates().wait(); |
| 44 | + future.return(); |
| 45 | + return; |
| 46 | + } |
| 47 | + setTimeout(timeoutFunc, 2000); |
| 48 | + }); |
| 49 | + }; |
| 50 | + timeoutFunc(); |
| 51 | + return future; |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + return Future.fromResult(); |
| 56 | + } |
| 57 | + |
| 58 | + private stopEmulator(platform: string): IFuture<void> { |
| 59 | + if (this.$mobileHelper.isiOSPlatform(platform)) { |
| 60 | + return this.$childProcess.exec("pkill -9 -f Simulator"); |
| 61 | + } |
| 62 | + return Future.fromResult(); |
| 63 | + } |
| 64 | + |
| 65 | + public getEmulatorInfo(platform: string, idOrName: string): IFuture<IEmulatorInfo> { |
| 66 | + return (() => { |
| 67 | + |
| 68 | + if (this.$mobileHelper.isAndroidPlatform(platform)) { |
| 69 | + let androidEmulators = this.getAndroidEmulators().wait(); |
| 70 | + let found = androidEmulators.filter((info:IEmulatorInfo) => info.id === idOrName); |
| 71 | + if (found.length > 0) { |
| 72 | + return found[0]; |
| 73 | + } |
| 74 | + this.$devicesService.initialize({platform: platform, deviceId: null, skipInferPlatform: true}).wait(); |
| 75 | + let info:IEmulatorInfo = null; |
| 76 | + let action = (device:Mobile.IDevice) => { |
| 77 | + return (() => { |
| 78 | + if (device.deviceInfo.identifier === idOrName) { |
| 79 | + info = { |
| 80 | + id: device.deviceInfo.identifier, |
| 81 | + name: device.deviceInfo.displayName, |
| 82 | + version: device.deviceInfo.version, |
| 83 | + platform: "Android", |
| 84 | + type: "emulator", |
| 85 | + isRunning: true |
| 86 | + }; |
| 87 | + } |
| 88 | + }).future<void>()(); |
| 89 | + }; |
| 90 | + this.$devicesService.execute(action, undefined, {allowNoDevices: true}).wait(); |
| 91 | + return info; |
| 92 | + } |
| 93 | + |
| 94 | + if (this.$mobileHelper.isiOSPlatform(platform)) { |
| 95 | + let emulators = this.getiOSEmulators().wait(); |
| 96 | + let sdk: string = null; |
| 97 | + let versionStart = idOrName.indexOf("("); |
| 98 | + if (versionStart > 0) { |
| 99 | + sdk = idOrName.substring(versionStart+1, idOrName.indexOf(")", versionStart)).trim(); |
| 100 | + idOrName = idOrName.substring(0, versionStart-1).trim(); |
| 101 | + } |
| 102 | + let found = emulators.filter((info:IEmulatorInfo) => { |
| 103 | + let sdkMatch = sdk ? info.version === sdk : true; |
| 104 | + return sdkMatch && info.id === idOrName || info.name === idOrName; |
| 105 | + }); |
| 106 | + return found.length>0 ? found[0] : null; |
| 107 | + } |
| 108 | + |
| 109 | + return null; |
| 110 | + |
| 111 | + }).future<IEmulatorInfo>()(); |
| 112 | + } |
| 113 | + |
| 114 | + public listAvailableEmulators(platform: string): IFuture<void> { |
| 115 | + return (() => { |
| 116 | + let emulators: IEmulatorInfo[] = []; |
| 117 | + if (!platform || this.$mobileHelper.isiOSPlatform(platform)) { |
| 118 | + let iosEmulators = this.getiOSEmulators().wait(); |
| 119 | + if (iosEmulators) { |
| 120 | + emulators = emulators.concat(iosEmulators); |
| 121 | + } |
| 122 | + } |
| 123 | + if (!platform || this.$mobileHelper.isAndroidPlatform(platform)) { |
| 124 | + let androidEmulators = this.getAndroidEmulators().wait(); |
| 125 | + if (androidEmulators) { |
| 126 | + emulators = emulators.concat(androidEmulators); |
| 127 | + } |
| 128 | + } |
| 129 | + this.outputEmulators("\nAvailable emulators", emulators); |
| 130 | + this.$logger.out("\nConnected devices & emulators"); |
| 131 | + $injector.resolveCommand("device").execute(platform ? [platform] : []).wait(); |
| 132 | + }).future<void>()(); |
| 133 | + } |
| 134 | + |
| 135 | + public getiOSEmulators(): IFuture<IEmulatorInfo[]> { |
| 136 | + return (()=>{ |
| 137 | + let output = this.$childProcess.exec("xcrun simctl list --json").wait(); |
| 138 | + let list = JSON.parse(output); |
| 139 | + let emulators: IEmulatorInfo[] = []; |
| 140 | + for (let osName in list["devices"]) { |
| 141 | + if (osName.indexOf("iOS") === -1) { |
| 142 | + continue; |
| 143 | + } |
| 144 | + let os = list["devices"][osName]; |
| 145 | + let version = this.parseiOSVersion(osName); |
| 146 | + for (let device of os) { |
| 147 | + if (device["availability"] !== "(available)") { |
| 148 | + continue; |
| 149 | + } |
| 150 | + let emulatorInfo: IEmulatorInfo = { |
| 151 | + id: device["udid"], |
| 152 | + name: device["name"], |
| 153 | + isRunning: device["state"] === "Booted", |
| 154 | + type: "simulator", |
| 155 | + version: version, |
| 156 | + platform: "iOS" |
| 157 | + }; |
| 158 | + emulators.push(emulatorInfo); |
| 159 | + } |
| 160 | + } |
| 161 | + return emulators; |
| 162 | + }).future<IEmulatorInfo[]>()(); |
| 163 | + } |
| 164 | + |
| 165 | + public getAndroidEmulators(): IFuture<IEmulatorInfo[]> { |
| 166 | + return (() => { |
| 167 | + let androidPath = path.join(process.env.ANDROID_HOME, "tools", "android"); |
| 168 | + let text:string = this.$childProcess.exec(`${androidPath} list avd`).wait(); |
| 169 | + let notLoadedIndex = text.indexOf("The following"); |
| 170 | + if (notLoadedIndex > 0) { |
| 171 | + text = text.substring(0, notLoadedIndex); |
| 172 | + } |
| 173 | + let textBlocks = text.split("---------"); |
| 174 | + let emulators: IEmulatorInfo[] = []; |
| 175 | + for (let block of textBlocks) { |
| 176 | + let lines = block.split("\n"); |
| 177 | + let info:IEmulatorInfo = { name: "", version: "", id: "", platform: "Android", type: "Emulator" }; |
| 178 | + for (let line of lines) { |
| 179 | + if (line.indexOf("Target") >= 0) { |
| 180 | + info.version = line.substring(line.indexOf(":")+1).replace("Android", "").trim(); |
| 181 | + } |
| 182 | + if (line.indexOf("Name") >= 0) { |
| 183 | + info.id = line.substring(line.indexOf(":")+1).trim(); |
| 184 | + } |
| 185 | + if (line.indexOf("Device") >= 0) { |
| 186 | + info.name = line.substring(line.indexOf(":")+1).trim(); |
| 187 | + } |
| 188 | + info.isRunning = false; |
| 189 | + } |
| 190 | + emulators.push(info); |
| 191 | + } |
| 192 | + return emulators; |
| 193 | + }).future<IEmulatorInfo[]>()(); |
| 194 | + } |
| 195 | + |
| 196 | + private parseiOSVersion(osName: string): string { |
| 197 | + osName = osName.replace("com.apple.CoreSimulator.SimRuntime.iOS-", ""); |
| 198 | + osName = osName.replace(/-/g, "."); |
| 199 | + osName = osName.replace("iOS", ""); |
| 200 | + osName = osName.trim(); |
| 201 | + return osName; |
| 202 | + } |
| 203 | + |
| 204 | + private outputEmulators(title: string, emulators: IEmulatorInfo[]) { |
| 205 | + this.$logger.out(title); |
| 206 | + let table: any = createTable(["Device Name", "Platform", "Version", "Device Identifier"], []); |
| 207 | + for (let info of emulators) { |
| 208 | + table.push([info.name, info.platform, info.version, info.id]); |
| 209 | + } |
| 210 | + this.$logger.out(table.toString()); |
| 211 | + } |
| 212 | +} |
| 213 | +$injector.register("emulatorPlatformService", EmulatorPlatformService); |
0 commit comments