microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.7.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/android/androidEmulatorManager.ts

160lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import { AdbHelper } from "./adb";
5import { ChildProcess } from "../../common/node/childProcess";
6import { IVirtualDevice, VirtualDeviceManager } from "../VirtualDeviceManager";
7import { OutputChannelLogger } from "../log/OutputChannelLogger";
8import * as nls from "vscode-nls";
9import { ErrorHelper } from "../../common/error/errorHelper";
10import { InternalErrorCode } from "../../common/error/internalErrorCode";
11import { QuickPickOptions, window } from "vscode";
12import { waitUntil } from "../../common/utils";
13nls.config({
14 messageFormat: nls.MessageFormat.bundle,
15 bundleFormat: nls.BundleFormat.standalone,
16})();
17const localize = nls.loadMessageBundle();
18
19// eslint-disable-next-line @typescript-eslint/no-empty-interface
20export interface IAndroidEmulator extends IVirtualDevice {}
21
22export class AndroidEmulatorManager extends VirtualDeviceManager {
23 private static readonly EMULATOR_COMMAND = "emulator";
24 private static readonly EMULATOR_LIST_AVDS_COMMAND = `-list-avds`;
25 private static readonly EMULATOR_AVD_START_COMMAND = `-avd`;
26
27 private static readonly EMULATOR_START_TIMEOUT = 120;
28
29 private logger: OutputChannelLogger = OutputChannelLogger.getChannel(
30 OutputChannelLogger.MAIN_CHANNEL_NAME,
31 true,
32 );
33
34 private adbHelper: AdbHelper;
35 private childProcess: ChildProcess;
36
37 constructor(adbHelper: AdbHelper) {
38 super();
39 this.adbHelper = adbHelper;
40 this.childProcess = new ChildProcess();
41 }
42
43 public async startEmulator(target: string): Promise<IAndroidEmulator | null> {
44 const onlineDevices = await this.adbHelper.getOnlineDevices();
45 for (let i = 0; i < onlineDevices.length; i++) {
46 if (onlineDevices[i].id === target) {
47 return { id: onlineDevices[i].id };
48 }
49 }
50 if (target && (await this.adbHelper.getOnlineDevices()).length === 0) {
51 if (target === "simulator") {
52 const newEmulator = await this.startSelection();
53 if (newEmulator) {
54 const emulatorId = await this.tryLaunchEmulatorByName(newEmulator);
55 return { name: newEmulator, id: emulatorId };
56 }
57 } else if (!target.includes("device")) {
58 const emulatorId = await this.tryLaunchEmulatorByName(target);
59 return { name: target, id: emulatorId };
60 }
61 }
62 return null;
63 }
64
65 public async tryLaunchEmulatorByName(emulatorName: string): Promise<string> {
66 return new Promise<string>(async (resolve, reject) => {
67 const emulatorProcess = this.childProcess.spawn(
68 AndroidEmulatorManager.EMULATOR_COMMAND,
69 [AndroidEmulatorManager.EMULATOR_AVD_START_COMMAND, emulatorName],
70 {
71 detached: true,
72 },
73 true,
74 );
75 emulatorProcess.outcome.catch(error => {
76 if (
77 process.platform == "win32" &&
78 process.env.SESSIONNAME &&
79 process.env.SESSIONNAME.toLowerCase().includes("rdp-tcp")
80 ) {
81 this.logger.warning(
82 localize(
83 "RDPEmulatorWarning",
84 "Android emulator was launched from the Windows RDP session, this might lead to failures.",
85 ),
86 );
87 }
88 reject(
89 ErrorHelper.getInternalError(
90 InternalErrorCode.VirtualDeviceSelectionError,
91 error,
92 ),
93 );
94 });
95 emulatorProcess.spawnedProcess.unref();
96
97 const condition = async () => {
98 const connectedDevices = await this.adbHelper.getOnlineDevices();
99 return connectedDevices && connectedDevices.length > 0 ? connectedDevices : null;
100 };
101
102 const connectedDevices = await waitUntil(
103 condition,
104 1000,
105 AndroidEmulatorManager.EMULATOR_START_TIMEOUT * 1000,
106 );
107 if (connectedDevices && connectedDevices.length > 0) {
108 this.logger.info(
109 localize("EmulatorLaunched", "Launched emulator {0}", emulatorName),
110 );
111 resolve(connectedDevices[0].id);
112 } else {
113 reject(
114 ErrorHelper.getInternalError(
115 InternalErrorCode.VirtualDeviceSelectionError,
116 localize(
117 "EmulatorStartWarning",
118 "Could not start the emulator {0} within {1} seconds.",
119 emulatorName,
120 AndroidEmulatorManager.EMULATOR_START_TIMEOUT,
121 ),
122 ),
123 );
124 }
125 });
126 }
127
128 public startSelection(): Promise<string | undefined> {
129 return this.selectVirtualDevice();
130 }
131
132 public async selectOnlineDevice(): Promise<string | undefined> {
133 const emulatorsList = (await this.adbHelper.getOnlineDevices()).map(device => device.id);
134 const quickPickOptions: QuickPickOptions = {
135 ignoreFocusOut: true,
136 canPickMany: false,
137 placeHolder: localize("SelectOnlineDevice", "Select online Android device"),
138 };
139 let result: string | undefined = emulatorsList[0];
140 if (emulatorsList.length > 1) {
141 result = await window.showQuickPick(emulatorsList, quickPickOptions);
142 }
143 return result?.toString();
144 }
145
146 protected async getVirtualDevicesNamesList(): Promise<string[]> {
147 const res = await this.childProcess.execToString(
148 `${AndroidEmulatorManager.EMULATOR_COMMAND} ${AndroidEmulatorManager.EMULATOR_LIST_AVDS_COMMAND}`,
149 );
150 let emulatorsList: string[] = [];
151 if (res) {
152 emulatorsList = res.split(/\r?\n|\r/g);
153 const indexOfBlank = emulatorsList.indexOf("");
154 if (emulatorsList.indexOf("") >= 0) {
155 emulatorsList.splice(indexOfBlank, 1);
156 }
157 }
158 return emulatorsList;
159 }
160}
161