diff --git a/PublicAPI.md b/PublicAPI.md index 40cba83daf..216ffc7187 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -53,6 +53,9 @@ const tns = require("nativescript"); * [sysInfo](#sysinfo) * [getSupportedNodeVersionRange](#getsupportednodeversionrange) * [getSystemWarnings](#getsystemwarnings) +* [devicesService](#devicesService) + * [getAvailableEmulators](#getAvailableEmulators) + * [startEmulator](#startEmulator) ## Module projectService @@ -1198,6 +1201,45 @@ tns.sysInfo.getSystemWarnings() .catch(err => console.error(`Error while trying to get system warnings: ${err}`)); ``` +## devicesService +The `devicesService` module allows interaction with devices and emulators. You can get a list of the available emulators or start a specific emulator. + +### getAvailableEmulators +The `getAvailableEmulators` method returns object of all running and available emulators. The result is in the following format: +```JavaScript + { + android: { + devices: Mobile.IDeviceInfo[], + errors: string[] + }, + ios: { + devices: Mobile.IDeviceInfo[], + errors: string[] + } + } +``` + +This method accepts platform parameter. If provided only devices by specified platform will be returned. + +* Usage +```TypeScript +tns.devicesService.getAvailableEmulators() + .then(availableEmulatorsOutput => { + Object.keys(availableEmulatorsOutput) + .forEach(platform => { + availableEmulatorsOutput[platform].devices.forEach(device => console.log(device)); + }) + }) +``` + +### startEmulator +The `startEmulator` method starts the emulator specified by provided options. Returns an array of errors if something unexpected happens or null otherwise. +* Usage +```TypeScript +tns.devicesService.startEmulator({imageIdentifier: "my emulator imageIdentifier"}) + .then(errors => { }); +``` + ## How to add a new method to Public API CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification. For example the `$options` injected module contains information about all `--` options passed on the terminal. When the CLI is used as a library, the options are not populated. Before adding method to public API, make sure its implementation does not rely on `$options`. diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index f572694a5a..6c016f1dd0 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -140,8 +140,6 @@ $injector.requireCommand("update", "./commands/update"); $injector.require("iOSLogFilter", "./services/ios-log-filter"); $injector.require("projectChangesService", "./services/project-changes-service"); -$injector.require("emulatorPlatformService", "./services/emulator-platform-service"); - $injector.require("pbxprojDomXcode", "./node/pbxproj-dom-xcode"); $injector.require("xcode", "./node/xcode"); diff --git a/lib/commands/run.ts b/lib/commands/run.ts index f2d361d609..b10bd15662 100644 --- a/lib/commands/run.ts +++ b/lib/commands/run.ts @@ -94,10 +94,9 @@ export class RunAndroidCommand implements ICommand { private $injector: IInjector, private $platformService: IPlatformService, private $projectData: IProjectData, - private $options: IOptions) { - } + private $options: IOptions) { } - public async execute(args: string[]): Promise { + public execute(args: string[]): Promise { return this.runCommand.execute(args); } diff --git a/lib/common b/lib/common index 4fa4347f6c..8d326ab9de 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 4fa4347f6c2f65611ea82d20e2c1c3010eb7b45b +Subproject commit 8d326ab9deb53b9e1ff44c37a0d1b00cc00f3404 diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index b977308e76..034f3403b6 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -91,6 +91,10 @@ interface IDebugOptions { * If not provided, defaults to 10 seconds. */ timeout?: string; + /** + * The sdk version of the emulator. + */ + sdk?: string; } /** diff --git a/lib/definitions/emulator-platform-service.d.ts b/lib/definitions/emulator-platform-service.d.ts deleted file mode 100644 index e088a2efc8..0000000000 --- a/lib/definitions/emulator-platform-service.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -interface IEmulatorInfo extends IPlatform { - name: string; - version: string; - id: string; - type: string; - isRunning?: boolean; -} - -interface IEmulatorPlatformService { - listAvailableEmulators(platform: string): Promise; - getEmulatorInfo(platform: string, nameOfId: string): Promise; - getiOSEmulators(): Promise; - getAndroidEmulators(): IEmulatorInfo[]; - startEmulator(info: IEmulatorInfo, projectData: IProjectData): Promise; -} \ No newline at end of file diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index b76e7fa5c2..23ba577239 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -264,7 +264,6 @@ interface ITrackPlatformAction extends IPlatform { interface IPlatformData { frameworkPackageName: string; platformProjectService: IPlatformProjectService; - emulatorServices: Mobile.IEmulatorPlatformServices; projectRoot: string; normalizedPlatformName: string; appDestinationDirectoryPath: string; diff --git a/lib/helpers/livesync-command-helper.ts b/lib/helpers/livesync-command-helper.ts index e97073ad42..f9315331ee 100644 --- a/lib/helpers/livesync-command-helper.ts +++ b/lib/helpers/livesync-command-helper.ts @@ -27,7 +27,8 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { platform, emulator, skipDeviceDetectionInterval: true, - skipInferPlatform: !platform + skipInferPlatform: !platform, + sdk: this.$options.sdk }); const devices = this.$devicesService.getDeviceInstances() diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index b258745c83..70c08cb483 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -17,8 +17,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject private isAndroidStudioTemplate: boolean; - constructor(private $androidEmulatorServices: Mobile.IEmulatorPlatformServices, - private $androidToolsInfo: IAndroidToolsInfo, + constructor(private $androidToolsInfo: IAndroidToolsInfo, private $childProcess: IChildProcess, private $errors: IErrors, $fs: IFileSystem, @@ -72,7 +71,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject normalizedPlatformName: "Android", appDestinationDirectoryPath: path.join(...appDestinationDirectoryArr), platformProjectService: this, - emulatorServices: this.$androidEmulatorServices, projectRoot: projectRoot, deviceBuildOutputPath: path.join(...deviceBuildOutputArr), getValidBuildOutputData: (buildOptions: IBuildOutputOptions): IValidBuildOutputData => { diff --git a/lib/services/emulator-platform-service.ts b/lib/services/emulator-platform-service.ts deleted file mode 100644 index b2a9af9e83..0000000000 --- a/lib/services/emulator-platform-service.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { createTable, deferPromise } from "../common/helpers"; -import { DeviceTypes } from "../common/constants"; - -export class EmulatorPlatformService implements IEmulatorPlatformService { - - constructor( - private $mobileHelper: Mobile.IMobileHelper, - private $childProcess: IChildProcess, - private $devicesService: Mobile.IDevicesService, - private $options: IOptions, - private $logger: ILogger, - private $androidEmulatorServices: Mobile.IAndroidEmulatorServices) { } - - public async startEmulator(info: IEmulatorInfo, projectData: IProjectData): Promise { - if (!info.isRunning) { - if (this.$mobileHelper.isAndroidPlatform(info.platform)) { - this.$options.avd = this.$options.device; - this.$options.device = null; - const platformsData: IPlatformsData = $injector.resolve("platformsData"); - const platformData = platformsData.getPlatformData(info.platform, projectData); - const emulatorServices = platformData.emulatorServices; - emulatorServices.checkAvailability(); - await emulatorServices.checkDependencies(); - await emulatorServices.startEmulator(); - this.$options.avd = null; - return; - } - - if (this.$mobileHelper.isiOSPlatform(info.platform)) { - await this.stopEmulator(info.platform); - const deferred = deferPromise(); - await this.$childProcess.exec(`open -a Simulator --args -CurrentDeviceUDID ${info.id}`); - const timeoutFunc = async () => { - info = await this.getEmulatorInfo("ios", info.id); - if (info.isRunning) { - await this.$devicesService.initialize({ platform: info.platform, deviceId: info.id }); - const device = this.$devicesService.getDeviceByIdentifier(info.id); - await device.applicationManager.checkForApplicationUpdates(); - deferred.resolve(); - return; - } - - setTimeout(timeoutFunc, 2000); - }; - await timeoutFunc(); - return deferred.promise; - } - } - } - - private async stopEmulator(platform: string): Promise { - if (this.$mobileHelper.isiOSPlatform(platform)) { - await this.$childProcess.exec("pkill -9 -f Simulator"); - } - } - - public async getEmulatorInfo(platform: string, idOrName: string): Promise { - if (this.$mobileHelper.isAndroidPlatform(platform)) { - const androidEmulators = this.getAndroidEmulators(); - const found = androidEmulators.filter((info: IEmulatorInfo) => info.id === idOrName); - if (found.length > 0) { - return found[0]; - } - - await this.$devicesService.initialize({ platform: platform, deviceId: null, skipInferPlatform: true }); - let info: IEmulatorInfo = null; - const action = async (device: Mobile.IDevice) => { - if (device.deviceInfo.identifier === idOrName) { - info = { - id: device.deviceInfo.identifier, - name: device.deviceInfo.displayName, - version: device.deviceInfo.version, - platform: "Android", - type: DeviceTypes.Emulator, - isRunning: true - }; - } - }; - await this.$devicesService.execute(action, undefined, { allowNoDevices: true }); - return info; - } - - if (this.$mobileHelper.isiOSPlatform(platform)) { - const emulators = await this.getiOSEmulators(); - let sdk: string = null; - const versionStart = idOrName.indexOf("("); - if (versionStart > 0) { - sdk = idOrName.substring(versionStart + 1, idOrName.indexOf(")", versionStart)).trim(); - idOrName = idOrName.substring(0, versionStart - 1).trim(); - } - const found = emulators.filter((info: IEmulatorInfo) => { - const sdkMatch = sdk ? info.version === sdk : true; - return sdkMatch && info.id === idOrName || info.name === idOrName; - }); - return found.length > 0 ? found[0] : null; - } - - return null; - - } - - public async listAvailableEmulators(platform: string): Promise { - let emulators: IEmulatorInfo[] = []; - if (!platform || this.$mobileHelper.isiOSPlatform(platform)) { - const iosEmulators = await this.getiOSEmulators(); - if (iosEmulators) { - emulators = emulators.concat(iosEmulators); - } - } - - if (!platform || this.$mobileHelper.isAndroidPlatform(platform)) { - const androidEmulators = this.getAndroidEmulators(); - if (androidEmulators) { - emulators = emulators.concat(androidEmulators); - } - } - - this.outputEmulators("\nAvailable emulators", emulators); - } - - public async getiOSEmulators(): Promise { - const output = await this.$childProcess.exec("xcrun simctl list --json"); - const list = JSON.parse(output); - const emulators: IEmulatorInfo[] = []; - for (const osName in list["devices"]) { - if (osName.indexOf("iOS") === -1) { - continue; - } - const os = list["devices"][osName]; - const version = this.parseiOSVersion(osName); - for (const device of os) { - if (device["availability"] !== "(available)") { - continue; - } - const emulatorInfo: IEmulatorInfo = { - id: device["udid"], - name: device["name"], - isRunning: device["state"] === "Booted", - type: "simulator", - version: version, - platform: "iOS" - }; - emulators.push(emulatorInfo); - } - } - - return emulators; - } - - public getAndroidEmulators(): IEmulatorInfo[] { - const androidVirtualDevices: Mobile.IAvdInfo[] = this.$androidEmulatorServices.getAvds().map(avd => this.$androidEmulatorServices.getInfoFromAvd(avd)); - - const emulators: IEmulatorInfo[] = _.map(androidVirtualDevices, avd => { - return { name: avd.device, version: avd.target, id: avd.name, platform: "Android", type: "Emulator", isRunning: false }; - }); - - return emulators; - } - - private parseiOSVersion(osName: string): string { - osName = osName.replace("com.apple.CoreSimulator.SimRuntime.iOS-", ""); - osName = osName.replace(/-/g, "."); - osName = osName.replace("iOS", ""); - osName = osName.trim(); - return osName; - } - - private outputEmulators(title: string, emulators: IEmulatorInfo[]) { - this.$logger.out(title); - const table: any = createTable(["Device Name", "Platform", "Version", "Device Identifier"], []); - for (const info of emulators) { - table.push([info.name, info.platform, info.version, info.id]); - } - - this.$logger.out(table.toString()); - } -} - -$injector.register("emulatorPlatformService", EmulatorPlatformService); diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 2fbb2c4691..c5f48b7f1a 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -129,7 +129,10 @@ export class IOSDebugService extends DebugServiceBase implements IPlatformDebugS args: args, appId: debugData.applicationIdentifier, skipInstall: true, - device: debugData.deviceIdentifier + device: debugData.deviceIdentifier, + justlaunch: debugOptions.justlaunch, + timeout: debugOptions.timeout, + sdk: debugOptions.sdk }); const pid = getPidFromiOSSimulatorLogs(debugData.applicationIdentifier, launchResult); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 06e886cb30..fd05c066ed 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -39,7 +39,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $cocoapodsService: ICocoaPodsService, private $errors: IErrors, private $logger: ILogger, - private $iOSEmulatorServices: Mobile.IEmulatorPlatformServices, private $injector: IInjector, $projectDataService: IProjectDataService, private $prompter: IPrompter, @@ -76,7 +75,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ normalizedPlatformName: "iOS", appDestinationDirectoryPath: path.join(projectRoot, projectData.projectName), platformProjectService: this, - emulatorServices: this.$iOSEmulatorServices, projectRoot: projectRoot, deviceBuildOutputPath: path.join(projectRoot, constants.BUILD_DIR, "device"), emulatorBuildOutputPath: path.join(projectRoot, constants.BUILD_DIR, "emulator"), diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 54b8d2ae00..a29e8cff37 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -121,6 +121,11 @@ function createTestInjector(projectPath: string, projectName: string, xcode?: IX testInjector.register("httpClient", {}); testInjector.register("platformEnvironmentRequirements", {}); testInjector.register("plistParser", {}); + testInjector.register("androidEmulatorServices", {}); + testInjector.register("androidEmulatorDiscovery", { + on: () => ({}) + }); + testInjector.register("emulatorHelper", {}); return testInjector; } diff --git a/test/npm-support.ts b/test/npm-support.ts index 5b9bc9e5d0..7ba520aa96 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -83,7 +83,6 @@ function createTestInjector(): IInjector { testInjector.register("xmlValidator", XmlValidator); testInjector.register("config", StaticConfigLib.Configuration); testInjector.register("projectChangesService", ProjectChangesLib.ProjectChangesService); - testInjector.register("emulatorPlatformService", stubs.EmulatorPlatformService); testInjector.register("analyticsService", { track: async (): Promise => undefined }); diff --git a/test/platform-commands.ts b/test/platform-commands.ts index f6d8253c34..f460fc08d8 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -33,7 +33,6 @@ class PlatformData implements IPlatformData { // intentionally left blank } }; - emulatorServices: Mobile.IEmulatorPlatformServices = null; projectRoot = ""; deviceBuildOutputPath = ""; getValidBuildOutputData = (buildOptions: IBuildOutputOptions) => ({ packageNames: [""] }); @@ -143,7 +142,6 @@ function createTestInjector() { testInjector.register("preparePlatformJSService", {}); testInjector.register("childProcess", ChildProcessLib.ChildProcess); testInjector.register("projectChangesService", ProjectChangesLib.ProjectChangesService); - testInjector.register("emulatorPlatformService", stubs.EmulatorPlatformService); testInjector.register("analyticsService", { track: async () => async (): Promise => undefined }); diff --git a/test/platform-service.ts b/test/platform-service.ts index 569fc61f0f..015a66a3fb 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -90,7 +90,6 @@ function createTestInjector() { }); testInjector.register("childProcess", ChildProcessLib.ChildProcess); testInjector.register("projectChangesService", ProjectChangesLib.ProjectChangesService); - testInjector.register("emulatorPlatformService", stubs.EmulatorPlatformService); testInjector.register("analyticsService", { track: async (): Promise => undefined, trackEventActionInGoogleAnalytics: () => Promise.resolve() @@ -259,7 +258,6 @@ describe('Platform Service Tests', () => { return { frameworkPackageName: packageName, platformProjectService: new stubs.PlatformProjectServiceStub(), - emulatorServices: undefined, projectRoot: "", normalizedPlatformName: "", appDestinationDirectoryPath: "", diff --git a/test/stubs.ts b/test/stubs.ts index 72575b03ab..230cbc4ade 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -309,7 +309,6 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor frameworkPackageName: "", normalizedPlatformName: "", platformProjectService: this, - emulatorServices: undefined, projectRoot: "", deviceBuildOutputPath: "", getValidBuildOutputData: (buildOptions: IBuildOutputOptions) => ({ packageNames: [] }), @@ -416,7 +415,6 @@ export class PlatformsDataStub extends EventEmitter implements IPlatformsData { return { frameworkPackageName: "", platformProjectService: new PlatformProjectServiceStub(), - emulatorServices: undefined, projectRoot: "", normalizedPlatformName: "", appDestinationDirectoryPath: "", @@ -811,28 +809,6 @@ export class PlatformServiceStub extends EventEmitter implements IPlatformServic } } -export class EmulatorPlatformService implements IEmulatorPlatformService { - public listAvailableEmulators(platform: string): Promise { - return Promise.resolve(); - } - - public getEmulatorInfo(platform: string, nameOfId: string): Promise { - return Promise.resolve(null); - } - - public getiOSEmulators(): Promise { - return Promise.resolve(null); - } - - public getAndroidEmulators(): IEmulatorInfo[] { - return null; - } - - public startEmulator(info: IEmulatorInfo): Promise { - return Promise.resolve(); - } -} - export class AndroidResourcesMigrationServiceStub implements IAndroidResourcesMigrationService { canMigrate(platformString: string): boolean { return true; @@ -859,7 +835,6 @@ export class InjectorStub extends Yok implements IInjector { this.register("hooksService", HooksServiceStub); this.register('projectDataService', ProjectDataService); this.register('devicePlatformsConstants', DevicePlatformsConstants); - this.register("emulatorPlatformService", EmulatorPlatformService); this.register("androidResourcesMigrationService", AndroidResourcesMigrationServiceStub); this.register("platformService", PlatformServiceStub); this.register("commandsService", CommandsService);