// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
import * as Q from "q";
import {IAppPlatform} from "../platformResolver";
import {ExtensionMessageSender, ExtensionMessage} from "../../common/extensionMessaging";
import {IRunOptions} from "../../common/launchArgs";
import {Log} from "../../common/log/log";
import {PackageNameResolver} from "../../common/android/packageNameResolver";
import {OutputVerifier, PatternToFailure} from "../../common/outputVerifier";
import {IDeviceHelper, DeviceHelper, IDevice} from "../../common/android/deviceHelper";
import {Package} from "../../common/node/package";
import {FileSystem} from "../../common/node/fileSystem";
import {IReactNative, ReactNative} from "../../common/reactNative";
/**
* Android specific platform implementation for debugging RN applications.
*/
export class AndroidPlatform implements IAppPlatform {
private extensionMessageSender: ExtensionMessageSender;
private static MULTIPLE_DEVICES_ERROR = "error: more than one device/emulator";
// We should add the common Android build/run erros we find to this list
private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure = {
"Failed to install on any devices": "Could not install the app on any available device. Make sure you have a correctly"
+ " configured device or emulator running. See https://facebook.github.io/react-native/docs/android-setup.html",
"com.android.ddmlib.ShellCommandUnresponsiveException": "An Android shell command timed-out. Please retry the operation.",
"Android project not found": "Android project not found.",
"error: more than one device/emulator": AndroidPlatform.MULTIPLE_DEVICES_ERROR,
};
private static RUN_ANDROID_SUCCESS_PATTERNS: string[] = ["BUILD SUCCESSFUL", "Starting the app", "Starting: Intent"];
private debugTarget: string;
private devices: IDevice[];
private packageName: string;
private deviceHelper: IDeviceHelper;
private reactNative: IReactNative;
private fileSystem: FileSystem;
constructor({ extensionMessageSender = new ExtensionMessageSender(),
deviceHelper = <IDeviceHelper>new DeviceHelper(),
reactNative = <IReactNative>new ReactNative(),
fileSystem = new FileSystem(),
} = {}) {
this.extensionMessageSender = extensionMessageSender;
this.deviceHelper = deviceHelper;
this.reactNative = reactNative;
this.fileSystem = fileSystem;
}
public runApp(runOptions: IRunOptions): Q.Promise<void> {
const runAndroidSpawn = this.reactNative.runAndroid(runOptions.projectRoot);
const output = new OutputVerifier(
() =>
Q(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
() =>
Q(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS)).process(runAndroidSpawn);
return output
.finally(() => {
return this.deviceHelper.getConnectedDevices().then(devices => {
this.devices = devices;
this.debugTarget = this.getTargetEmulator(runOptions, devices);
return this.getPackageName(runOptions.projectRoot).then(packageName =>
this.packageName = packageName);
});
}).catch(reason => {
if (reason.message === AndroidPlatform.MULTIPLE_DEVICES_ERROR && this.devices.length > 1 && this.debugTarget) {
/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
return this.deviceHelper.launchApp(runOptions.projectRoot, this.packageName, this.debugTarget);
} else {
return Q.reject<void>(reason);
}
}).then(() =>
this.startMonitoringLogCat(runOptions.logCatArguments).catch(error => // The LogCatMonitor failing won't stop the debugging experience
Log.logWarning("Couldn't start LogCat monitor", error)));
}
public enableJSDebuggingMode(runOptions: IRunOptions): Q.Promise<void> {
return this.deviceHelper.reloadAppInDebugMode(runOptions.projectRoot, this.packageName, this.debugTarget);
}
private getPackageName(projectRoot: string): Q.Promise<string> {
return new Package(projectRoot, { fileSystem: this.fileSystem }).name().then(appName =>
new PackageNameResolver(appName).resolvePackageName(projectRoot));
}
/**
* Returns the target emulator, using the following logic:
* * If an emulator is specified and it is connected, use that one.
* * Otherwise, use the first one in the list.
*/
private getTargetEmulator(runOptions: IRunOptions, devices: IDevice[]): string {
let activeFilterFunction = (device: IDevice) => {
return device.isOnline;
};
let targetFilterFunction = (device: IDevice) => {
return device.id === runOptions.target && activeFilterFunction(device);
};
if (runOptions && runOptions.target && devices) {
/* check if the specified target is active */
if (devices.some(targetFilterFunction)) {
return runOptions.target;
}
}
/* return the first active device in the list */
let activeDevices = devices && devices.filter(activeFilterFunction);
return activeDevices && activeDevices[0] && activeDevices[0].id;
}
private startMonitoringLogCat(logCatArguments: string): Q.Promise<void> {
return this.extensionMessageSender.sendMessage(ExtensionMessage.START_MONITORING_LOGCAT, [this.debugTarget, logCatArguments]);
}
}microsoft/vscode-react-native
Publicmirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable
src/debugger/android/androidPlatform.ts
120lines · modepreview