Skip to content
This repository was archived by the owner on Feb 2, 2021. It is now read-only.

Expose emulator api #1101

Merged
merged 5 commits into from
Jul 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions appbuilder/device-emitter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EventEmitter } from "events";
import { DeviceDiscoveryEventNames, DEVICE_LOG_EVENT_NAME } from "../constants";
import { DeviceDiscoveryEventNames, EmulatorDiscoveryNames, DEVICE_LOG_EVENT_NAME } from "../constants";

export class DeviceEmitter extends EventEmitter {
constructor(private $deviceLogProvider: EventEmitter,
Expand All @@ -8,7 +8,6 @@ export class DeviceEmitter extends EventEmitter {
super();

this.initialize();

}

private _companionAppIdentifiers: IDictionary<IStringDictionary>;
Expand Down Expand Up @@ -36,6 +35,14 @@ export class DeviceEmitter extends EventEmitter {
this.$deviceLogProvider.on("data", (identifier: string, data: any) => {
this.emit(DEVICE_LOG_EVENT_NAME, identifier, data.toString());
});

this.$devicesService.on(EmulatorDiscoveryNames.EMULATOR_IMAGES_FOUND, (emulator: Mobile.IDeviceInfo) => {
this.emit(EmulatorDiscoveryNames.EMULATOR_IMAGES_FOUND, emulator);
});

this.$devicesService.on(EmulatorDiscoveryNames.EMULATOR_IMAGES_LOST, (emulator: Mobile.IDeviceInfo) => {
this.emit(EmulatorDiscoveryNames.EMULATOR_IMAGES_LOST, emulator);
});
}

private attachApplicationChangedHandlers(device: Mobile.IDevice): void {
Expand Down
7 changes: 6 additions & 1 deletion bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ $injector.require("messagesService", "./services/messages-service");

$injector.require("cancellation", "./services/cancellation");
$injector.require("hooksService", "./services/hooks-service");
$injector.require("emulatorImageService", "./services/emulator-image-service");

$injector.require("httpClient", "./http-client");
$injector.require("childProcess", "./child-process");
Expand Down Expand Up @@ -60,11 +59,16 @@ $injector.require("deviceDiscovery", "./mobile/mobile-core/device-discovery");
$injector.require("iOSDeviceDiscovery", "./mobile/mobile-core/ios-device-discovery");
$injector.require("iOSSimulatorDiscovery", "./mobile/mobile-core/ios-simulator-discovery");
$injector.require("androidDeviceDiscovery", "./mobile/mobile-core/android-device-discovery");
$injector.require("androidEmulatorDiscovery", "./mobile/mobile-core/android-emulator-discovery");
$injector.require("iOSDevice", "./mobile/ios/device/ios-device");
$injector.require("iOSDeviceProductNameMapper", "./mobile/ios/ios-device-product-name-mapper");
$injector.require("androidDevice", "./mobile/android/android-device");
$injector.require("adb", "./mobile/android/android-debug-bridge");
$injector.require("androidDebugBridgeResultHandler", "./mobile/android/android-debug-bridge-result-handler");
$injector.require("androidVirtualDeviceService", "./mobile/android/android-virtual-device-service");
$injector.require("androidIniFileParser", "./mobile/android/android-ini-file-parser");
$injector.require("androidGenymotionService", "./mobile/android/genymotion/genymotion-service");
$injector.require("virtualBoxService", "./mobile/android/genymotion/virtualbox-service");
$injector.require("logcatHelper", "./mobile/android/logcat-helper");
$injector.require("iOSSimResolver", "./mobile/ios/simulator/ios-sim-resolver");
$injector.require("iOSSimulatorLogProvider", "./mobile/ios/simulator/ios-simulator-log-provider");
Expand All @@ -89,6 +93,7 @@ $injector.require("opener", "./opener");
$injector.require("dynamicHelpService", "./services/dynamic-help-service");
$injector.require("microTemplateService", "./services/micro-templating-service");
$injector.require("mobileHelper", "./mobile/mobile-helper");
$injector.require("emulatorHelper", "./mobile/emulator-helper");
$injector.require("devicePlatformsConstants", "./mobile/device-platforms-constants");
$injector.require("helpService", "./services/help-service");
$injector.require("messageContractGenerator", "./services/message-contract-generator");
Expand Down
10 changes: 10 additions & 0 deletions child-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ export class ChildProcess extends EventEmitter implements IChildProcess {
});
}

public async trySpawnFromCloseEvent(command: string, args: string[], options?: any, spawnFromEventOptions?: ISpawnFromEventOptions): Promise<ISpawnResult> {
try {
const childProcess = await this.spawnFromEvent(command, args, "close", options, spawnFromEventOptions);
return childProcess;
} catch (err) {
this.$logger.trace(`Error from trySpawnFromCloseEvent method. More info: ${err}`);
return Promise.resolve({ stderr: err && err.message ? err.message : err, stdout: null, exitCode: -1 });
}
}

public async tryExecuteApplication(command: string, args: string[], event: string,
errorMessage: string, condition: (_childProcess: any) => boolean): Promise<any> {
const childProcess = await this.tryExecuteApplicationCore(command, args, event, errorMessage);
Expand Down
24 changes: 21 additions & 3 deletions commands/device/list-devices.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import { createTable } from "../../helpers";
import { createTable, formatListOfNames } from "../../helpers";

export class ListDevicesCommand implements ICommand {
constructor(private $devicesService: Mobile.IDevicesService,
private $errors: IErrors,
private $emulatorHelper: Mobile.IEmulatorHelper,
private $logger: ILogger,
private $stringParameter: ICommandParameter,
private $mobileHelper: Mobile.IMobileHelper,
private $emulatorImageService: Mobile.IEmulatorImageService,
private $options: ICommonOptions) { }

public allowedParameters = [this.$stringParameter];

public async execute(args: string[]): Promise<void> {
if (this.$options.availableDevices) {
await this.$emulatorImageService.listAvailableEmulators(this.$mobileHelper.validatePlatformName(args[0]));
const platform = this.$mobileHelper.normalizePlatformName(args[0]);
if (!platform && args[0]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we stop using this.$mobileHelper.validatePlatformName?

Copy link
Contributor Author

@Fatme Fatme Jul 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because when tns devices --available-devices command was executed, an error was thrown from validatePlatformName method. I fixed this behaviour.
Actually when the platform is not valid (for example when tns devices invalidplatform), normalizePlatformName method will return undefined and the check above

if (!platform && args[0]) {

will throw an error.

this.$errors.failWithoutHelp(`${args[0]} is not a valid device platform. The valid platforms are ${formatListOfNames(this.$mobileHelper.platformNames)}`);
}

const availableEmulatorsOutput = await this.$devicesService.getAvailableEmulators({ platform });
const emulators = this.$emulatorHelper.getEmulatorsFromAvailableEmulatorsOutput(availableEmulatorsOutput);
this.printEmulators("\nAvailable emulators", emulators);
}

this.$logger.out("\nConnected devices & emulators");
Expand Down Expand Up @@ -40,6 +48,16 @@ export class ListDevicesCommand implements ICommand {
this.$logger.out(table.toString());
}
}

private printEmulators(title: string, emulators: Mobile.IDeviceInfo[]) {
this.$logger.out(title);
const table: any = createTable(["Device Name", "Platform", "Version", "Device Identifier", "Image Identifier", "Error Help"], []);
for (const info of emulators) {
table.push([info.displayName, info.platform, info.version, info.identifier || "", info.imageIdentifier || "", info.errorHelp || ""]);
}

this.$logger.out(table.toString());
}
}

$injector.registerCommand(["device|*list", "devices|*list"], ListDevicesCommand);
Expand Down
39 changes: 39 additions & 0 deletions constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ export const ERROR_NO_VALID_SUBCOMMAND_FORMAT = "The input is not valid sub-comm

export const UNREACHABLE_STATUS = "Unreachable";
export const CONNECTED_STATUS = "Connected";
export const DISCONNECTED_STATUS = "Disconnected";

export const RUNNING_EMULATOR_STATUS = "Running";
export const NOT_RUNNING_EMULATOR_STATUS = "Not running";

export const APPLE_VENDOR_NAME = "Apple";

export class LiveSyncConstants {
static VERSION_2 = 2;
Expand All @@ -39,6 +45,11 @@ export class DeviceDiscoveryEventNames {
static DEVICE_LOST = "deviceLost";
}

export class EmulatorDiscoveryNames {
static EMULATOR_IMAGES_FOUND = "emulatorImagesFound";
static EMULATOR_IMAGES_LOST = "emulatorImagesLost";
}

export const DEVICE_LOG_EVENT_NAME = "deviceLogData";

export const TARGET_FRAMEWORK_IDENTIFIERS = {
Expand Down Expand Up @@ -103,3 +114,31 @@ export const enum CommandsDelimiters {

export const DEBUGGER_PORT_FOUND_EVENT_NAME = "DEBUGGER_PORT_FOUND";
export const ATTACH_REQUEST_EVENT_NAME = "ATTACH_REQUEST";

export class AndroidVirtualDevice {
static ANDROID_DIR_NAME = ".android";
static AVD_DIR_NAME = "avd";
static ENCODING_MASK = /^avd\.ini\.encoding=(.*)$/;
static INI_FILES_MASK = /^(.*)\.ini$/i;
static AVD_FILES_MASK = /^(.*)\.avd$/i;
static MIN_ANDROID_APILEVEL = 17;
static MIN_ANDROID_VERSION = "4.2";
/**
* The message that is printed from `avdmanager list avds`
*/
static AVAILABLE_AVDS_MESSAGE = "Available Android Virtual Devices:";
/**
* The delimiter between devices that is used from `avdmanager list avds`
*/
static AVD_LIST_DELIMITER = "---------";
static CONFIG_INI_FILE_NAME = "config.ini";
static INI_FILE_EXTENSION = ".ini";
static AVD_FILE_EXTENSION = ".avd";
static RUNNING_AVD_EMULATOR_REGEX = /^(emulator-\d+)\s+device$/;
static RUNNING_GENY_EMULATOR_REGEX = /^(.+?)\s+device$/;
static GENYMOTION_VENDOR_NAME = "Genymotion";
static AVD_VENDOR_NAME = "Avd";
static TIMEOUT_SECONDS = 120;

static UNABLE_TO_START_EMULATOR_MESSAGE = "Cannot run your app in the native emulator. Increase the timeout of the operation with the --timeout option or try to restart your adb server with 'adb kill-server' command. Alternatively, run the Android Virtual Device manager and increase the allocated RAM for the virtual device.";
}
3 changes: 2 additions & 1 deletion declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,9 +621,10 @@ interface IQueue<T> {

interface IChildProcess extends NodeJS.EventEmitter {
exec(command: string, options?: any, execOptions?: IExecOptions): Promise<any>;
execFile(command: string, args: string[]): Promise<any>;
execFile<T>(command: string, args: string[]): Promise<T>;
spawn(command: string, args?: string[], options?: any): any; // it returns child_process.ChildProcess you can safely cast to it
spawnFromEvent(command: string, args: string[], event: string, options?: any, spawnFromEventOptions?: ISpawnFromEventOptions): Promise<ISpawnResult>;
trySpawnFromCloseEvent(command: string, args: string[], options?: any, spawnFromEventOptions?: ISpawnFromEventOptions): Promise<ISpawnResult>;
tryExecuteApplication(command: string, args: string[], event: string, errorMessage: string, condition?: (childProcess: any) => boolean): Promise<any>;
/**
* This is a special case of the child_process.spawn() functionality for spawning Node.js processes.
Expand Down
Loading