Support for device picker during launch (#86)

This commit is contained in:
Dave Holoway
2020-04-23 16:44:19 +01:00
committed by GitHub
parent 44d887dd6c
commit 1535f133d9
5 changed files with 54 additions and 9 deletions

View File

@@ -4,6 +4,7 @@ const vscode = require('vscode');
const { AndroidContentProvider } = require('./src/contentprovider'); const { AndroidContentProvider } = require('./src/contentprovider');
const { openLogcatWindow } = require('./src/logcat'); const { openLogcatWindow } = require('./src/logcat');
const { selectAndroidProcessID } = require('./src/process-attach'); const { selectAndroidProcessID } = require('./src/process-attach');
const { selectTargetDevice } = require('./src/utils/device');
// this method is called when your extension is activated // this method is called when your extension is activated
// your extension is activated the very first time the command is executed // your extension is activated the very first time the command is executed
@@ -18,6 +19,12 @@ function activate(context) {
vscode.commands.registerCommand('android-dev-ext.view_logcat', () => { vscode.commands.registerCommand('android-dev-ext.view_logcat', () => {
openLogcatWindow(vscode); openLogcatWindow(vscode);
}), }),
// add the device picker handler - used to choose a target device
vscode.commands.registerCommand('PickAndroidDevice', async () => {
const device = await selectTargetDevice(vscode, "Launch", { alwaysShow:true });
// the debugger requires a string value to be returned
return JSON.stringify(device);
}),
// add the process picker handler - used to choose a PID to attach to // add the process picker handler - used to choose a PID to attach to
vscode.commands.registerCommand('PickAndroidProcess', async () => { vscode.commands.registerCommand('PickAndroidProcess', async () => {
const o = await selectAndroidProcessID(vscode); const o = await selectAndroidProcessID(vscode);

View File

@@ -19,6 +19,7 @@
}, },
"activationEvents": [ "activationEvents": [
"onCommand:android-dev-ext.view_logcat", "onCommand:android-dev-ext.view_logcat",
"onCommand:PickAndroidDevice",
"onCommand:PickAndroidProcess" "onCommand:PickAndroidProcess"
], ],
"repository": { "repository": {
@@ -121,7 +122,7 @@
"targetDevice": { "targetDevice": {
"type": "string", "type": "string",
"description": "Target Device ID (as indicated by 'adb devices'). Use this to specify which device is used for deployment when multiple devices are connected.", "description": "Target Device ID (as indicated by 'adb devices'). Use this to specify which device is used for deployment when multiple devices are connected.",
"default": "" "default": "${command:PickAndroidDevice}"
}, },
"trace": { "trace": {
"type": "boolean", "type": "boolean",
@@ -155,7 +156,7 @@
"targetDevice": { "targetDevice": {
"type": "string", "type": "string",
"description": "Target Device ID (as indicated by 'adb devices'). Use this to specify which device is used when multiple devices are connected.", "description": "Target Device ID (as indicated by 'adb devices'). Use this to specify which device is used when multiple devices are connected.",
"default": "" "default": "${command:PickAndroidDevice}"
}, },
"trace": { "trace": {
"type": "boolean", "type": "boolean",

View File

@@ -305,6 +305,19 @@ class AndroidDebugSession extends DebugSession {
} }
} }
extractTargetDeviceID(s) {
if (!s || typeof s !== 'string') {
return '';
}
// the device picker returns a stringified object
try {
const o = JSON.parse(s);
return o.serial || s;
} catch {
}
return s;
}
async attachRequest(response, args) { async attachRequest(response, args) {
this.debug_mode = 'attach'; this.debug_mode = 'attach';
if (args && args.trace) { if (args && args.trace) {
@@ -313,6 +326,14 @@ class AndroidDebugSession extends DebugSession {
} }
D(`Attach: ${JSON.stringify(args)}`); D(`Attach: ${JSON.stringify(args)}`);
if (args.targetDevice === 'null') {
// "null" is returned from the device picker if there's an error or if the
// user cancels.
D('targetDevice === "null"');
this.sendEvent(new TerminatedEvent(false));
return;
}
if (!args.processId) { if (!args.processId) {
this.LOG(`Attach failed: Missing "processId" property in launch.json`); this.LOG(`Attach failed: Missing "processId" property in launch.json`);
this.sendEvent(new TerminatedEvent(false)); this.sendEvent(new TerminatedEvent(false));
@@ -348,7 +369,7 @@ class AndroidDebugSession extends DebugSession {
try { try {
let { processId, targetDevice } = attach_info; let { processId, targetDevice } = attach_info;
if (!targetDevice) { if (!targetDevice) {
targetDevice = args.targetDevice; targetDevice = this.extractTargetDeviceID(args.targetDevice);
} }
// make sure ADB exists and is started and look for a connected device // make sure ADB exists and is started and look for a connected device
await checkADBStarted(args.autoStartADB !== false); await checkADBStarted(args.autoStartADB !== false);
@@ -416,6 +437,14 @@ class AndroidDebugSession extends DebugSession {
} }
D(`Launch: ${JSON.stringify(args)}`); D(`Launch: ${JSON.stringify(args)}`);
if (args.targetDevice === 'null') {
// "null" is returned from the device picker if there's an error or if the
// user cancels.
D('targetDevice === "null"');
this.sendEvent(new TerminatedEvent(false));
return;
}
// app_src_root must end in a path-separator for correct validation of sub-paths // app_src_root must end in a path-separator for correct validation of sub-paths
this.app_src_root = ensure_path_end_slash(args.appSrcRoot); this.app_src_root = ensure_path_end_slash(args.appSrcRoot);
this.apk_fpn = args.apkFile; this.apk_fpn = args.apkFile;
@@ -465,7 +494,8 @@ class AndroidDebugSession extends DebugSession {
// make sure ADB exists and is started and look for a device to install on // make sure ADB exists and is started and look for a device to install on
await checkADBStarted(args.autoStartADB !== false); await checkADBStarted(args.autoStartADB !== false);
this._device = await this.findSuitableDevice(args.targetDevice, true); const targetDevice = this.extractTargetDeviceID(args.targetDevice);
this._device = await this.findSuitableDevice(targetDevice, true);
this._device.adbclient = new ADBClient(this._device.serial); this._device.adbclient = new ADBClient(this._device.serial);
// install the APK we are going to debug // install the APK we are going to debug

View File

@@ -9,7 +9,7 @@ class BuildInfo {
* @param {string} pkgname * @param {string} pkgname
* @param {Map<string,PackageInfo>} packages * @param {Map<string,PackageInfo>} packages
* @param {string} launchActivity * @param {string} launchActivity
* @param {string[]} amCommandArgs custom arguments passed to `am start` * @param {string[]} [amCommandArgs] custom arguments passed to `am start`
*/ */
constructor(pkgname, packages, launchActivity, amCommandArgs) { constructor(pkgname, packages, launchActivity, amCommandArgs) {
this.pkgname = pkgname; this.pkgname = pkgname;

View File

@@ -26,9 +26,10 @@ async function showDevicePicker(vscode, devices) {
/** /**
* *
* @param {import('vscode')} vscode * @param {import('vscode')} vscode
* @param {'Attach'|'Logcat display'} action * @param {'Launch'|'Attach'|'Logcat display'} action
* @param {{alwaysShow:boolean}} [options]
*/ */
async function selectTargetDevice(vscode, action) { async function selectTargetDevice(vscode, action, options) {
const devices = await new ADBClient().list_devices(); const devices = await new ADBClient().list_devices();
let device; let device;
switch(devices.length) { switch(devices.length) {
@@ -36,14 +37,20 @@ async function selectTargetDevice(vscode, action) {
vscode.window.showWarningMessage(`${action} failed. No Android devices are connected.`); vscode.window.showWarningMessage(`${action} failed. No Android devices are connected.`);
return null; return null;
case 1: case 1:
if (!options || !options.alwaysShow) {
return devices[0]; // only one device - just use it return devices[0]; // only one device - just use it
} }
break;
}
device = await showDevicePicker(vscode, devices); device = await showDevicePicker(vscode, devices);
if (!device) {
return null; // user cancelled
}
// the user might take a while to choose the device, so once // the user might take a while to choose the device, so once
// chosen, recheck it exists // chosen, recheck it exists
const current_devices = await new ADBClient().list_devices(); const current_devices = await new ADBClient().list_devices();
if (!current_devices.find(d => d.serial === device.serial)) { if (!current_devices.find(d => d.serial === device.serial)) {
vscode.window.showInformationMessage(`${action} failed. The target device is disconnected`); vscode.window.showInformationMessage(`${action} failed. The target device is disconnected.`);
return null; return null;
} }
return device; return device;