mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-22 17:39:19 +00:00
support custom launch arguments (#84)
* add amStartArgs launch property to allow launch command arguments to be customised. * update readme launch options docs
This commit is contained in:
48
README.md
48
README.md
@@ -34,31 +34,61 @@ The following settings are used to configure the debugger:
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
// configuration type, request and name. "launch" is used to deploy the app to your device and start a debugging session
|
// configuration type, request and name. "launch" is used to deploy the app
|
||||||
|
// to your device and start a debugging session.
|
||||||
"type": "android",
|
"type": "android",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Launch App",
|
"name": "Launch App",
|
||||||
|
|
||||||
// Location of the App source files. This value must point to the root of your App source tree (containing AndroidManifest.xml)
|
// Location of the App source files. This value must point to the root of
|
||||||
|
// your App source tree (containing AndroidManifest.xml).
|
||||||
"appSrcRoot": "${workspaceRoot}/app/src/main",
|
"appSrcRoot": "${workspaceRoot}/app/src/main",
|
||||||
|
|
||||||
// Fully qualified path to the built APK (Android Application Package)
|
// Fully qualified path to the built APK (Android Application Package).
|
||||||
"apkFile": "${workspaceRoot}/app/build/outputs/apk/app-debug.apk",
|
"apkFile": "${workspaceRoot}/app/build/outputs/apk/app-debug.apk",
|
||||||
|
|
||||||
// Port number to connect to the local ADB (Android Debug Bridge) instance. Default: 5037
|
// Port number to connect to the local ADB (Android Debug Bridge) instance.
|
||||||
|
// Default: 5037
|
||||||
"adbPort": 5037,
|
"adbPort": 5037,
|
||||||
|
|
||||||
// Launch behaviour if source files have been saved after the APK was built. One of: [ ignore warn stop ]. Default: warn
|
// Automatically launch 'adb start-server' if not already started.
|
||||||
|
// Default: true
|
||||||
|
"autoStartADB": true,
|
||||||
|
|
||||||
|
// Launch behaviour if source files have been saved after the APK was built.
|
||||||
|
// One of: [ ignore warn stop ]. Default: warn
|
||||||
"staleBuild": "warn",
|
"staleBuild": "warn",
|
||||||
|
|
||||||
// Fully qualified path to the AndroidManifest.xml file compiled in the APK. Default: appSrcRoot/AndroidManifest.xml
|
// Target Device ID (as indicated by 'adb devices').
|
||||||
|
// Use this to specify which device is used for deployment
|
||||||
|
// when multiple devices are connected.
|
||||||
|
"targetDevice": "",
|
||||||
|
|
||||||
|
// Fully qualified path to the AndroidManifest.xml file compiled into the APK.
|
||||||
|
// Default: "${appSrcRoot}/AndroidManifest.xml"
|
||||||
"manifestFile": "${workspaceRoot}/app/src/main/AndroidManifest.xml",
|
"manifestFile": "${workspaceRoot}/app/src/main/AndroidManifest.xml",
|
||||||
|
|
||||||
// APK install arguments passed to the Android package manager. Run 'adb shell pm' to show valid arguments. Default: ["-r"]
|
// Custom arguments passed to the Android package manager to install the app.
|
||||||
|
// Run 'adb shell pm' to show valid arguments. Default: ["-r"]
|
||||||
"pmInstallArgs": ["-r"],
|
"pmInstallArgs": ["-r"],
|
||||||
|
|
||||||
// Manually specify the activity to run when the app is started.
|
// Custom arguments passed to the Android application manager to start the app.
|
||||||
"launchActivity": ".MainActivity"
|
// Run `adb shell am` to show valid arguments.
|
||||||
|
// Note that `-D` is required to enable debugging.
|
||||||
|
"amStartArgs": [
|
||||||
|
"-D",
|
||||||
|
"--activity-brought-to-front",
|
||||||
|
"-a android.intent.action.MAIN",
|
||||||
|
"-c android.intent.category.LAUNCHER",
|
||||||
|
"-n package.name/launch.activity"
|
||||||
|
],
|
||||||
|
|
||||||
|
// Manually specify the activity to run when the app is started. This option is
|
||||||
|
// mutually exclusive with "amStartArgs".
|
||||||
|
"launchActivity": ".MainActivity",
|
||||||
|
|
||||||
|
// Set to true to output debugging logs for diagnostics.
|
||||||
|
"trace": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -54,6 +54,17 @@
|
|||||||
"adbPort"
|
"adbPort"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"amStartArgs": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Custom arguments to pass to the Android application manager to start the app. Run `adb shell am` to show valid arguments. Note that `-D` is required to enable debugging.\r\nBe careful using this option - you must specify the correct parameters or the app will not start.\r\n\r\nThis option is incompatible with the `launchActivity` option.",
|
||||||
|
"default": [
|
||||||
|
"-D",
|
||||||
|
"--activity-brought-to-front",
|
||||||
|
"-a android.intent.action.MAIN",
|
||||||
|
"-c android.intent.category.LAUNCHER",
|
||||||
|
"-n package.name/launch.activity"
|
||||||
|
]
|
||||||
|
},
|
||||||
"appSrcRoot": {
|
"appSrcRoot": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Location of the App source files. This value must point to the root of your App source tree (containing AndroidManifest.xml)",
|
"description": "Location of the App source files. This value must point to the root of your App source tree (containing AndroidManifest.xml)",
|
||||||
|
|||||||
@@ -53,6 +53,18 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
// the full file path name of the AndroidManifest.xml, taken from the manifestFile launch property
|
// the full file path name of the AndroidManifest.xml, taken from the manifestFile launch property
|
||||||
this.manifest_fpn = '';
|
this.manifest_fpn = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* array of custom arguments to pass to `pm install`
|
||||||
|
* @type {string[]}
|
||||||
|
*/
|
||||||
|
this.pm_install_args = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* array of custom arguments to pass to `am start`
|
||||||
|
* @type {string[]}
|
||||||
|
*/
|
||||||
|
this.am_start_args = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the threads (from the last refreshThreads() call)
|
* the threads (from the last refreshThreads() call)
|
||||||
* @type {AndroidThread[]}
|
* @type {AndroidThread[]}
|
||||||
@@ -263,10 +275,18 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
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;
|
||||||
this.manifest_fpn = args.manifestFile;
|
this.manifest_fpn = args.manifestFile;
|
||||||
this.pmInstallArgs = args.pmInstallArgs;
|
this.pm_install_args = args.pmInstallArgs;
|
||||||
|
this.am_start_args = args.amStartArgs;
|
||||||
if (typeof args.callStackDisplaySize === 'number' && args.callStackDisplaySize >= 0)
|
if (typeof args.callStackDisplaySize === 'number' && args.callStackDisplaySize >= 0)
|
||||||
this.callStackDisplaySize = args.callStackDisplaySize|0;
|
this.callStackDisplaySize = args.callStackDisplaySize|0;
|
||||||
|
|
||||||
|
// we don't allow both amStartArgs and launchActivity to be specified (the launch activity must be included in amStartArgs)
|
||||||
|
if (args.amStartArgs && args.launchActivity) {
|
||||||
|
this.LOG('amStartArgs and launchActivity options cannot both be specified in the launch configuration.');
|
||||||
|
this.sendEvent(new TerminatedEvent(false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// set the custom ADB port - this should be changed to pass it to each ADBClient instance
|
// set the custom ADB port - this should be changed to pass it to each ADBClient instance
|
||||||
if (typeof args.adbPort === 'number' && args.adbPort >= 0 && args.adbPort <= 65535) {
|
if (typeof args.adbPort === 'number' && args.adbPort >= 0 && args.adbPort <= 65535) {
|
||||||
ADBSocket.ADBPort = args.adbPort;
|
ADBSocket.ADBPort = args.adbPort;
|
||||||
@@ -378,16 +398,24 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startLaunchActivity(launchActivity) {
|
async startLaunchActivity(launchActivity) {
|
||||||
if (!launchActivity) {
|
if (!launchActivity) {
|
||||||
if (!(launchActivity = this.apk_file_info.manifest.launcher)) {
|
// we're allowed no launchActivity if we have a custom am start command
|
||||||
throw new Error('No valid launch activity found in AndroidManifest.xml or launch.json');
|
if (!this.am_start_args) {
|
||||||
|
if (!(launchActivity = this.apk_file_info.manifest.launcher)) {
|
||||||
|
throw new Error('No valid launch activity found in AndroidManifest.xml or launch.json');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const build = new BuildInfo(this.apk_file_info.manifest.package, new Map(this.src_packages.packages), launchActivity);
|
const build = new BuildInfo(this.apk_file_info.manifest.package, new Map(this.src_packages.packages), launchActivity, this.am_start_args);
|
||||||
this.LOG(`Launching ${build.pkgname}/${launchActivity} on device ${this._device.serial} [API:${this.device_api_level||'?'}]`);
|
|
||||||
return this.dbgr.startDebugSession(build, this._device.serial);
|
this.LOG(`Launching on device ${this._device.serial} [API:${this.device_api_level||'?'}]`);
|
||||||
|
if (this.am_start_args) {
|
||||||
|
this.LOG(`Using custom launch arguments '${this.am_start_args.join(' ')}'`);
|
||||||
|
}
|
||||||
|
const am_stdout = await this.dbgr.startDebugSession(build, this._device.serial);
|
||||||
|
this.LOG(am_stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
async configureAPISourcePath() {
|
async configureAPISourcePath() {
|
||||||
@@ -440,7 +468,8 @@ class AndroidDebugSession extends DebugSession {
|
|||||||
})
|
})
|
||||||
// send the install command
|
// send the install command
|
||||||
this.LOG('Installing...');
|
this.LOG('Installing...');
|
||||||
const command = `pm install ${Array.isArray(this.pmInstallArgs) ? this.pmInstallArgs.join(' ') : '-r'} ${device_apk_fpn}`;
|
const pm_install_args = Array.isArray(this.pm_install_args) ? this.pm_install_args.join(' ') : '-r';
|
||||||
|
const command = `pm install ${pm_install_args} ${device_apk_fpn}`;
|
||||||
D(command);
|
D(command);
|
||||||
const stdout = await this._device.adbclient.shell_cmd({
|
const stdout = await this._device.adbclient.shell_cmd({
|
||||||
command,
|
command,
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ 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`
|
||||||
*/
|
*/
|
||||||
constructor(pkgname, packages, launchActivity) {
|
constructor(pkgname, packages, launchActivity, amCommandArgs) {
|
||||||
this.pkgname = pkgname;
|
this.pkgname = pkgname;
|
||||||
this.packages = packages;
|
this.packages = packages;
|
||||||
this.launchActivity = launchActivity;
|
this.launchActivity = launchActivity;
|
||||||
/** the arguments passed to `am start` */
|
/** the arguments passed to `am start` */
|
||||||
this.startCommandArgs = [
|
this.startCommandArgs = amCommandArgs || [
|
||||||
'-D', // enable debugging
|
'-D', // enable debugging
|
||||||
'--activity-brought-to-front',
|
'--activity-brought-to-front',
|
||||||
'-a android.intent.action.MAIN',
|
'-a android.intent.action.MAIN',
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const {
|
|||||||
TypeNotAvailable,
|
TypeNotAvailable,
|
||||||
} = require('./debugger-types');
|
} = require('./debugger-types');
|
||||||
|
|
||||||
class Debugger extends EventEmitter {
|
class Debugger extends EventEmitter {
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
super();
|
super();
|
||||||
@@ -76,7 +76,7 @@ class Debugger extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
async startDebugSession(build, deviceid) {
|
async startDebugSession(build, deviceid) {
|
||||||
this.session = new DebugSession(build, deviceid);
|
this.session = new DebugSession(build, deviceid);
|
||||||
await Debugger.runApp(deviceid, build.startCommandArgs, build.postLaunchPause);
|
const stdout = await Debugger.runApp(deviceid, build.startCommandArgs, build.postLaunchPause);
|
||||||
|
|
||||||
// retrieve the list of debuggable processes
|
// retrieve the list of debuggable processes
|
||||||
const pids = await this.getDebuggablePIDs(this.session.deviceid);
|
const pids = await this.getDebuggablePIDs(this.session.deviceid);
|
||||||
@@ -84,6 +84,7 @@ class Debugger extends EventEmitter {
|
|||||||
const pid = pids[pids.length - 1];
|
const pid = pids[pids.length - 1];
|
||||||
// after connect(), the caller must call resume() to begin
|
// after connect(), the caller must call resume() to begin
|
||||||
await this.connect(pid);
|
await this.connect(pid);
|
||||||
|
return stdout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,7 +95,7 @@ class Debugger extends EventEmitter {
|
|||||||
static async runApp(deviceid, launch_cmd_args, post_launch_pause = 1000) {
|
static async runApp(deviceid, launch_cmd_args, post_launch_pause = 1000) {
|
||||||
// older (<3) versions of Android only allow target components to be specified with -n
|
// older (<3) versions of Android only allow target components to be specified with -n
|
||||||
const shell_cmd = {
|
const shell_cmd = {
|
||||||
command: 'am start ' + launch_cmd_args.join(' '),
|
command: `am start ${launch_cmd_args.join(' ')}`,
|
||||||
};
|
};
|
||||||
let retries = 10
|
let retries = 10
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -104,12 +105,14 @@ class Debugger extends EventEmitter {
|
|||||||
await sleep(post_launch_pause);
|
await sleep(post_launch_pause);
|
||||||
// failures:
|
// failures:
|
||||||
// Error: Activity not started...
|
// Error: Activity not started...
|
||||||
const m = stdout.match(/Error:.*/g);
|
// /system/bin/sh: syntax error: unexpected EOF - this happens with invalid am command arguments
|
||||||
|
const m = stdout.match(/Error:.*|syntax error:/gi);
|
||||||
if (!m) {
|
if (!m) {
|
||||||
break;
|
// return the stdout from am (it shows the fully qualified component name)
|
||||||
|
return stdout.toString().trim();
|
||||||
}
|
}
|
||||||
else if (retries <= 0){
|
else if (retries <= 0){
|
||||||
throw new Error(m[0]);
|
throw new Error(stdout.toString().trim());
|
||||||
}
|
}
|
||||||
retries -= 1;
|
retries -= 1;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user