microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.0.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/androidPlatform.ts

282lines · 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
df8c800dArtem Egorov8 years ago4import * as semver from "semver";
52f3873ddigeff10 years ago5
5c8365a6Artem Egorov8 years ago6import {GeneralMobilePlatform, MobilePlatformDeps } from "../generalMobilePlatform";
259c018fYuri Skorokhodov5 years ago7import {IAndroidRunOptions, PlatformType} from "../launchArgs";
7daed3fcArtem Egorov8 years ago8import {AdbHelper, AndroidAPILevel, IDevice} from "./adb";
0a68f8dbArtem Egorov8 years ago9import {Package} from "../../common/node/package";
5c8365a6Artem Egorov8 years ago10import {PackageNameResolver} from "./packageNameResolver";
0a68f8dbArtem Egorov8 years ago11import {OutputVerifier, PatternToFailure} from "../../common/outputVerifier";
12import {TelemetryHelper} from "../../common/telemetryHelper";
8022afdfVladimir Kotikov8 years ago13import {CommandExecutor} from "../../common/commandExecutor";
0a68f8dbArtem Egorov8 years ago14import {LogCatMonitor} from "./logCatMonitor";
d7d405aeYuri Skorokhodov7 years ago15import * as nls from "vscode-nls";
16import { InternalErrorCode } from "../../common/error/internalErrorCode";
17import { ErrorHelper } from "../../common/error/errorHelper";
78c2b4deRedMickey6 years ago18import { isNullOrUndefined } from "util";
ce5e88eeYuri Skorokhodov5 years ago19import { PromiseUtil } from "../../common/node/promise";
68a5b8d5JiglioNero5 years ago20import { AndroidEmulatorManager, IAndroidEmulator } from "./androidEmulatorManager";
2d8af448Yuri Skorokhodov6 years ago21nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
d7d405aeYuri Skorokhodov7 years ago22const localize = nls.loadMessageBundle();
5c8365a6Artem Egorov8 years ago23
52f3873ddigeff10 years ago24/**
25* Android specific platform implementation for debugging RN applications.
26*/
299b0557Patricio Beltran10 years ago27export class AndroidPlatform extends GeneralMobilePlatform {
52f3873ddigeff10 years ago28
0a68f8dbArtem Egorov8 years ago29// We should add the common Android build/run errors we find to this list
ef902673Vladimir Kotikov9 years ago30private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure[] = [{
31pattern: "Failed to install on any devices",
d7d405aeYuri Skorokhodov7 years ago32errorCode: InternalErrorCode.AndroidCouldNotInstallTheAppOnAnyAvailibleDevice,
ef902673Vladimir Kotikov9 years ago33}, {
34pattern: "com.android.ddmlib.ShellCommandUnresponsiveException",
d7d405aeYuri Skorokhodov7 years ago35errorCode: InternalErrorCode.AndroidShellCommandTimedOut,
ef902673Vladimir Kotikov9 years ago36}, {
37pattern: "Android project not found",
d7d405aeYuri Skorokhodov7 years ago38errorCode: InternalErrorCode.AndroidProjectNotFound,
ef902673Vladimir Kotikov9 years ago39
40}, {
41pattern: "error: more than one device/emulator",
d7d405aeYuri Skorokhodov7 years ago42errorCode: InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator,
ef902673Vladimir Kotikov9 years ago43}, {
44pattern: /^Error: Activity class \{.*\} does not exist\.$/m,
d7d405aeYuri Skorokhodov7 years ago45errorCode: InternalErrorCode.AndroidFailedToLaunchTheSpecifiedActivity,
ef902673Vladimir Kotikov9 years ago46}];
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;
0a68f8dbArtem Egorov8 years ago53private logCatMonitor: LogCatMonitor | null = null;
db6fd42aRuslan Bikkinin7 years ago54private adbHelper: AdbHelper;
68a5b8d5JiglioNero5 years ago55private emulatorManager: AndroidEmulatorManager;
52f3873ddigeff10 years ago56
57private needsToLaunchApps: boolean = false;
db6fd42aRuslan Bikkinin7 years ago58
ce5e88eeYuri Skorokhodov5 years ago59public showDevMenu(deviceId?: string): Promise<void> {
db6fd42aRuslan Bikkinin7 years ago60return this.adbHelper.showDevMenu(deviceId);
7daed3fcArtem Egorov8 years ago61}
db6fd42aRuslan Bikkinin7 years ago62
ce5e88eeYuri Skorokhodov5 years ago63public reloadApp(deviceId?: string): Promise<void> {
db6fd42aRuslan Bikkinin7 years ago64return this.adbHelper.reloadApp(deviceId);
7daed3fcArtem Egorov8 years ago65}
52f3873ddigeff10 years ago66
299b0557Patricio Beltran10 years ago67// 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 ago68constructor(protected runOptions: IAndroidRunOptions, platformDeps: MobilePlatformDeps = {}) {
0a68f8dbArtem Egorov8 years ago69super(runOptions, platformDeps);
db6fd42aRuslan Bikkinin7 years ago70this.adbHelper = new AdbHelper(this.runOptions.projectRoot, this.logger);
68a5b8d5JiglioNero5 years ago71this.emulatorManager = new AndroidEmulatorManager(this.adbHelper);
db6fd42aRuslan Bikkinin7 years ago72}
43e1a996Ruslan Bikkinin7 years ago73
db6fd42aRuslan Bikkinin7 years ago74// TODO: remove this method when sinon will be updated to upper version. Now it is used for tests only.
75public setAdbHelper(adbHelper: AdbHelper) {
76this.adbHelper = adbHelper;
52f3873ddigeff10 years ago77}
78
68a5b8d5JiglioNero5 years ago79public resolveVirtualDevice(target: string): Promise<IAndroidEmulator | null> {
80if (!target.includes("device")) {
81return this.emulatorManager.startEmulator(target)
82.then((emulator: IAndroidEmulator | null) => {
83if (emulator) {
84GeneralMobilePlatform.setRunArgument(this.runArguments, "--deviceId", emulator.id);
85}
86return emulator;
87});
88}
89else {
90return Promise.resolve(null);
91}
92}
93
ce5e88eeYuri Skorokhodov5 years ago94public runApp(shouldLaunchInAllDevices: boolean = false): Promise<void> {
549baae2RedMickey6 years ago95let extProps: any = {
031832ffArtem Egorov8 years ago96platform: {
259c018fYuri Skorokhodov5 years ago97value: PlatformType.Android,
031832ffArtem Egorov8 years ago98isPii: false,
99},
100};
101
549baae2RedMickey6 years ago102if (this.runOptions.isDirect) {
103extProps.isDirect = {
104value: true,
105isPii: false,
106};
107}
108
7fa90b3bRedMickey6 years ago109extProps = TelemetryHelper.addPropertyToTelemetryProperties(this.runOptions.reactNativeVersions.reactNativeVersion, "reactNativeVersion", extProps);
ba953e9fRedMickey6 years ago110
031832ffArtem Egorov8 years ago111return TelemetryHelper.generate("AndroidPlatform.runApp", extProps, () => {
de838bbfJiglioNero6 years ago112const env = GeneralMobilePlatform.getEnvArgument(process.env, this.runOptions.env, this.runOptions.envFile);
78c2b4deRedMickey6 years ago113
e3706a1cRedMickey6 years ago114if (
115!semver.valid(this.runOptions.reactNativeVersions.reactNativeVersion) /*Custom RN implementations should support this flag*/ ||
116semver.gte(this.runOptions.reactNativeVersions.reactNativeVersion, AndroidPlatform.NO_PACKAGER_VERSION)
117) {
118this.runArguments.push("--no-packager");
119}
120
121let mainActivity = GeneralMobilePlatform.getOptFromRunArgs(this.runArguments, "--main-activity");
122
123if (mainActivity) {
124this.adbHelper.setLaunchActivity(mainActivity);
125} else if (!isNullOrUndefined(this.runOptions.debugLaunchActivity)) {
126this.runArguments.push("--main-activity", this.runOptions.debugLaunchActivity);
127this.adbHelper.setLaunchActivity(this.runOptions.debugLaunchActivity);
128}
129
130const runAndroidSpawn = new CommandExecutor(this.projectPath, this.logger).spawnReactCommand("run-android", this.runArguments, {env});
131const output = new OutputVerifier(
132() =>
ce5e88eeYuri Skorokhodov5 years ago133Promise.resolve(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
e3706a1cRedMickey6 years ago134() =>
ce5e88eeYuri Skorokhodov5 years ago135Promise.resolve(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS),
259c018fYuri Skorokhodov5 years ago136PlatformType.Android).process(runAndroidSpawn);
e3706a1cRedMickey6 years ago137
138return output
139.finally(() => {
140return this.initializeTargetDevicesAndPackageName();
141}).then(() => [this.debugTarget], reason => {
142if (reason.message === ErrorHelper.getInternalError(InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator).message && this.devices.length > 1 && this.debugTarget) {
143/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
144this.needsToLaunchApps = true;
145return shouldLaunchInAllDevices
146? this.adbHelper.getOnlineDevices()
ce5e88eeYuri Skorokhodov5 years ago147: Promise.resolve([this.debugTarget]);
e3706a1cRedMickey6 years ago148} else {
ce5e88eeYuri Skorokhodov5 years ago149return Promise.reject<IDevice[]>(reason);
78c2b4deRedMickey6 years ago150}
e3706a1cRedMickey6 years ago151}).then(devices => {
152return new PromiseUtil().forEach(devices, device => {
153return this.launchAppWithADBReverseAndLogCat(device);
154});
52f3873ddigeff10 years ago155});
156});
157}
158
ce5e88eeYuri Skorokhodov5 years ago159public enableJSDebuggingMode(): Promise<void> {
db6fd42aRuslan Bikkinin7 years ago160return this.adbHelper.switchDebugMode(this.runOptions.projectRoot, this.packageName, true, this.debugTarget.id);
b57ea017Artem Egorov8 years ago161}
162
ce5e88eeYuri Skorokhodov5 years ago163public disableJSDebuggingMode(): Promise<void> {
db6fd42aRuslan Bikkinin7 years ago164return this.adbHelper.switchDebugMode(this.runOptions.projectRoot, this.packageName, false, this.debugTarget.id);
52f3873ddigeff10 years ago165}
166
ce5e88eeYuri Skorokhodov5 years ago167public prewarmBundleCache(): Promise<void> {
259c018fYuri Skorokhodov5 years ago168return this.packager.prewarmBundleCache(PlatformType.Android);
299b0557Patricio Beltran10 years ago169}
170
cbc7ac5bArtem Egorov7 years ago171public getRunArguments(): string[] {
8022afdfVladimir Kotikov8 years ago172let runArguments: string[] = [];
173
174if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
175runArguments = this.runOptions.runArguments;
176} else {
177if (this.runOptions.variant) {
178runArguments.push("--variant", this.runOptions.variant);
179}
180if (this.runOptions.target) {
cbc7ac5bArtem Egorov7 years ago181if (this.runOptions.target === AndroidPlatform.simulatorString ||
182this.runOptions.target === AndroidPlatform.deviceString) {
183
d7d405aeYuri Skorokhodov7 years ago184const message = localize("TargetIsNotSupportedForAndroid",
de838bbfJiglioNero6 years ago185"Target {0} is not supported for Android platform. \n If you want to use particular device or simulator for launching Android app,\n please specify device id (as in 'adb devices' output) instead.",
d7d405aeYuri Skorokhodov7 years ago186this.runOptions.target);
cbc7ac5bArtem Egorov7 years ago187this.logger.warning(message);
188} else {
189runArguments.push("--deviceId", this.runOptions.target);
190}
8022afdfVladimir Kotikov8 years ago191}
192}
193
194return runArguments;
195}
196
ce5e88eeYuri Skorokhodov5 years ago197private initializeTargetDevicesAndPackageName(): Promise<void> {
db6fd42aRuslan Bikkinin7 years ago198return this.adbHelper.getConnectedDevices().then(devices => {
52f3873ddigeff10 years ago199this.devices = devices;
200this.debugTarget = this.getTargetEmulator(devices);
201return this.getPackageName().then(packageName => {
202this.packageName = packageName;
203});
204});
205}
206
ce5e88eeYuri Skorokhodov5 years ago207private launchAppWithADBReverseAndLogCat(device: IDevice): Promise<void> {
208return this.configureADBReverseWhenApplicable(device)
52f3873ddigeff10 years ago209.then(() => {
210return this.needsToLaunchApps
db6fd42aRuslan Bikkinin7 years ago211? this.adbHelper.launchApp(this.runOptions.projectRoot, this.packageName, device.id)
ce5e88eeYuri Skorokhodov5 years ago212: Promise.resolve();
213})
214.then(() => {
0a68f8dbArtem Egorov8 years ago215return this.startMonitoringLogCat(device, this.runOptions.logCatArguments);
52f3873ddigeff10 years ago216});
217}
218
ce5e88eeYuri Skorokhodov5 years ago219private configureADBReverseWhenApplicable(device: IDevice): Promise<void> {
220return Promise.resolve()// For other emulators and devices we try to enable adb reverse
db6fd42aRuslan Bikkinin7 years ago221.then(() => this.adbHelper.apiVersion(device.id))
b57ea017Artem Egorov8 years ago222.then(apiVersion => {
223if (apiVersion >= AndroidAPILevel.LOLLIPOP) { // If we support adb reverse
db6fd42aRuslan Bikkinin7 years ago224return this.adbHelper.reverseAdb(device.id, Number(this.runOptions.packagerPort));
b57ea017Artem Egorov8 years ago225} else {
d7d405aeYuri Skorokhodov7 years ago226const message = localize("DeviceSupportsOnlyAPILevel",
227"Device {0} supports only API Level {1}. \n Level {2} is needed to support port forwarding via adb reverse. \n For debugging to work you'll need <Shake or press menu button> for the dev menu, \n go into <Dev Settings> and configure <Debug Server host & port for Device> to be \n an IP address of your computer that the Device can reach. More info at: \n https://facebook.github.io/react-native/docs/debugging.html#debugging-react-native-apps",
228device.id, apiVersion, AndroidAPILevel.LOLLIPOP);
229this.logger.warning(message);
b57ea017Artem Egorov8 years ago230return void 0;
231}
232});
52f3873ddigeff10 years ago233}
234
ce5e88eeYuri Skorokhodov5 years ago235private getPackageName(): Promise<string> {
8022afdfVladimir Kotikov8 years ago236return new Package(this.runOptions.projectRoot).name().then(appName =>
52f3873ddigeff10 years ago237new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot));
238}
239
240/**
241* Returns the target emulator, using the following logic:
242* * If an emulator is specified and it is connected, use that one.
243* * Otherwise, use the first one in the list.
244*/
245private getTargetEmulator(devices: IDevice[]): IDevice {
246let activeFilterFunction = (device: IDevice) => {
247return device.isOnline;
248};
249
250let targetFilterFunction = (device: IDevice) => {
251return device.id === this.runOptions.target && activeFilterFunction(device);
252};
253
254if (this.runOptions && this.runOptions.target && devices) {
255/* check if the specified target is active */
256const targetDevice = devices.find(targetFilterFunction);
257if (targetDevice) {
258return targetDevice;
259}
260}
261
262/* return the first active device in the list */
263let activeDevices = devices && devices.filter(activeFilterFunction);
264return activeDevices && activeDevices[0];
265}
266
0a68f8dbArtem Egorov8 years ago267private startMonitoringLogCat(device: IDevice, logCatArguments: string): void {
268this.stopMonitoringLogCat(); // Stop previous logcat monitor if it's running
269
270// this.logCatMonitor can be mutated, so we store it locally too
db6fd42aRuslan Bikkinin7 years ago271this.logCatMonitor = new LogCatMonitor(device.id, logCatArguments, this.adbHelper);
0a68f8dbArtem Egorov8 years ago272this.logCatMonitor.start() // The LogCat will continue running forever, so we don't wait for it
ce5e88eeYuri Skorokhodov5 years ago273.catch(error => this.logger.warning(localize("ErrorWhileMonitoringLogCat", "Error while monitoring LogCat"), error)); // The LogCatMonitor failing won't stop the debugging experience
0a68f8dbArtem Egorov8 years ago274}
275
276private stopMonitoringLogCat(): void {
277if (this.logCatMonitor) {
278this.logCatMonitor.dispose();
279this.logCatMonitor = null;
280}
52f3873ddigeff10 years ago281}
ef902673Vladimir Kotikov9 years ago282}