microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.4.3

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/androidPlatform.ts

227lines · 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
5c8365a6Artem Egorov8 years ago6import {GeneralMobilePlatform, MobilePlatformDeps } from "../generalMobilePlatform";
0db0be15Artem Egorov8 years ago7import {IAndroidRunOptions} from "../launchArgs";
7daed3fcArtem Egorov8 years ago8import {AdbHelper, AndroidAPILevel, IDevice} from "./adb";
0a68f8dbArtem Egorov8 years ago9import {Package} from "../../common/node/package";
10import {PromiseUtil} from "../../common/node/promise";
5c8365a6Artem Egorov8 years ago11import {PackageNameResolver} from "./packageNameResolver";
0a68f8dbArtem Egorov8 years ago12import {OutputVerifier, PatternToFailure} from "../../common/outputVerifier";
13import {TelemetryHelper} from "../../common/telemetryHelper";
8022afdfVladimir Kotikov8 years ago14import {CommandExecutor} from "../../common/commandExecutor";
0a68f8dbArtem Egorov8 years ago15import {LogCatMonitor} from "./logCatMonitor";
5c8365a6Artem Egorov8 years ago16
52f3873ddigeff10 years ago17/**
18* Android specific platform implementation for debugging RN applications.
19*/
299b0557Patricio Beltran10 years ago20export class AndroidPlatform extends GeneralMobilePlatform {
52f3873ddigeff10 years ago21private static MULTIPLE_DEVICES_ERROR = "error: more than one device/emulator";
22
0a68f8dbArtem Egorov8 years ago23// We should add the common Android build/run errors we find to this list
ef902673Vladimir Kotikov9 years ago24private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure[] = [{
25pattern: "Failed to install on any devices",
26message: "Could not install the app on any available device. Make sure you have a correctly"
27+ " configured device or emulator running. See https://facebook.github.io/react-native/docs/android-setup.html",
28}, {
29pattern: "com.android.ddmlib.ShellCommandUnresponsiveException",
30message: "An Android shell command timed-out. Please retry the operation.",
31}, {
32pattern: "Android project not found",
33message: "Android project not found.",
34
35}, {
36pattern: "error: more than one device/emulator",
37message: AndroidPlatform.MULTIPLE_DEVICES_ERROR,
38}, {
39pattern: /^Error: Activity class \{.*\} does not exist\.$/m,
40message: "Failed to launch the specified activity. Try running application manually and "
41+ "start debugging using 'Attach to packager' launch configuration.",
42}];
52f3873ddigeff10 years ago43
44private static RUN_ANDROID_SUCCESS_PATTERNS: string[] = ["BUILD SUCCESSFUL", "Starting the app", "Starting: Intent"];
45
46private debugTarget: IDevice;
47private devices: IDevice[];
48private packageName: string;
0a68f8dbArtem Egorov8 years ago49private logCatMonitor: LogCatMonitor | null = null;
52f3873ddigeff10 years ago50
51private needsToLaunchApps: boolean = false;
7daed3fcArtem Egorov8 years ago52public static showDevMenu(deviceId?: string): Q.Promise<void> {
53return AdbHelper.showDevMenu(deviceId);
54}
55public static reloadApp(deviceId?: string): Q.Promise<void> {
56return AdbHelper.reloadApp(deviceId);
57}
52f3873ddigeff10 years ago58
299b0557Patricio Beltran10 years ago59// We set remoteExtension = null so that if there is an instance of androidPlatform that wants to have it's custom remoteExtension it can. This is specifically useful for tests.
7daed3fcArtem Egorov8 years ago60constructor(protected runOptions: IAndroidRunOptions, platformDeps: MobilePlatformDeps = {}) {
0a68f8dbArtem Egorov8 years ago61super(runOptions, platformDeps);
1ca47c7cArtem Egorov8 years ago62
63if (this.runOptions.target === AndroidPlatform.simulatorString ||
64this.runOptions.target === AndroidPlatform.deviceString) {
65
66const message = `Target ${this.runOptions.target} is not supported for Android ` +
67"platform. If you want to use particular device or simulator for launching " +
68"Android app, please specify device id (as in 'adb devices' output) instead.";
69
0a68f8dbArtem Egorov8 years ago70this.logger.warning(message);
1ca47c7cArtem Egorov8 years ago71delete this.runOptions.target;
72}
52f3873ddigeff10 years ago73}
74
75public runApp(shouldLaunchInAllDevices: boolean = false): Q.Promise<void> {
76return TelemetryHelper.generate("AndroidPlatform.runApp", () => {
8022afdfVladimir Kotikov8 years ago77const runArguments = this.getRunArgument();
0a68f8dbArtem Egorov8 years ago78const runAndroidSpawn = new CommandExecutor(this.projectPath, this.logger).spawnReactCommand("run-android", runArguments);
8022afdfVladimir Kotikov8 years ago79
52f3873ddigeff10 years ago80const output = new OutputVerifier(
81() =>
82Q(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
83() =>
84Q(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS)).process(runAndroidSpawn);
85
86return output
87.finally(() => {
88return this.initializeTargetDevicesAndPackageName();
89}).then(() => [this.debugTarget], reason => {
90if (reason.message === AndroidPlatform.MULTIPLE_DEVICES_ERROR && this.devices.length > 1 && this.debugTarget) {
91/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
92this.needsToLaunchApps = true;
93return shouldLaunchInAllDevices
7daed3fcArtem Egorov8 years ago94? AdbHelper.getOnlineDevices()
52f3873ddigeff10 years ago95: Q([this.debugTarget]);
96} else {
97return Q.reject<IDevice[]>(reason);
98}
99}).then(devices => {
100return new PromiseUtil().forEach(devices, device => {
101return this.launchAppWithADBReverseAndLogCat(device);
102});
103});
104});
105}
106
107public enableJSDebuggingMode(): Q.Promise<void> {
7daed3fcArtem Egorov8 years ago108return AdbHelper.switchDebugMode(this.runOptions.projectRoot, this.packageName, true, this.debugTarget.id);
b57ea017Artem Egorov8 years ago109}
110
111public disableJSDebuggingMode(): Q.Promise<void> {
7daed3fcArtem Egorov8 years ago112return AdbHelper.switchDebugMode(this.runOptions.projectRoot, this.packageName, false, this.debugTarget.id);
52f3873ddigeff10 years ago113}
114
299b0557Patricio Beltran10 years ago115public prewarmBundleCache(): Q.Promise<void> {
0a68f8dbArtem Egorov8 years ago116return this.packager.prewarmBundleCache("android");
299b0557Patricio Beltran10 years ago117}
118
8022afdfVladimir Kotikov8 years ago119public getRunArgument(): string[] {
120let runArguments: string[] = [];
121
122if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
123runArguments = this.runOptions.runArguments;
124} else {
125if (this.runOptions.variant) {
126runArguments.push("--variant", this.runOptions.variant);
127}
128if (this.runOptions.target) {
129runArguments.push("--deviceId", this.runOptions.target);
130}
131}
132
b57ea017Artem Egorov8 years ago133runArguments.push("--no-packager");
134
8022afdfVladimir Kotikov8 years ago135return runArguments;
136}
137
52f3873ddigeff10 years ago138private initializeTargetDevicesAndPackageName(): Q.Promise<void> {
7daed3fcArtem Egorov8 years ago139return AdbHelper.getConnectedDevices().then(devices => {
52f3873ddigeff10 years ago140this.devices = devices;
141this.debugTarget = this.getTargetEmulator(devices);
142return this.getPackageName().then(packageName => {
143this.packageName = packageName;
144});
145});
146}
147
148private launchAppWithADBReverseAndLogCat(device: IDevice): Q.Promise<void> {
149return Q({})
150.then(() => {
151return this.configureADBReverseWhenApplicable(device);
152}).then(() => {
153return this.needsToLaunchApps
7daed3fcArtem Egorov8 years ago154? AdbHelper.launchApp(this.runOptions.projectRoot, this.packageName, device.id)
52f3873ddigeff10 years ago155: Q<void>(void 0);
156}).then(() => {
0a68f8dbArtem Egorov8 years ago157return this.startMonitoringLogCat(device, this.runOptions.logCatArguments);
52f3873ddigeff10 years ago158});
159}
160
161private configureADBReverseWhenApplicable(device: IDevice): Q.Promise<void> {
b57ea017Artem Egorov8 years ago162return Q({}) // For other emulators and devices we try to enable adb reverse
7daed3fcArtem Egorov8 years ago163.then(() => AdbHelper.apiVersion(device.id))
b57ea017Artem Egorov8 years ago164.then(apiVersion => {
165if (apiVersion >= AndroidAPILevel.LOLLIPOP) { // If we support adb reverse
7daed3fcArtem Egorov8 years ago166return AdbHelper.reverseAdb(device.id, Number( this.runOptions.packagerPort));
b57ea017Artem Egorov8 years ago167} else {
0a68f8dbArtem Egorov8 years ago168this.logger.warning(`Device ${device.id} supports only API Level ${apiVersion}. `
b57ea017Artem Egorov8 years ago169+ `Level ${AndroidAPILevel.LOLLIPOP} is needed to support port forwarding via adb reverse. `
170+ "For debugging to work you'll need <Shake or press menu button> for the dev menu, "
171+ "go into <Dev Settings> and configure <Debug Server host & port for Device> to be "
172+ "an IP address of your computer that the Device can reach. More info at: "
173+ "https://facebook.github.io/react-native/docs/debugging.html#debugging-react-native-apps");
174return void 0;
175}
176});
52f3873ddigeff10 years ago177}
178
179private getPackageName(): Q.Promise<string> {
8022afdfVladimir Kotikov8 years ago180return new Package(this.runOptions.projectRoot).name().then(appName =>
52f3873ddigeff10 years ago181new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot));
182}
183
184/**
185* Returns the target emulator, using the following logic:
186* * If an emulator is specified and it is connected, use that one.
187* * Otherwise, use the first one in the list.
188*/
189private getTargetEmulator(devices: IDevice[]): IDevice {
190let activeFilterFunction = (device: IDevice) => {
191return device.isOnline;
192};
193
194let targetFilterFunction = (device: IDevice) => {
195return device.id === this.runOptions.target && activeFilterFunction(device);
196};
197
198if (this.runOptions && this.runOptions.target && devices) {
199/* check if the specified target is active */
200const targetDevice = devices.find(targetFilterFunction);
201if (targetDevice) {
202return targetDevice;
203}
204}
205
206/* return the first active device in the list */
207let activeDevices = devices && devices.filter(activeFilterFunction);
208return activeDevices && activeDevices[0];
209}
210
0a68f8dbArtem Egorov8 years ago211private startMonitoringLogCat(device: IDevice, logCatArguments: string): void {
212this.stopMonitoringLogCat(); // Stop previous logcat monitor if it's running
213
214// this.logCatMonitor can be mutated, so we store it locally too
215this.logCatMonitor = new LogCatMonitor(device.id, logCatArguments);
216this.logCatMonitor.start() // The LogCat will continue running forever, so we don't wait for it
217.catch(error => this.logger.warning("Error while monitoring LogCat", error)) // The LogCatMonitor failing won't stop the debugging experience
218.done();
219}
220
221private stopMonitoringLogCat(): void {
222if (this.logCatMonitor) {
223this.logCatMonitor.dispose();
224this.logCatMonitor = null;
225}
52f3873ddigeff10 years ago226}
ef902673Vladimir Kotikov9 years ago227}