microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
280c07463f45573fc0cd19fc2d2eb91eb3e828fd

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/common/android/androidPlatform.ts

176lines · 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
27private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure = {
28"Failed to install on any devices": "Could not install the app on any available device. Make sure you have a correctly"
29+ " configured device or emulator running. See https://facebook.github.io/react-native/docs/android-setup.html",
30"com.android.ddmlib.ShellCommandUnresponsiveException": "An Android shell command timed-out. Please retry the operation.",
31"Android project not found": "Android project not found.",
32"error: more than one device/emulator": AndroidPlatform.MULTIPLE_DEVICES_ERROR,
33};
34
35private static RUN_ANDROID_SUCCESS_PATTERNS: string[] = ["BUILD SUCCESSFUL", "Starting the app", "Starting: Intent"];
36
37private debugTarget: IDevice;
38private devices: IDevice[];
39private packageName: string;
40private adb: IAdb;
41private reactNative: IReactNative;
42private fileSystem: FileSystem;
43
44private needsToLaunchApps: boolean = false;
45
299b0557Patricio Beltran10 years ago46// 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.
47constructor(runOptions: IRunOptions, { remoteExtension = null,
52f3873ddigeff10 years ago48adb = <IAdb>new Adb(),
49reactNative = <IReactNative>new ReactNative(),
50fileSystem = new FileSystem(),
51} = {}) {
299b0557Patricio Beltran10 years ago52super(runOptions, { remoteExtension: remoteExtension });
52f3873ddigeff10 years ago53this.adb = adb;
54this.reactNative = reactNative;
55this.fileSystem = fileSystem;
56}
57
58public runApp(shouldLaunchInAllDevices: boolean = false): Q.Promise<void> {
59return TelemetryHelper.generate("AndroidPlatform.runApp", () => {
de899f99Jimmy Thomson9 years ago60const runAndroidSpawn = this.reactNative.runAndroid(this.runOptions.projectRoot, this.runOptions.variant);
52f3873ddigeff10 years ago61const output = new OutputVerifier(
62() =>
63Q(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
64() =>
65Q(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS)).process(runAndroidSpawn);
66
67return output
68.finally(() => {
69return this.initializeTargetDevicesAndPackageName();
70}).then(() => [this.debugTarget], reason => {
71if (reason.message === AndroidPlatform.MULTIPLE_DEVICES_ERROR && this.devices.length > 1 && this.debugTarget) {
72/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
73this.needsToLaunchApps = true;
74return shouldLaunchInAllDevices
75? this.adb.getOnlineDevices()
76: Q([this.debugTarget]);
77} else {
78return Q.reject<IDevice[]>(reason);
79}
80}).then(devices => {
81return new PromiseUtil().forEach(devices, device => {
82return this.launchAppWithADBReverseAndLogCat(device);
83});
84});
85});
86}
87
88public enableJSDebuggingMode(): Q.Promise<void> {
89return this.adb.reloadAppInDebugMode(this.runOptions.projectRoot, this.packageName, this.debugTarget.id);
90}
91
299b0557Patricio Beltran10 years ago92public prewarmBundleCache(): Q.Promise<void> {
93return this.remoteExtension.prewarmBundleCache(this.platformName);
94}
95
52f3873ddigeff10 years ago96private initializeTargetDevicesAndPackageName(): Q.Promise<void> {
97return this.adb.getConnectedDevices().then(devices => {
98this.devices = devices;
99this.debugTarget = this.getTargetEmulator(devices);
100return this.getPackageName().then(packageName => {
101this.packageName = packageName;
102});
103});
104}
105
106private launchAppWithADBReverseAndLogCat(device: IDevice): Q.Promise<void> {
107return Q({})
108.then(() => {
109return this.configureADBReverseWhenApplicable(device);
110}).then(() => {
111return this.needsToLaunchApps
112? this.adb.launchApp(this.runOptions.projectRoot, this.packageName, device.id)
113: Q<void>(void 0);
114}).then(() => {
115return this.startMonitoringLogCat(device, this.runOptions.logCatArguments).catch(error => // The LogCatMonitor failing won't stop the debugging experience
116Log.logWarning("Couldn't start LogCat monitor", error));
117});
118}
119
120private configureADBReverseWhenApplicable(device: IDevice): Q.Promise<void> {
121if (device.type !== DeviceType.AndroidSdkEmulator) {
122return Q({}) // For other emulators and devices we try to enable adb reverse
123.then(() => this.adb.apiVersion(device.id))
124.then(apiVersion => {
125if (apiVersion >= AndroidAPILevel.LOLLIPOP) { // If we support adb reverse
126return this.adb.reverseAdd(device.id, Packager.DEFAULT_PORT.toString(), this.runOptions.packagerPort);
127} else {
128Log.logWarning(`Device ${device.id} supports only API Level ${apiVersion}. `
129+ `Level ${AndroidAPILevel.LOLLIPOP} is needed to support port forwarding via adb reverse. `
130+ "For debugging to work you'll need <Shake or press menu button> for the dev menu, "
131+ "go into <Dev Settings> and configure <Debug Server host & port for Device> to be "
132+ "an IP address of your computer that the Device can reach. More info at: "
133+ "https://facebook.github.io/react-native/docs/debugging.html#debugging-react-native-apps");
134}
135});
136} else {
137return Q<void>(void 0); // Android SDK emulators can connect directly to 10.0.0.2, so they don't need port forwarding
138}
139}
140
141private getPackageName(): Q.Promise<string> {
142return new Package(this.runOptions.projectRoot, { fileSystem: this.fileSystem }).name().then(appName =>
143new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot));
144}
145
146/**
147* Returns the target emulator, using the following logic:
148* * If an emulator is specified and it is connected, use that one.
149* * Otherwise, use the first one in the list.
150*/
151private getTargetEmulator(devices: IDevice[]): IDevice {
152let activeFilterFunction = (device: IDevice) => {
153return device.isOnline;
154};
155
156let targetFilterFunction = (device: IDevice) => {
157return device.id === this.runOptions.target && activeFilterFunction(device);
158};
159
160if (this.runOptions && this.runOptions.target && devices) {
161/* check if the specified target is active */
162const targetDevice = devices.find(targetFilterFunction);
163if (targetDevice) {
164return targetDevice;
165}
166}
167
168/* return the first active device in the list */
169let activeDevices = devices && devices.filter(activeFilterFunction);
170return activeDevices && activeDevices[0];
171}
172
173private startMonitoringLogCat(device: IDevice, logCatArguments: string): Q.Promise<void> {
174return this.remoteExtension.startMonitoringLogcat(device.id, logCatArguments);
175}
176}