microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.3.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/common/android/androidPlatform.ts

188lines · 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
ac7fef0cPatricio Beltran9 years ago6import {GeneralMobilePlatform} from "../generalMobilePlatform";
52f3873ddigeff10 years ago7import {Packager} from "../../common/packager";
8import {IRunOptions} from "../../common/launchArgs";
9import {Log} from "../../common/log/log";
10import {IAdb, Adb, AndroidAPILevel, IDevice, DeviceType} from "../../common/android/adb";
11import {Package} from "../../common/node/package";
12import {PromiseUtil} from "../../common/node/promise";
13import {PackageNameResolver} from "../../common/android/packageNameResolver";
14import {OutputVerifier, PatternToFailure} from "../../common/outputVerifier";
15import {FileSystem} from "../../common/node/fileSystem";
16import {IReactNative, ReactNative} from "../../common/reactNative";
17import {TelemetryHelper} from "../../common/telemetryHelper";
18
19
20/**
21* Android specific platform implementation for debugging RN applications.
22*/
299b0557Patricio Beltran10 years ago23export class AndroidPlatform extends GeneralMobilePlatform {
52f3873ddigeff10 years ago24private static MULTIPLE_DEVICES_ERROR = "error: more than one device/emulator";
25
26// We should add the common Android build/run erros we find to this list
ef902673Vladimir Kotikov9 years ago27private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure[] = [{
28pattern: "Failed to install on any devices",
29message: "Could not install the app on any available device. Make sure you have a correctly"
30+ " configured device or emulator running. See https://facebook.github.io/react-native/docs/android-setup.html",
31}, {
32pattern: "com.android.ddmlib.ShellCommandUnresponsiveException",
33message: "An Android shell command timed-out. Please retry the operation.",
34}, {
35pattern: "Android project not found",
36message: "Android project not found.",
37
38}, {
39pattern: "error: more than one device/emulator",
40message: AndroidPlatform.MULTIPLE_DEVICES_ERROR,
41}, {
42pattern: /^Error: Activity class \{.*\} does not exist\.$/m,
43message: "Failed to launch the specified activity. Try running application manually and "
44+ "start debugging using 'Attach to packager' launch configuration.",
45}];
52f3873ddigeff10 years ago46
47private static RUN_ANDROID_SUCCESS_PATTERNS: string[] = ["BUILD SUCCESSFUL", "Starting the app", "Starting: Intent"];
48
49private debugTarget: IDevice;
50private devices: IDevice[];
51private packageName: string;
52private adb: IAdb;
53private reactNative: IReactNative;
54private fileSystem: FileSystem;
55
56private needsToLaunchApps: boolean = false;
57
299b0557Patricio Beltran10 years ago58// 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.
59constructor(runOptions: IRunOptions, { remoteExtension = null,
52f3873ddigeff10 years ago60adb = <IAdb>new Adb(),
61reactNative = <IReactNative>new ReactNative(),
62fileSystem = new FileSystem(),
63} = {}) {
299b0557Patricio Beltran10 years ago64super(runOptions, { remoteExtension: remoteExtension });
52f3873ddigeff10 years ago65this.adb = adb;
66this.reactNative = reactNative;
67this.fileSystem = fileSystem;
68}
69
70public runApp(shouldLaunchInAllDevices: boolean = false): Q.Promise<void> {
71return TelemetryHelper.generate("AndroidPlatform.runApp", () => {
de899f99Jimmy Thomson9 years ago72const runAndroidSpawn = this.reactNative.runAndroid(this.runOptions.projectRoot, this.runOptions.variant);
52f3873ddigeff10 years ago73const output = new OutputVerifier(
74() =>
75Q(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
76() =>
77Q(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS)).process(runAndroidSpawn);
78
79return output
80.finally(() => {
81return this.initializeTargetDevicesAndPackageName();
82}).then(() => [this.debugTarget], reason => {
83if (reason.message === AndroidPlatform.MULTIPLE_DEVICES_ERROR && this.devices.length > 1 && this.debugTarget) {
84/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
85this.needsToLaunchApps = true;
86return shouldLaunchInAllDevices
87? this.adb.getOnlineDevices()
88: Q([this.debugTarget]);
89} else {
90return Q.reject<IDevice[]>(reason);
91}
92}).then(devices => {
93return new PromiseUtil().forEach(devices, device => {
94return this.launchAppWithADBReverseAndLogCat(device);
95});
96});
97});
98}
99
100public enableJSDebuggingMode(): Q.Promise<void> {
101return this.adb.reloadAppInDebugMode(this.runOptions.projectRoot, this.packageName, this.debugTarget.id);
102}
103
299b0557Patricio Beltran10 years ago104public prewarmBundleCache(): Q.Promise<void> {
105return this.remoteExtension.prewarmBundleCache(this.platformName);
106}
107
52f3873ddigeff10 years ago108private initializeTargetDevicesAndPackageName(): Q.Promise<void> {
109return this.adb.getConnectedDevices().then(devices => {
110this.devices = devices;
111this.debugTarget = this.getTargetEmulator(devices);
112return this.getPackageName().then(packageName => {
113this.packageName = packageName;
114});
115});
116}
117
118private launchAppWithADBReverseAndLogCat(device: IDevice): Q.Promise<void> {
119return Q({})
120.then(() => {
121return this.configureADBReverseWhenApplicable(device);
122}).then(() => {
123return this.needsToLaunchApps
124? this.adb.launchApp(this.runOptions.projectRoot, this.packageName, device.id)
125: Q<void>(void 0);
126}).then(() => {
127return this.startMonitoringLogCat(device, this.runOptions.logCatArguments).catch(error => // The LogCatMonitor failing won't stop the debugging experience
128Log.logWarning("Couldn't start LogCat monitor", error));
129});
130}
131
132private configureADBReverseWhenApplicable(device: IDevice): Q.Promise<void> {
133if (device.type !== DeviceType.AndroidSdkEmulator) {
134return Q({}) // For other emulators and devices we try to enable adb reverse
135.then(() => this.adb.apiVersion(device.id))
136.then(apiVersion => {
137if (apiVersion >= AndroidAPILevel.LOLLIPOP) { // If we support adb reverse
138return this.adb.reverseAdd(device.id, Packager.DEFAULT_PORT.toString(), this.runOptions.packagerPort);
139} else {
140Log.logWarning(`Device ${device.id} supports only API Level ${apiVersion}. `
141+ `Level ${AndroidAPILevel.LOLLIPOP} is needed to support port forwarding via adb reverse. `
142+ "For debugging to work you'll need <Shake or press menu button> for the dev menu, "
143+ "go into <Dev Settings> and configure <Debug Server host & port for Device> to be "
144+ "an IP address of your computer that the Device can reach. More info at: "
145+ "https://facebook.github.io/react-native/docs/debugging.html#debugging-react-native-apps");
146}
147});
148} else {
149return Q<void>(void 0); // Android SDK emulators can connect directly to 10.0.0.2, so they don't need port forwarding
150}
151}
152
153private getPackageName(): Q.Promise<string> {
154return new Package(this.runOptions.projectRoot, { fileSystem: this.fileSystem }).name().then(appName =>
155new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot));
156}
157
158/**
159* Returns the target emulator, using the following logic:
160* * If an emulator is specified and it is connected, use that one.
161* * Otherwise, use the first one in the list.
162*/
163private getTargetEmulator(devices: IDevice[]): IDevice {
164let activeFilterFunction = (device: IDevice) => {
165return device.isOnline;
166};
167
168let targetFilterFunction = (device: IDevice) => {
169return device.id === this.runOptions.target && activeFilterFunction(device);
170};
171
172if (this.runOptions && this.runOptions.target && devices) {
173/* check if the specified target is active */
174const targetDevice = devices.find(targetFilterFunction);
175if (targetDevice) {
176return targetDevice;
177}
178}
179
180/* return the first active device in the list */
181let activeDevices = devices && devices.filter(activeFilterFunction);
182return activeDevices && activeDevices[0];
183}
184
185private startMonitoringLogCat(device: IDevice, logCatArguments: string): Q.Promise<void> {
186return this.remoteExtension.startMonitoringLogcat(device.id, logCatArguments);
187}
ef902673Vladimir Kotikov9 years ago188}