microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.3.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/androidPlatform.ts

363lines · 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
8df5011eYuri Skorokhodov5 years ago6import { GeneralMobilePlatform, MobilePlatformDeps } from "../generalMobilePlatform";
7import { IAndroidRunOptions, PlatformType } from "../launchArgs";
8import { AdbHelper, AndroidAPILevel, IDevice } from "./adb";
9import { Package } from "../../common/node/package";
10import { PackageNameResolver } from "./packageNameResolver";
11import { OutputVerifier, PatternToFailure } from "../../common/outputVerifier";
12import { TelemetryHelper } from "../../common/telemetryHelper";
13import { CommandExecutor } from "../../common/commandExecutor";
14import { 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";
8df5011eYuri Skorokhodov5 years ago21import { LogCatMonitorManager } from "./logCatMonitorManager";
34472878RedMickey5 years ago22nls.config({
23messageFormat: nls.MessageFormat.bundle,
24bundleFormat: nls.BundleFormat.standalone,
25})();
d7d405aeYuri Skorokhodov7 years ago26const localize = nls.loadMessageBundle();
5c8365a6Artem Egorov8 years ago27
52f3873ddigeff10 years ago28/**
29* Android specific platform implementation for debugging RN applications.
30*/
299b0557Patricio Beltran10 years ago31export class AndroidPlatform extends GeneralMobilePlatform {
0a68f8dbArtem Egorov8 years ago32// We should add the common Android build/run errors we find to this list
34472878RedMickey5 years ago33private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure[] = [
34{
35pattern: "Failed to install on any devices",
36errorCode: InternalErrorCode.AndroidCouldNotInstallTheAppOnAnyAvailibleDevice,
37},
38{
39pattern: "com.android.ddmlib.ShellCommandUnresponsiveException",
40errorCode: InternalErrorCode.AndroidShellCommandTimedOut,
41},
42{
43pattern: "Android project not found",
44errorCode: InternalErrorCode.AndroidProjectNotFound,
45},
46{
47pattern: "error: more than one device/emulator",
48errorCode: InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator,
49},
50{
51pattern: /^Error: Activity class \{.*\} does not exist\.$/m,
52errorCode: InternalErrorCode.AndroidFailedToLaunchTheSpecifiedActivity,
53},
54];
55
56private static RUN_ANDROID_SUCCESS_PATTERNS: string[] = [
57"BUILD SUCCESSFUL",
58"Starting the app",
59"Starting: Intent",
60];
52f3873ddigeff10 years ago61
62private debugTarget: IDevice;
63private devices: IDevice[];
64private packageName: string;
db6fd42aRuslan Bikkinin7 years ago65private adbHelper: AdbHelper;
68a5b8d5JiglioNero5 years ago66private emulatorManager: AndroidEmulatorManager;
8df5011eYuri Skorokhodov5 years ago67private logCatMonitor: LogCatMonitor | null = null;
52f3873ddigeff10 years ago68
69private needsToLaunchApps: boolean = false;
db6fd42aRuslan Bikkinin7 years ago70
ce5e88eeYuri Skorokhodov5 years ago71public showDevMenu(deviceId?: string): Promise<void> {
db6fd42aRuslan Bikkinin7 years ago72return this.adbHelper.showDevMenu(deviceId);
7daed3fcArtem Egorov8 years ago73}
db6fd42aRuslan Bikkinin7 years ago74
ce5e88eeYuri Skorokhodov5 years ago75public reloadApp(deviceId?: string): Promise<void> {
db6fd42aRuslan Bikkinin7 years ago76return this.adbHelper.reloadApp(deviceId);
7daed3fcArtem Egorov8 years ago77}
52f3873ddigeff10 years ago78
299b0557Patricio Beltran10 years ago79// 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 ago80constructor(protected runOptions: IAndroidRunOptions, platformDeps: MobilePlatformDeps = {}) {
0a68f8dbArtem Egorov8 years ago81super(runOptions, platformDeps);
db6fd42aRuslan Bikkinin7 years ago82this.adbHelper = new AdbHelper(this.runOptions.projectRoot, this.logger);
68a5b8d5JiglioNero5 years ago83this.emulatorManager = new AndroidEmulatorManager(this.adbHelper);
db6fd42aRuslan Bikkinin7 years ago84}
43e1a996Ruslan Bikkinin7 years ago85
db6fd42aRuslan Bikkinin7 years ago86// TODO: remove this method when sinon will be updated to upper version. Now it is used for tests only.
8df5011eYuri Skorokhodov5 years ago87public setAdbHelper(adbHelper: AdbHelper): void {
db6fd42aRuslan Bikkinin7 years ago88this.adbHelper = adbHelper;
52f3873ddigeff10 years ago89}
90
68a5b8d5JiglioNero5 years ago91public resolveVirtualDevice(target: string): Promise<IAndroidEmulator | null> {
92if (!target.includes("device")) {
34472878RedMickey5 years ago93return this.emulatorManager
94.startEmulator(target)
8df5011eYuri Skorokhodov5 years ago95.then((emulator: IAndroidEmulator | null) => {
96if (emulator) {
34472878RedMickey5 years ago97GeneralMobilePlatform.setRunArgument(
98this.runArguments,
99"--deviceId",
100emulator.id,
101);
8df5011eYuri Skorokhodov5 years ago102}
103return emulator;
104});
34472878RedMickey5 years ago105} else {
68a5b8d5JiglioNero5 years ago106return Promise.resolve(null);
107}
108}
109
ce5e88eeYuri Skorokhodov5 years ago110public runApp(shouldLaunchInAllDevices: boolean = false): Promise<void> {
549baae2RedMickey6 years ago111let extProps: any = {
031832ffArtem Egorov8 years ago112platform: {
259c018fYuri Skorokhodov5 years ago113value: PlatformType.Android,
031832ffArtem Egorov8 years ago114isPii: false,
115},
116};
117
549baae2RedMickey6 years ago118if (this.runOptions.isDirect) {
119extProps.isDirect = {
120value: true,
121isPii: false,
122};
123}
124
34472878RedMickey5 years ago125extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
126this.runOptions,
127this.runOptions.reactNativeVersions,
128extProps,
129);
ba953e9fRedMickey6 years ago130
031832ffArtem Egorov8 years ago131return TelemetryHelper.generate("AndroidPlatform.runApp", extProps, () => {
34472878RedMickey5 years ago132const env = GeneralMobilePlatform.getEnvArgument(
133process.env,
134this.runOptions.env,
135this.runOptions.envFile,
136);
78c2b4deRedMickey6 years ago137
e3706a1cRedMickey6 years ago138if (
34472878RedMickey5 years ago139!semver.valid(
140this.runOptions.reactNativeVersions.reactNativeVersion,
141) /*Custom RN implementations should support this flag*/ ||
142semver.gte(
143this.runOptions.reactNativeVersions.reactNativeVersion,
144AndroidPlatform.NO_PACKAGER_VERSION,
145)
e3706a1cRedMickey6 years ago146) {
147this.runArguments.push("--no-packager");
148}
149
34472878RedMickey5 years ago150let mainActivity = GeneralMobilePlatform.getOptFromRunArgs(
151this.runArguments,
152"--main-activity",
153);
e3706a1cRedMickey6 years ago154
155if (mainActivity) {
156this.adbHelper.setLaunchActivity(mainActivity);
157} else if (!isNullOrUndefined(this.runOptions.debugLaunchActivity)) {
158this.runArguments.push("--main-activity", this.runOptions.debugLaunchActivity);
159this.adbHelper.setLaunchActivity(this.runOptions.debugLaunchActivity);
160}
161
34472878RedMickey5 years ago162const runAndroidSpawn = new CommandExecutor(
163this.projectPath,
164this.logger,
165).spawnReactCommand("run-android", this.runArguments, { env });
e3706a1cRedMickey6 years ago166const output = new OutputVerifier(
34472878RedMickey5 years ago167() => Promise.resolve(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
168() => Promise.resolve(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS),
169PlatformType.Android,
170).process(runAndroidSpawn);
e3706a1cRedMickey6 years ago171
172return output
173.finally(() => {
174return this.initializeTargetDevicesAndPackageName();
34472878RedMickey5 years ago175})
176.then(
177() => [this.debugTarget],
178reason => {
179if (
180reason.message ===
181ErrorHelper.getInternalError(
182InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator,
183).message &&
184this.devices.length > 1 &&
185this.debugTarget
186) {
187/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
188this.needsToLaunchApps = true;
189return shouldLaunchInAllDevices
190? this.adbHelper.getOnlineDevices()
191: Promise.resolve([this.debugTarget]);
192} else {
193return Promise.reject<IDevice[]>(reason);
194}
195},
196)
197.then(devices => {
e3706a1cRedMickey6 years ago198return new PromiseUtil().forEach(devices, device => {
199return this.launchAppWithADBReverseAndLogCat(device);
200});
52f3873ddigeff10 years ago201});
202});
203}
204
ce5e88eeYuri Skorokhodov5 years ago205public enableJSDebuggingMode(): Promise<void> {
34472878RedMickey5 years ago206return this.adbHelper.switchDebugMode(
207this.runOptions.projectRoot,
208this.packageName,
209true,
210this.debugTarget.id,
211);
b57ea017Artem Egorov8 years ago212}
213
ce5e88eeYuri Skorokhodov5 years ago214public disableJSDebuggingMode(): Promise<void> {
34472878RedMickey5 years ago215return this.adbHelper.switchDebugMode(
216this.runOptions.projectRoot,
217this.packageName,
218false,
219this.debugTarget.id,
220);
52f3873ddigeff10 years ago221}
222
ce5e88eeYuri Skorokhodov5 years ago223public prewarmBundleCache(): Promise<void> {
259c018fYuri Skorokhodov5 years ago224return this.packager.prewarmBundleCache(PlatformType.Android);
299b0557Patricio Beltran10 years ago225}
226
cbc7ac5bArtem Egorov7 years ago227public getRunArguments(): string[] {
8022afdfVladimir Kotikov8 years ago228let runArguments: string[] = [];
229
8df5011eYuri Skorokhodov5 years ago230if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
8022afdfVladimir Kotikov8 years ago231runArguments = this.runOptions.runArguments;
232} else {
233if (this.runOptions.variant) {
234runArguments.push("--variant", this.runOptions.variant);
235}
236if (this.runOptions.target) {
34472878RedMickey5 years ago237if (
238this.runOptions.target === AndroidPlatform.simulatorString ||
239this.runOptions.target === AndroidPlatform.deviceString
240) {
241const message = localize(
242"TargetIsNotSupportedForAndroid",
8df5011eYuri Skorokhodov5 years ago243"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.",
34472878RedMickey5 years ago244this.runOptions.target,
245);
cbc7ac5bArtem Egorov7 years ago246this.logger.warning(message);
247} else {
248runArguments.push("--deviceId", this.runOptions.target);
249}
8022afdfVladimir Kotikov8 years ago250}
251}
252
253return runArguments;
254}
255
34472878RedMickey5 years ago256public dispose(): void {
8df5011eYuri Skorokhodov5 years ago257if (this.logCatMonitor) {
258LogCatMonitorManager.delMonitor(this.logCatMonitor.deviceId);
259this.logCatMonitor = null;
260}
261}
262
ce5e88eeYuri Skorokhodov5 years ago263private initializeTargetDevicesAndPackageName(): Promise<void> {
db6fd42aRuslan Bikkinin7 years ago264return this.adbHelper.getConnectedDevices().then(devices => {
52f3873ddigeff10 years ago265this.devices = devices;
266this.debugTarget = this.getTargetEmulator(devices);
267return this.getPackageName().then(packageName => {
268this.packageName = packageName;
269});
270});
271}
272
ce5e88eeYuri Skorokhodov5 years ago273private launchAppWithADBReverseAndLogCat(device: IDevice): Promise<void> {
8df5011eYuri Skorokhodov5 years ago274return this.configureADBReverseWhenApplicable(device)
52f3873ddigeff10 years ago275.then(() => {
276return this.needsToLaunchApps
34472878RedMickey5 years ago277? this.adbHelper.launchApp(
278this.runOptions.projectRoot,
279this.packageName,
280device.id,
281)
ce5e88eeYuri Skorokhodov5 years ago282: Promise.resolve();
283})
284.then(() => {
0a68f8dbArtem Egorov8 years ago285return this.startMonitoringLogCat(device, this.runOptions.logCatArguments);
52f3873ddigeff10 years ago286});
287}
288
ce5e88eeYuri Skorokhodov5 years ago289private configureADBReverseWhenApplicable(device: IDevice): Promise<void> {
34472878RedMickey5 years ago290return Promise.resolve() // For other emulators and devices we try to enable adb reverse
db6fd42aRuslan Bikkinin7 years ago291.then(() => this.adbHelper.apiVersion(device.id))
b57ea017Artem Egorov8 years ago292.then(apiVersion => {
34472878RedMickey5 years ago293if (apiVersion >= AndroidAPILevel.LOLLIPOP) {
294// If we support adb reverse
295return this.adbHelper.reverseAdb(
296device.id,
297Number(this.runOptions.packagerPort),
298);
b57ea017Artem Egorov8 years ago299} else {
34472878RedMickey5 years ago300const message = localize(
301"DeviceSupportsOnlyAPILevel",
8df5011eYuri Skorokhodov5 years ago302"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",
34472878RedMickey5 years ago303device.id,
304apiVersion,
305AndroidAPILevel.LOLLIPOP,
306);
d7d405aeYuri Skorokhodov7 years ago307this.logger.warning(message);
b57ea017Artem Egorov8 years ago308return void 0;
309}
310});
52f3873ddigeff10 years ago311}
312
ce5e88eeYuri Skorokhodov5 years ago313private getPackageName(): Promise<string> {
34472878RedMickey5 years ago314return new Package(this.runOptions.projectRoot)
315.name()
316.then(appName =>
317new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot),
318);
52f3873ddigeff10 years ago319}
320
321/**
322* Returns the target emulator, using the following logic:
323* * If an emulator is specified and it is connected, use that one.
324* * Otherwise, use the first one in the list.
325*/
326private getTargetEmulator(devices: IDevice[]): IDevice {
327let activeFilterFunction = (device: IDevice) => {
328return device.isOnline;
329};
330
331let targetFilterFunction = (device: IDevice) => {
332return device.id === this.runOptions.target && activeFilterFunction(device);
333};
334
335if (this.runOptions && this.runOptions.target && devices) {
336/* check if the specified target is active */
337const targetDevice = devices.find(targetFilterFunction);
338if (targetDevice) {
339return targetDevice;
340}
341}
342
343/* return the first active device in the list */
344let activeDevices = devices && devices.filter(activeFilterFunction);
345return activeDevices && activeDevices[0];
346}
347
8df5011eYuri Skorokhodov5 years ago348private startMonitoringLogCat(device: IDevice, logCatArguments: string[]): void {
349LogCatMonitorManager.delMonitor(device.id); // Stop previous logcat monitor if it's running
0a68f8dbArtem Egorov8 years ago350
351// this.logCatMonitor can be mutated, so we store it locally too
8df5011eYuri Skorokhodov5 years ago352this.logCatMonitor = new LogCatMonitor(device.id, this.adbHelper, logCatArguments);
353LogCatMonitorManager.addMonitor(this.logCatMonitor);
34472878RedMickey5 years ago354this.logCatMonitor
355.start() // The LogCat will continue running forever, so we don't wait for it
356.catch(error => {
357this.logger.warning(error);
358this.logger.warning(
359localize("ErrorWhileMonitoringLogCat", "Error while monitoring LogCat"),
360);
361}); // The LogCatMonitor failing won't stop the debugging experience
0a68f8dbArtem Egorov8 years ago362}
ef902673Vladimir Kotikov9 years ago363}