microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.4.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/common/android/androidPlatform.ts

215lines · 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";
7import {Packager} from "../packager";
0db0be15Artem Egorov8 years ago8import {IAndroidRunOptions} from "../launchArgs";
5c8365a6Artem Egorov8 years ago9import {Log} from "../log/log";
10import {IAdb, Adb, AndroidAPILevel, IDevice, DeviceType} from "./adb";
11import {Package} from "../node/package";
12import {PromiseUtil} from "../node/promise";
13import {PackageNameResolver} from "./packageNameResolver";
14import {OutputVerifier, PatternToFailure} from "../outputVerifier";
15import {TelemetryHelper} from "../telemetryHelper";
8022afdfVladimir Kotikov8 years ago16import {CommandExecutor} from "../../common/commandExecutor";
5c8365a6Artem Egorov8 years ago17
18export interface AndroidPlatformDeps extends MobilePlatformDeps {
19adb?: IAdb;
20}
52f3873ddigeff10 years ago21/**
22* Android specific platform implementation for debugging RN applications.
23*/
299b0557Patricio Beltran10 years ago24export class AndroidPlatform extends GeneralMobilePlatform {
52f3873ddigeff10 years ago25private static MULTIPLE_DEVICES_ERROR = "error: more than one device/emulator";
26
27// We should add the common Android build/run erros we find to this list
ef902673Vladimir Kotikov9 years ago28private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure[] = [{
29pattern: "Failed to install on any devices",
30message: "Could not install the app on any available device. Make sure you have a correctly"
31+ " configured device or emulator running. See https://facebook.github.io/react-native/docs/android-setup.html",
32}, {
33pattern: "com.android.ddmlib.ShellCommandUnresponsiveException",
34message: "An Android shell command timed-out. Please retry the operation.",
35}, {
36pattern: "Android project not found",
37message: "Android project not found.",
38
39}, {
40pattern: "error: more than one device/emulator",
41message: AndroidPlatform.MULTIPLE_DEVICES_ERROR,
42}, {
43pattern: /^Error: Activity class \{.*\} does not exist\.$/m,
44message: "Failed to launch the specified activity. Try running application manually and "
45+ "start debugging using 'Attach to packager' launch configuration.",
46}];
52f3873ddigeff10 years ago47
48private static RUN_ANDROID_SUCCESS_PATTERNS: string[] = ["BUILD SUCCESSFUL", "Starting the app", "Starting: Intent"];
49
50private debugTarget: IDevice;
51private devices: IDevice[];
52private packageName: string;
53private adb: IAdb;
54
55private needsToLaunchApps: boolean = false;
56
299b0557Patricio Beltran10 years ago57// 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.
0db0be15Artem Egorov8 years ago58constructor(protected runOptions: IAndroidRunOptions, {
5c8365a6Artem Egorov8 years ago59remoteExtension,
52f3873ddigeff10 years ago60adb = <IAdb>new Adb(),
5c8365a6Artem Egorov8 years ago61}: AndroidPlatformDeps = {}) {
299b0557Patricio Beltran10 years ago62super(runOptions, { remoteExtension: remoteExtension });
52f3873ddigeff10 years ago63this.adb = adb;
1ca47c7cArtem Egorov8 years ago64
65if (this.runOptions.target === AndroidPlatform.simulatorString ||
66this.runOptions.target === AndroidPlatform.deviceString) {
67
68const message = `Target ${this.runOptions.target} is not supported for Android ` +
69"platform. If you want to use particular device or simulator for launching " +
70"Android app, please specify device id (as in 'adb devices' output) instead.";
71
72Log.logMessage(message);
73delete this.runOptions.target;
74}
52f3873ddigeff10 years ago75}
76
77public runApp(shouldLaunchInAllDevices: boolean = false): Q.Promise<void> {
78return TelemetryHelper.generate("AndroidPlatform.runApp", () => {
8022afdfVladimir Kotikov8 years ago79const runArguments = this.getRunArgument();
80const runAndroidSpawn = new CommandExecutor(this.projectPath).spawnReactCommand("run-android", runArguments);
81
52f3873ddigeff10 years ago82const output = new OutputVerifier(
83() =>
84Q(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
85() =>
86Q(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS)).process(runAndroidSpawn);
87
88return output
89.finally(() => {
90return this.initializeTargetDevicesAndPackageName();
91}).then(() => [this.debugTarget], reason => {
92if (reason.message === AndroidPlatform.MULTIPLE_DEVICES_ERROR && this.devices.length > 1 && this.debugTarget) {
93/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
94this.needsToLaunchApps = true;
95return shouldLaunchInAllDevices
96? this.adb.getOnlineDevices()
97: Q([this.debugTarget]);
98} else {
99return Q.reject<IDevice[]>(reason);
100}
101}).then(devices => {
102return new PromiseUtil().forEach(devices, device => {
103return this.launchAppWithADBReverseAndLogCat(device);
104});
105});
106});
107}
108
109public enableJSDebuggingMode(): Q.Promise<void> {
110return this.adb.reloadAppInDebugMode(this.runOptions.projectRoot, this.packageName, this.debugTarget.id);
111}
112
299b0557Patricio Beltran10 years ago113public prewarmBundleCache(): Q.Promise<void> {
114return this.remoteExtension.prewarmBundleCache(this.platformName);
115}
116
8022afdfVladimir Kotikov8 years ago117public getRunArgument(): string[] {
118let runArguments: string[] = [];
119
120if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
121runArguments = this.runOptions.runArguments;
122} else {
123if (this.runOptions.variant) {
124runArguments.push("--variant", this.runOptions.variant);
125}
126if (this.runOptions.target) {
127runArguments.push("--deviceId", this.runOptions.target);
128}
129}
130
131return runArguments;
132}
133
52f3873ddigeff10 years ago134private initializeTargetDevicesAndPackageName(): Q.Promise<void> {
135return this.adb.getConnectedDevices().then(devices => {
136this.devices = devices;
137this.debugTarget = this.getTargetEmulator(devices);
138return this.getPackageName().then(packageName => {
139this.packageName = packageName;
140});
141});
142}
143
144private launchAppWithADBReverseAndLogCat(device: IDevice): Q.Promise<void> {
145return Q({})
146.then(() => {
147return this.configureADBReverseWhenApplicable(device);
148}).then(() => {
149return this.needsToLaunchApps
150? this.adb.launchApp(this.runOptions.projectRoot, this.packageName, device.id)
151: Q<void>(void 0);
152}).then(() => {
153return this.startMonitoringLogCat(device, this.runOptions.logCatArguments).catch(error => // The LogCatMonitor failing won't stop the debugging experience
154Log.logWarning("Couldn't start LogCat monitor", error));
155});
156}
157
158private configureADBReverseWhenApplicable(device: IDevice): Q.Promise<void> {
159if (device.type !== DeviceType.AndroidSdkEmulator) {
160return Q({}) // For other emulators and devices we try to enable adb reverse
161.then(() => this.adb.apiVersion(device.id))
162.then(apiVersion => {
163if (apiVersion >= AndroidAPILevel.LOLLIPOP) { // If we support adb reverse
164return this.adb.reverseAdd(device.id, Packager.DEFAULT_PORT.toString(), this.runOptions.packagerPort);
165} else {
166Log.logWarning(`Device ${device.id} supports only API Level ${apiVersion}. `
167+ `Level ${AndroidAPILevel.LOLLIPOP} is needed to support port forwarding via adb reverse. `
168+ "For debugging to work you'll need <Shake or press menu button> for the dev menu, "
169+ "go into <Dev Settings> and configure <Debug Server host & port for Device> to be "
170+ "an IP address of your computer that the Device can reach. More info at: "
171+ "https://facebook.github.io/react-native/docs/debugging.html#debugging-react-native-apps");
5c8365a6Artem Egorov8 years ago172return void 0;
52f3873ddigeff10 years ago173}
174});
175} else {
176return Q<void>(void 0); // Android SDK emulators can connect directly to 10.0.0.2, so they don't need port forwarding
177}
178}
179
180private getPackageName(): Q.Promise<string> {
8022afdfVladimir Kotikov8 years ago181return new Package(this.runOptions.projectRoot).name().then(appName =>
52f3873ddigeff10 years ago182new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot));
183}
184
185/**
186* Returns the target emulator, using the following logic:
187* * If an emulator is specified and it is connected, use that one.
188* * Otherwise, use the first one in the list.
189*/
190private getTargetEmulator(devices: IDevice[]): IDevice {
191let activeFilterFunction = (device: IDevice) => {
192return device.isOnline;
193};
194
195let targetFilterFunction = (device: IDevice) => {
196return device.id === this.runOptions.target && activeFilterFunction(device);
197};
198
199if (this.runOptions && this.runOptions.target && devices) {
200/* check if the specified target is active */
201const targetDevice = devices.find(targetFilterFunction);
202if (targetDevice) {
203return targetDevice;
204}
205}
206
207/* return the first active device in the list */
208let activeDevices = devices && devices.filter(activeFilterFunction);
209return activeDevices && activeDevices[0];
210}
211
212private startMonitoringLogCat(device: IDevice, logCatArguments: string): Q.Promise<void> {
213return this.remoteExtension.startMonitoringLogcat(device.id, logCatArguments);
214}
ef902673Vladimir Kotikov9 years ago215}