microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.0.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/adb.ts

215lines · 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 { ChildProcess, ISpawnResult } from "../../common/node/childProcess";
5import { CommandExecutor } from "../../common/commandExecutor";
6import * as path from "path";
7import * as fs from "fs";
8import { ILogger } from "../log/LogHelper";
9import * as os from "os";
10import * as nls from "vscode-nls";
11nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
12const localize = nls.loadMessageBundle();
13
14// See android versions usage at: http://developer.android.com/about/dashboards/index.html
15export enum AndroidAPILevel {
16 Marshmallow = 23,
17 LOLLIPOP_MR1 = 22,
18 LOLLIPOP = 21, /* Supports adb reverse */
19 KITKAT = 19,
20 JELLY_BEAN_MR2 = 18,
21 JELLY_BEAN_MR1 = 17,
22 JELLY_BEAN = 16,
23 ICE_CREAM_SANDWICH_MR1 = 15,
24 GINGERBREAD_MR1 = 10,
25}
26
27enum KeyEvents {
28 KEYCODE_BACK = 4,
29 KEYCODE_DPAD_UP = 19,
30 KEYCODE_DPAD_DOWN = 20,
31 KEYCODE_DPAD_CENTER = 23,
32 KEYCODE_MENU = 82,
33}
34
35export enum DeviceType {
36 AndroidSdkEmulator, // These seem to have emulator-<port> ids
37 Other,
38}
39
40export interface IDevice {
41 id: string;
42 isOnline: boolean;
43 type: DeviceType;
44}
45
46const AndroidSDKEmulatorPattern = /^emulator-\d{1,5}$/;
47
48export class AdbHelper {
49 private childProcess: ChildProcess = new ChildProcess();
50 private commandExecutor: CommandExecutor = new CommandExecutor();
51 private adbExecutable: string = "";
52 private launchActivity: string;
53
54 constructor(projectRoot: string, logger?: ILogger, launchActivity: string = "MainActivity") {
55 this.adbExecutable = this.getAdbPath(projectRoot, logger);
56 this.launchActivity = launchActivity;
57 }
58
59 /**
60 * Gets the list of Android connected devices and emulators.
61 */
62 public getConnectedDevices(): Promise<IDevice[]> {
63 return this.childProcess.execToString(`${this.adbExecutable} devices`)
64 .then(output => {
65 return this.parseConnectedDevices(output);
66 });
67 }
68
69 public setLaunchActivity(launchActivity: string): void {
70 this.launchActivity = launchActivity;
71 }
72
73 /**
74 * Broadcasts an intent to reload the application in debug mode.
75 */
76 public switchDebugMode(projectRoot: string, packageName: string, enable: boolean, debugTarget?: string): Promise<void> {
77 let enableDebugCommand = `${this.adbExecutable} ${debugTarget ? "-s " + debugTarget : ""} shell am broadcast -a "${packageName}.RELOAD_APP_ACTION" --ez jsproxy ${enable}`;
78 return new CommandExecutor(projectRoot).execute(enableDebugCommand)
79 .then(() => { // We should stop and start application again after RELOAD_APP_ACTION, otherwise app going to hangs up
80 return new Promise((resolve) => {
81 setTimeout(() => {
82 this.stopApp(projectRoot, packageName, debugTarget)
83 .then(() => {
84 return resolve();
85 });
86 }, 200); // We need a little delay after broadcast command
87 });
88 })
89 .then(() => {
90 return this.launchApp(projectRoot, packageName, debugTarget);
91 });
92 }
93
94 /**
95 * Sends an intent which launches the main activity of the application.
96 */
97 public launchApp(projectRoot: string, packageName: string, debugTarget?: string): Promise<void> {
98 let launchAppCommand = `${this.adbExecutable} ${debugTarget ? "-s " + debugTarget : ""} shell am start -n ${packageName}/.${this.launchActivity}`;
99 return new CommandExecutor(projectRoot).execute(launchAppCommand);
100 }
101
102 public stopApp(projectRoot: string, packageName: string, debugTarget?: string): Promise<void> {
103 let stopAppCommand = `${this.adbExecutable} ${debugTarget ? "-s " + debugTarget : ""} shell am force-stop ${packageName}`;
104 return new CommandExecutor(projectRoot).execute(stopAppCommand);
105 }
106
107 public apiVersion(deviceId: string): Promise<AndroidAPILevel> {
108 return this.executeQuery(deviceId, "shell getprop ro.build.version.sdk").then(output =>
109 parseInt(output, 10));
110 }
111
112 public reverseAdb(deviceId: string, packagerPort: number): Promise<void> {
113 return this.execute(deviceId, `reverse tcp:${packagerPort} tcp:${packagerPort}`);
114 }
115
116 public showDevMenu(deviceId?: string): Promise<void> {
117 let command = `${this.adbExecutable} ${deviceId ? "-s " + deviceId : ""} shell input keyevent ${KeyEvents.KEYCODE_MENU}`;
118 return this.commandExecutor.execute(command);
119 }
120
121 public reloadApp(deviceId?: string): Promise<void> {
122 let command = `${this.adbExecutable} ${deviceId ? "-s " + deviceId : ""} shell input text "RR"`;
123 return this.commandExecutor.execute(command);
124 }
125
126 public getOnlineDevices(): Promise<IDevice[]> {
127 return this.getConnectedDevices().then(devices => {
128 return devices.filter(device =>
129 device.isOnline);
130 });
131 }
132
133 public startLogCat(adbParameters: string[]): ISpawnResult {
134 return this.childProcess.spawn(this.adbExecutable.replace(/\"/g, ""), adbParameters);
135 }
136
137 public parseSdkLocation(fileContent: string, logger?: ILogger) {
138 const matches = fileContent.match(/^sdk\.dir=(.+)$/m);
139 if (!matches || !matches[1]) {
140 if (logger) {
141 logger.info(localize("NoSdkDirFoundInLocalPropertiesFile", "No sdk.dir value found in local.properties file. Using Android SDK location from PATH."));
142 }
143 return null;
144 }
145
146 let sdkLocation = matches[1].trim();
147 if (os.platform() === "win32") {
148 // For Windows we need to unescape files separators and drive letter separators
149 sdkLocation = sdkLocation.replace(/\\\\/g, "\\").replace("\\:", ":");
150 }
151 if (logger) {
152 logger.info(localize("UsindAndroidSDKLocationDefinedInLocalPropertiesFile", "Using Android SDK location defined in android/local.properties file: {0}.", sdkLocation));
153 }
154
155 return sdkLocation;
156 }
157
158 public getAdbPath(projectRoot: string, logger?: ILogger): string {
159 // Trying to read sdk location from local.properties file and if we succueded then
160 // we would run adb from inside it, otherwise we would rely to PATH
161 const sdkLocation = this.getSdkLocationFromLocalPropertiesFile(projectRoot, logger);
162 return sdkLocation ? `"${path.join(sdkLocation, "platform-tools", "adb")}"` : "adb";
163 }
164
165 private parseConnectedDevices(input: string): IDevice[] {
166 let result: IDevice[] = [];
167 let regex = new RegExp("^(\\S+)\\t(\\S+)$", "mg");
168 let match = regex.exec(input);
169 while (match != null) {
170 result.push({ id: match[1], isOnline: match[2] === "device", type: this.extractDeviceType(match[1]) });
171 match = regex.exec(input);
172 }
173 return result;
174 }
175
176 private extractDeviceType(id: string): DeviceType {
177 return id.match(AndroidSDKEmulatorPattern)
178 ? DeviceType.AndroidSdkEmulator
179 : DeviceType.Other;
180 }
181
182 private executeQuery(deviceId: string, command: string): Promise<string> {
183 return this.childProcess.execToString(this.generateCommandForDevice(deviceId, command));
184 }
185
186 private execute(deviceId: string, command: string): Promise<void> {
187 return this.commandExecutor.execute(this.generateCommandForDevice(deviceId, command));
188 }
189
190 private generateCommandForDevice(deviceId: string, adbCommand: string): string {
191 return `${this.adbExecutable} -s "${deviceId}" ${adbCommand}`;
192 }
193
194 private getSdkLocationFromLocalPropertiesFile(projectRoot: string, logger?: ILogger): string | null {
195 const localPropertiesFilePath = path.join(projectRoot, "android", "local.properties");
196 if (!fs.existsSync(localPropertiesFilePath)) {
197 if (logger) {
198 logger.info(localize("LocalPropertiesFileDoesNotExist", "local.properties file doesn't exist. Using Android SDK location from PATH."));
199 }
200 return null;
201 }
202
203 let fileContent: string;
204 try {
205 fileContent = fs.readFileSync(localPropertiesFilePath).toString();
206 } catch (e) {
207 if (logger) {
208 logger.error(localize("CouldNotReadFrom", "Couldn't read from {0}.", localPropertiesFilePath), e, e.stack);
209 logger.info(localize("UsingAndroidSDKLocationFromPATH", "Using Android SDK location from PATH."));
210 }
211 return null;
212 }
213 return this.parseSdkLocation(fileContent, logger);
214 }
215}
216