src/main/java/at/favre/tools/uberadb/ui/CLIParser.java
/*
*
* * Copyright 2016 Patrick Favre-Bulle
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package at.favre.tools.uberadb.ui;
import at.favre.tools.uberadb.util.CmdUtil;
import at.favre.tools.uberadb.util.MiscUtil;
import org.apache.commons.cli.*;
public final class CLIParser {
private CLIParser() {
}
static final String ARG_INSTALL = "i";
static final String ARG_UNINSTALL = "u";
static final String ARG_BUGREPORT = "b";
static final String ARG_DEVICE_SERIAL = "s";
static final String ARG_FORCE_STOP = "force-stop";
static final String ARG_CLEAR_DATA = "clear";
static final String ARG_APPINFO = "appinfo";
static final String ARG_START_ACTIVITY = "start";
static final int DEFAULT_DELAY_SEC = 2;
public static Arg parse(String[] args) {
Options options = setupOptions();
CommandLineParser parser = new DefaultParser();
Arg argument = new Arg();
try {
CommandLine commandLine = parser.parse(options, args);
if (commandLine.hasOption("h") || commandLine.hasOption("help")) {
printHelp(options);
return null;
}
if (commandLine.hasOption("v") || commandLine.hasOption("version")) {
System.out.println("Version: " + CmdUtil.jarVersion());
return null;
}
int mainArgCount = 0;
if (commandLine.hasOption(ARG_UNINSTALL)) {
argument.mainArgument = commandLine.getOptionValues(ARG_UNINSTALL);
argument.mode = Arg.Mode.UNINSTALL;
mainArgCount++;
}
if (commandLine.hasOption(ARG_INSTALL)) {
argument.mainArgument = commandLine.getOptionValues(ARG_INSTALL);
argument.mode = Arg.Mode.INSTALL;
mainArgCount++;
}
if (commandLine.hasOption(ARG_BUGREPORT)) {
argument.mainArgument = commandLine.getOptionValues(ARG_BUGREPORT);
argument.mode = Arg.Mode.BUGREPORT;
mainArgCount++;
}
if (commandLine.hasOption(ARG_FORCE_STOP)) {
argument.mainArgument = commandLine.getOptionValues(ARG_FORCE_STOP);
argument.mode = Arg.Mode.FORCE_STOP;
mainArgCount++;
}
if (commandLine.hasOption(ARG_CLEAR_DATA)) {
argument.mainArgument = commandLine.getOptionValues(ARG_CLEAR_DATA);
argument.mode = Arg.Mode.CLEAR;
mainArgCount++;
}
if (commandLine.hasOption(ARG_APPINFO)) {
argument.mainArgument = commandLine.getOptionValues(ARG_APPINFO);
argument.mode = Arg.Mode.INFO;
mainArgCount++;
}
if (commandLine.hasOption(ARG_START_ACTIVITY)) {
argument.mainArgument = MiscUtil.getPackagesWithoutDelayValue(commandLine.getOptionValues(ARG_START_ACTIVITY));
argument.delayStartActivitySec = MiscUtil.getIntInLastElement(commandLine.getOptionValues(ARG_START_ACTIVITY), DEFAULT_DELAY_SEC);
argument.mode = Arg.Mode.START_ACTIVITY;
mainArgCount++;
}
if (commandLine.hasOption("reportDebugIntent")) {
String[] reportArgs = commandLine.getOptionValues("reportDebugIntent");
if (reportArgs.length < 2) {
throw new IllegalArgumentException("must provide filter and intent argument eg. 'com.google.* \"-a intent.at\"");
}
argument.reportFilterIntent = reportArgs;
}
if (mainArgCount != 1) {
throw new IllegalArgumentException("Must either provide either one of " + ARG_INSTALL + ", " + ARG_UNINSTALL + ", " + ARG_BUGREPORT + ", " + ARG_FORCE_STOP + ", " + ARG_CLEAR_DATA + " or " + ARG_START_ACTIVITY + " argument");
}
if (argument.mode == Arg.Mode.START_ACTIVITY && (argument.delayStartActivitySec <= 0 || argument.delayStartActivitySec > 500)) {
throw new IllegalArgumentException("Delay must be between 1 and 500 seconds (found " + argument.delayStartActivitySec + ")");
}
if (commandLine.hasOption("adbPath")) {
argument.adbPath = commandLine.getOptionValue("adbPath");
}
if (commandLine.hasOption(ARG_DEVICE_SERIAL)) {
argument.device = commandLine.getOptionValue(ARG_DEVICE_SERIAL);
}
if (commandLine.hasOption("dumpsysServices")) {
argument.dumpsysServices = commandLine.getOptionValues("dumpsysServices");
}
argument.dryRun = commandLine.hasOption("dryRun");
argument.skipEmulators = commandLine.hasOption("skipEmulators");
argument.keepData = commandLine.hasOption("keepData") || commandLine.hasOption("upgrade");
argument.quiet = commandLine.hasOption("quiet");
argument.debug = commandLine.hasOption("debug");
argument.force = commandLine.hasOption("force");
argument.grantPermissions = commandLine.hasOption("grant");
argument.simpleBugReport = commandLine.hasOption("simpleBugreport");
argument.waitForDevice = commandLine.hasOption("waitForDevice");
} catch (Exception e) {
System.err.println(e.getMessage());
CLIParser.printHelp(options);
argument = null;
}
return argument;
}
private static Options setupOptions() {
Options options = new Options();
Option mainInstall = Option.builder(ARG_INSTALL).longOpt("install").argName("apk file/folder").desc("Provide path to an " +
"apk file or folder containing apk files and the tool tries to install all of them to all connected devices (if not" +
" a specfic device is selected). It is possible to pass multiple files/folders as arguments e.g. '/apks apk1.apk apk2.apk'").hasArgs().build();
Option mainUninstall = Option.builder(ARG_UNINSTALL).longOpt("uninstall").argName("package filter").hasArgs().desc("Filter" +
" string that has to be a package name or part of it containing wildcards '*' for uninstalling. Can be multiple filter" +
" Strings space separated. Example: 'com.android.*' or 'com.android.* com.google.*'.").build();
Option mainBugReport = Option.builder(ARG_BUGREPORT).longOpt("bugreport").argName("out folder").hasArg().optionalArg(true).desc("Creates" +
" a generic bug report (including eg. logcat and screenshot) from all connected devices and zips it to the folder given as arg." +
" If no folder is given tries to zips it in the location of the .jar.").build();
Option mainForceStop = Option.builder().longOpt(ARG_FORCE_STOP).argName("package filter").hasArgs().desc("Will stop the process " +
"of given packages. Argument is the filter string that has to be a package name or part of it containing wildcards '*'. Can" +
" be multiple filter Strings space separated. Example: 'com.android.*' or 'com.android.* com.google.*'.").build();
Option mainClearAppData = Option.builder().longOpt(ARG_CLEAR_DATA).argName("package filter").hasArgs().desc("Will clear app data " +
"for given packages. Argument is the filter string that has to be a package name or part of it containing wildcards '*'. Can" +
" be multiple filter Strings space separated. Example: 'com.android.*' or 'com.android.* com.google.*'.").build();
Option mainInfoAppData = Option.builder().longOpt(ARG_APPINFO).argName("package filter").hasArgs().desc("Will show additional information " +
"for like version, install-time, etc of the apps matching the argument. Argument is the filter string that has to be a package " +
"name or part of it containing wildcards '*'. Can be multiple filter Strings space separated. Example: 'com.android.*' or " +
"'com.android.* com.google.*'.").build();
Option mainStartActivityData = Option.builder().longOpt(ARG_START_ACTIVITY).argName("package filter> <[seconds]").hasArgs().desc("Will " +
"start the launcher activity of this app. Argument is the filter string that has to be a package name or part of it containing " +
"wildcards '*'. Can be multiple filter Strings space separated. Example: 'com.android.*' or 'com.android.* com.google.*'. The " +
"last argument may be a int in seconds which represents the wait time between the apps eg.: 'com.exmaple.* 10' will have a 10 " +
"sec delay between starts.").build();
Option adbPathOpt = Option.builder().longOpt("adbPath").argName("path").hasArg(true).desc("Full path to adb executable. If this " +
"is omitted the tool tries to find adb in PATH env variable.").build();
Option deviceOpt = Option.builder(ARG_DEVICE_SERIAL).longOpt("serial").argName("device serial").hasArg(true).desc("If this is set, " +
"will only use given device. Default is all connected devices. Device id is the same that is given by 'adb devices'").build();
Option reportFilter = Option.builder().longOpt("reportDebugIntent").argName("package> <intent").hasArgs().valueSeparator(' ').desc("Only for " +
"Bugreport: This is useful to start a e.g. activity that e.g. logs additional info before reading the logcat. " +
"First param is a package filter (see --uninstall argument) followed by a series of params appended to a 'adb shell am' type " +
"command to start an activity or service (See https://goo.gl/MGK7ck). This will be executed for each app/package that is " +
"matched by the first parameter. You can use the placeholder '${package}' and will substitute the package name. " +
"Example: 'com.google* start -n ${package}/com.myapp.LogActivity --ez LOG true' See https://goo.gl/luuPfz for the " +
"correct intent start syntax.").build();
Option dumpsysOpt = Option.builder().longOpt("dumpsysServices").argName("service-name").hasArgs().desc("Only for bugreport: include only theses dumpsys services. See all services with 'adb shell dumpsys list'").build();
Option dryRunOpt = Option.builder().longOpt("dryRun").hasArg(false).desc("Use this to see what would be installed/uninstalled on what devices with the given params. Will not install/uninstall anything.").build();
Option skipEmuOpt = Option.builder().longOpt("skipEmulators").hasArg(false).desc("Skips device emulators for install/uninstall.").build();
Option keepDataOpt = Option.builder().longOpt("keepData").hasArg(false).desc("Only for uninstall: Uses the '-k' param on 'adb uninstall' to keep data and caches of the app.").build();
Option upgradeOpt = Option.builder().longOpt("upgrade").hasArg(false).desc("Only for install: Uses the '-r' param on 'adb install' for trying to reinstall the app and keeping its data.").build();
Option quietOpt = Option.builder().longOpt("quiet").hasArg(false).desc("Prints less output.").build();
Option debugOpt = Option.builder().longOpt("debug").hasArg(false).desc("Prints additional info for debugging.").build();
Option forceOpt = Option.builder().longOpt("force").hasArg(false).desc("If this flag is set all matched apps will be installed/uninstalled without any further warning. Otherwise a user input is necessary.").build();
Option grantOpt = Option.builder().longOpt("grant").hasArg(false).desc("Only for install: will grant all permissions set in the apk automatically.").build();
Option simpleBugreportOpt = Option.builder().longOpt("simpleBugreport").hasArg(false).desc("Only for bugreport: report will only contain the most essential data").build();
Option waitForDeviceOpt = Option.builder().longOpt("waitForDevice").hasArg(false).desc("If set, will wait until a device is connected and debug mode is enabled.").build();
Option help = Option.builder("h").longOpt("help").desc("Prints docs").build();
Option version = Option.builder("v").longOpt("version").desc("Prints current version.").build();
OptionGroup mainArgs = new OptionGroup();
mainArgs.addOption(mainUninstall).addOption(mainInstall).addOption(mainBugReport).addOption(mainForceStop).addOption(mainClearAppData).addOption(help).addOption(version).addOption(mainInfoAppData).addOption(mainStartActivityData);
mainArgs.setRequired(true);
options.addOptionGroup(mainArgs);
options.addOption(adbPathOpt).addOption(deviceOpt).addOption(dryRunOpt).addOption(skipEmuOpt).addOption(keepDataOpt)
.addOption(quietOpt).addOption(debugOpt).addOption(forceOpt).addOption(upgradeOpt).addOption(reportFilter)
.addOption(grantOpt).addOption(simpleBugreportOpt).addOption(dumpsysOpt).addOption(waitForDeviceOpt).addOption(mainStartActivityData);
return options;
}
private static void printHelp(Options options) {
HelpFormatter help = new HelpFormatter();
help.setWidth(120);
help.setLeftPadding(4);
help.setDescPadding(3);
help.printHelp("-" + ARG_INSTALL + " <apk file/folder> | -" + ARG_UNINSTALL + " <package filter> | -" + ARG_BUGREPORT + " <out folder> | -"
+ ARG_FORCE_STOP + " <package filter> | -" + ARG_CLEAR_DATA + " <package filter> | " + ARG_APPINFO + " <package filter> | --help", "Version:" + CmdUtil.jarVersion(), options, " ", false);
}
}