microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.7.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/adb.ts

217lines · modeblame

52f3873ddigeff10 years ago1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as Q from "q";
5
db6fd42aRuslan Bikkinin7 years ago6import { ChildProcess, ISpawnResult } from "../../common/node/childProcess";
7import { CommandExecutor } from "../../common/commandExecutor";
8import * as path from "path";
9import * as fs from "fs";
10import { ILogger } from "../log/LogHelper";
8b383051Ruslan Bikkinin7 years ago11import * as os from "os";
52f3873ddigeff10 years ago12
ce7fd946digeff10 years ago13// See android versions usage at: http://developer.android.com/about/dashboards/index.html
52f3873ddigeff10 years ago14export enum AndroidAPILevel {
ce7fd946digeff10 years ago15Marshmallow = 23,
52f3873ddigeff10 years ago16LOLLIPOP_MR1 = 22,
17LOLLIPOP = 21, /* Supports adb reverse */
18KITKAT = 19,
19JELLY_BEAN_MR2 = 18,
20JELLY_BEAN_MR1 = 17,
21JELLY_BEAN = 16,
22ICE_CREAM_SANDWICH_MR1 = 15,
23GINGERBREAD_MR1 = 10,
24}
25
7daed3fcArtem Egorov8 years ago26enum KeyEvents {
27KEYCODE_BACK = 4,
28KEYCODE_DPAD_UP = 19,
29KEYCODE_DPAD_DOWN = 20,
30KEYCODE_DPAD_CENTER = 23,
31KEYCODE_MENU = 82,
32}
33
52f3873ddigeff10 years ago34export enum DeviceType {
35AndroidSdkEmulator, // These seem to have emulator-<port> ids
27710197Vladimir Kotikov8 years ago36Other,
52f3873ddigeff10 years ago37}
38
39export interface IDevice {
40id: string;
41isOnline: boolean;
42type: DeviceType;
43}
44
7daed3fcArtem Egorov8 years ago45const AndroidSDKEmulatorPattern = /^emulator-\d{1,5}$/;
52f3873ddigeff10 years ago46
7daed3fcArtem Egorov8 years ago47export class AdbHelper {
db6fd42aRuslan Bikkinin7 years ago48private childProcess: ChildProcess = new ChildProcess();
49private commandExecutor: CommandExecutor = new CommandExecutor();
50private adbExecutable: string = "";
51
52constructor(projectRoot: string, logger?: ILogger) {
53
54// Trying to read sdk location from local.properties file and if we succueded then
55// we would run adb from inside it, otherwise we would rely to PATH
56const sdkLocation = this.getSdkLocationFromLocalPropertiesFile(projectRoot, logger);
57this.adbExecutable = sdkLocation ? `${path.join(sdkLocation, "platform-tools", "adb")}` : "adb";
58}
52f3873ddigeff10 years ago59
60/**
61* Gets the list of Android connected devices and emulators.
62*/
db6fd42aRuslan Bikkinin7 years ago63public getConnectedDevices(): Q.Promise<IDevice[]> {
64return this.childProcess.execToString(`${this.adbExecutable} devices`)
52f3873ddigeff10 years ago65.then(output => {
66return this.parseConnectedDevices(output);
67});
68}
69
70/**
71* Broadcasts an intent to reload the application in debug mode.
72*/
db6fd42aRuslan Bikkinin7 years ago73public switchDebugMode(projectRoot: string, packageName: string, enable: boolean, debugTarget?: string): Q.Promise<void> {
74let enableDebugCommand = `${this.adbExecutable} ${debugTarget ? "-s " + debugTarget : ""} shell am broadcast -a "${packageName}.RELOAD_APP_ACTION" --ez jsproxy ${enable}`;
b57ea017Artem Egorov8 years ago75return new CommandExecutor(projectRoot).execute(enableDebugCommand)
76.then(() => { // We should stop and start application again after RELOAD_APP_ACTION, otherwise app going to hangs up
77let deferred = Q.defer();
78setTimeout(() => {
79this.stopApp(projectRoot, packageName, debugTarget)
80.then(() => {
81return deferred.resolve({});
82});
83}, 200); // We need a little delay after broadcast command
84
85return deferred.promise;
86})
87.then(() => {
88return this.launchApp(projectRoot, packageName, debugTarget);
89});
52f3873ddigeff10 years ago90}
91
92/**
93* Sends an intent which launches the main activity of the application.
94*/
db6fd42aRuslan Bikkinin7 years ago95public launchApp(projectRoot: string, packageName: string, debugTarget?: string): Q.Promise<void> {
96let launchAppCommand = `${this.adbExecutable} ${debugTarget ? "-s " + debugTarget : ""} shell am start -n ${packageName}/.MainActivity`;
52f3873ddigeff10 years ago97return new CommandExecutor(projectRoot).execute(launchAppCommand);
98}
99
db6fd42aRuslan Bikkinin7 years ago100public stopApp(projectRoot: string, packageName: string, debugTarget?: string): Q.Promise<void> {
101let stopAppCommand = `${this.adbExecutable} ${debugTarget ? "-s " + debugTarget : ""} shell am force-stop ${packageName}`;
b57ea017Artem Egorov8 years ago102return new CommandExecutor(projectRoot).execute(stopAppCommand);
103}
104
db6fd42aRuslan Bikkinin7 years ago105public apiVersion(deviceId: string): Q.Promise<AndroidAPILevel> {
52f3873ddigeff10 years ago106return this.executeQuery(deviceId, "shell getprop ro.build.version.sdk").then(output =>
107parseInt(output, 10));
108}
109
db6fd42aRuslan Bikkinin7 years ago110public reverseAdb(deviceId: string, packagerPort: number): Q.Promise<void> {
b57ea017Artem Egorov8 years ago111return this.execute(deviceId, `reverse tcp:${packagerPort} tcp:${packagerPort}`);
52f3873ddigeff10 years ago112}
113
db6fd42aRuslan Bikkinin7 years ago114public showDevMenu(deviceId?: string): Q.Promise<void> {
115let command = `${this.adbExecutable} ${deviceId ? "-s " + deviceId : ""} shell input keyevent ${KeyEvents.KEYCODE_MENU}`;
7daed3fcArtem Egorov8 years ago116return this.commandExecutor.execute(command);
117}
118
db6fd42aRuslan Bikkinin7 years ago119public reloadApp(deviceId?: string): Q.Promise<void> {
7daed3fcArtem Egorov8 years ago120let commands = [
db6fd42aRuslan Bikkinin7 years ago121`${this.adbExecutable} ${deviceId ? "-s " + deviceId : ""} shell input keyevent ${KeyEvents.KEYCODE_MENU}`,
122`${this.adbExecutable} ${deviceId ? "-s " + deviceId : ""} shell input keyevent ${KeyEvents.KEYCODE_DPAD_UP}`,
123`${this.adbExecutable} ${deviceId ? "-s " + deviceId : ""} shell input keyevent ${KeyEvents.KEYCODE_DPAD_CENTER}`,
7daed3fcArtem Egorov8 years ago124];
125
126return this.executeChain(commands);
127}
128
db6fd42aRuslan Bikkinin7 years ago129public getOnlineDevices(): Q.Promise<IDevice[]> {
7daed3fcArtem Egorov8 years ago130return this.getConnectedDevices().then(devices => {
131return devices.filter(device =>
132device.isOnline);
133});
134}
135
db6fd42aRuslan Bikkinin7 years ago136public startLogCat(adbParameters: string[]): ISpawnResult {
137return new ChildProcess().spawn(`${this.adbExecutable}`, adbParameters);
138}
139
8b383051Ruslan Bikkinin7 years ago140public parseSdkLocation(fileContent: string, logger?: ILogger) {
141const matches = fileContent.match(/^sdk\.dir=(.+)$/m);
142if (!matches || !matches[1]) {
143if (logger) {
144logger.info(`No sdk.dir value found in local.properties file. Using Android SDK location from PATH.`);
145}
146return null;
147}
148
149let sdkLocation = matches[1].trim();
150if (os.platform() === "win32") {
151// For Windows we need to unescape files separators and drive letter separators
152sdkLocation = sdkLocation.replace(/\\\\/g, "\\").replace("\\:", ":");
153}
154if (logger) {
155logger.info(`Using Android SDK location defined in android/local.properties file: ${sdkLocation}.`);
156}
157
158return sdkLocation;
159}
160
db6fd42aRuslan Bikkinin7 years ago161private parseConnectedDevices(input: string): IDevice[] {
52f3873ddigeff10 years ago162let result: IDevice[] = [];
163let regex = new RegExp("^(\\S+)\\t(\\S+)$", "mg");
164let match = regex.exec(input);
165while (match != null) {
166result.push({ id: match[1], isOnline: match[2] === "device", type: this.extractDeviceType(match[1]) });
167match = regex.exec(input);
168}
169return result;
170}
171
db6fd42aRuslan Bikkinin7 years ago172private extractDeviceType(id: string): DeviceType {
52f3873ddigeff10 years ago173return id.match(AndroidSDKEmulatorPattern)
174? DeviceType.AndroidSdkEmulator
175: DeviceType.Other;
176}
177
db6fd42aRuslan Bikkinin7 years ago178private executeQuery(deviceId: string, command: string): Q.Promise<string> {
52f3873ddigeff10 years ago179return this.childProcess.execToString(this.generateCommandForDevice(deviceId, command));
180}
181
db6fd42aRuslan Bikkinin7 years ago182private execute(deviceId: string, command: string): Q.Promise<void> {
52f3873ddigeff10 years ago183return this.commandExecutor.execute(this.generateCommandForDevice(deviceId, command));
184}
185
db6fd42aRuslan Bikkinin7 years ago186private executeChain(commands: string[]): Q.Promise<any> {
7daed3fcArtem Egorov8 years ago187return commands.reduce((promise, command) => {
188return promise.then(() => this.commandExecutor.execute(command));
189}, Q(void 0));
190}
191
db6fd42aRuslan Bikkinin7 years ago192private generateCommandForDevice(deviceId: string, adbCommand: string): string {
193return `${this.adbExecutable} -s "${deviceId}" ${adbCommand}`;
194}
195
196private getSdkLocationFromLocalPropertiesFile(projectRoot: string, logger?: ILogger): string | null {
197const localPropertiesFilePath = path.join(projectRoot, "android", "local.properties");
198if (!fs.existsSync(localPropertiesFilePath)) {
199if (logger) {
200logger.info(`local.properties file doesn't exist. Using Android SDK location from PATH.`);
201}
202return null;
203}
204
8b383051Ruslan Bikkinin7 years ago205let fileContent: string;
db6fd42aRuslan Bikkinin7 years ago206try {
207fileContent = fs.readFileSync(localPropertiesFilePath).toString();
208} catch (e) {
209if (logger) {
8b383051Ruslan Bikkinin7 years ago210logger.error(`Couldn't read from ${localPropertiesFilePath}.`, e, e.stack);
db6fd42aRuslan Bikkinin7 years ago211logger.info(`Using Android SDK location from PATH.`);
212}
213return null;
214}
8b383051Ruslan Bikkinin7 years ago215return this.parseSdkLocation(fileContent, logger);
52f3873ddigeff10 years ago216}
217}