feat: Simplify command and option names and descriptions (#338)

BREAKING CHANGE: Options have been renamed.
This commit is contained in:
oSumAtrIX 2024-08-14 16:06:58 +04:00 committed by GitHub
parent 6306b1abfe
commit 6e7797a3f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 100 additions and 98 deletions

View File

@ -13,7 +13,7 @@ java -jar revanced-cli.jar -h
## 📃 List patches ## 📃 List patches
```bash ```bash
java -jar revanced-cli.jar list-patches --with-packages --with-versions --with-options revanced-patches.rvp java -jar revanced-cli.jar list-patches --with-packages --with-versions --with-options patches.rvp
``` ```
## 💉 Patch an app ## 💉 Patch an app
@ -21,48 +21,48 @@ java -jar revanced-cli.jar list-patches --with-packages --with-versions --with-o
To patch an app using the default list of patches, use the `patch` command: To patch an app using the default list of patches, use the `patch` command:
```bash ```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp input.apk java -jar revanced-cli.jar patch -p patches.rvp input.apk
``` ```
You can also use multiple patch bundles: You can also use multiple files containing patches:
```bash ```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp -b another-patches.rvp input.apk java -jar revanced-cli.jar patch -p patches.rvp -b another-patches.rvp input.apk
``` ```
To change the default set of used patches, use the option `-i` or `-e` to use or disuse specific patches. To change the default set of enabled or disabled patches, use the option `-e` or `-d` to enable or disable specific patches.
You can use the `list-patches` command to see which patches are used by default. You can use the `list-patches` command to see which patches are enabled by default.
To only use specific patches, you can use the option `--exclusive` combined with `-i`. To only enable specific patches, you can use the option `--exclusive` combined with `-e`.
Remember that the options `-i` and `-e` match the patch's name exactly. Here is an example: Remember that the options `-e` and `-d` match the patch's name exactly. Here is an example:
```bash ```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp --exclusive -i "Patch name" -i "Another patch name" input.apk java -jar revanced-cli.jar patch -p patches.rvp --exclusive -e "Patch name" -e "Another patch name" input.apk
``` ```
You can also use the options `--ii` and `--ie` to use or disuse patches by their index. You can also use the options `--ei` or `--di` to enable or disable patches by their index.
This is useful, if two patches happen to have the same name. This is useful, if two patches happen to have the same name, or if typing the names is too cumbersome.
To know the indices of patches, use the command `list-patches`: To know the indices of patches, use the command `list-patches`:
```bash ```bash
java -jar revanced-cli.jar list-patches revanced-patches.rvp java -jar revanced-cli.jar list-patches patches.rvp
``` ```
Then you can use the indices to use or disuse patches: Then you can use the indices to enable or disable patches:
```bash ```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp --ii 123 --ie 456 input.apk java -jar revanced-cli.jar patch -b patches.rvp --ei 123 --di 456 input.apk
``` ```
You can combine the option `-i`, `-e`, `--ii`, `--ie` and `--exclusive`. Here is an example: You can combine the option `-e`, `-d`, `--ei`, `--di` and `--exclusive`. Here is an example:
```bash ```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp --exclusive -i "Patch name" --ii 123 input.apk java -jar revanced-cli.jar patch -p patches.rvp --exclusive -i "Patch name" --ei 123 input.apk
``` ```
> [!TIP] > [!TIP]
> You can use the option `-d` to automatically install the patched app after patching. > You can use the option `-i` to automatically install the patched app after patching.
> Make sure ADB is working: > Make sure ADB is working:
> >
> ```bash > ```bash
@ -83,7 +83,7 @@ Patches can have options you can set using the option `-O` alongside the option
To know the options of a patch, use the option `--with-options` when listing patches: To know the options of a patch, use the option `--with-options` when listing patches:
```bash ```bash
java -jar revanced-cli.jar list-patches --with-options revanced-patches.rvp java -jar revanced-cli.jar list-patches --with-options patches.rvp
``` ```
Each patch can have multiple options. You can set them using the option `-O`. Each patch can have multiple options. You can set them using the option `-O`.
@ -91,10 +91,10 @@ For example, to set the options for the patch with the name `Patch name`
with the key `key1` and `key2` to `value1` and `value2` respectively, use the following command: with the key `key1` and `key2` to `value1` and `value2` respectively, use the following command:
```bash ```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1=value1 -Okey2=value2 input.apk java -jar revanced-cli.jar patch -b patches.rvp -e "Patch name" -Okey1=value1 -Okey2=value2 input.apk
``` ```
If you want to set a value to `null`, you can omit the value: If you want to set the option value to `null`, you can omit the value:
```bash ```bash
java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1 input.apk java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1 input.apk
@ -102,7 +102,7 @@ java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1
> [!WARNING] > [!WARNING]
> Option values are usually typed. If you set a value with the wrong type, the patch can fail. > Option values are usually typed. If you set a value with the wrong type, the patch can fail.
> Option value types can be seen when listing patches with the option `--with-options`. > The value types can be seen when listing patches with the option `--with-options`.
> >
> Example option values: > Example option values:
> >
@ -131,7 +131,7 @@ java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1
> Example command with an escaped integer as a string: > Example command with an escaped integer as a string:
> >
> ```bash > ```bash
> java -jar revanced-cli.jar -b revanced-patches.rvp -i "Patch name" -OstringKey=\'1\' input.apk > java -jar revanced-cli.jar -p patches.rvp -e "Patch name" -OstringKey=\'1\' input.apk
> ``` > ```
## 📦 Install an app manually ## 📦 Install an app manually

View File

@ -12,17 +12,17 @@ import java.util.logging.Logger
name = "list-versions", name = "list-versions",
description = [ description = [
"List the most common compatible versions of apps that are compatible " + "List the most common compatible versions of apps that are compatible " +
"with the patches in the supplied patch bundles.", "with the patches in the supplied files containing patches.",
], ],
) )
internal class ListCompatibleVersions : Runnable { internal class ListCompatibleVersions : Runnable {
private val logger = Logger.getLogger(this::class.java.name) private val logger = Logger.getLogger(this::class.java.name)
@CommandLine.Parameters( @CommandLine.Parameters(
description = ["Paths to patch bundles."], description = ["One or more paths to files containing patches."],
arity = "1..*", arity = "1..*",
) )
private lateinit var patchBundles: Set<File> private lateinit var patchesFiles: Set<File>
@CommandLine.Option( @CommandLine.Option(
names = ["-f", "--filter-package-names"], names = ["-f", "--filter-package-names"],
@ -56,7 +56,7 @@ internal class ListCompatibleVersions : Runnable {
appendLine(versions.buildVersionsString().prependIndent("\t")) appendLine(versions.buildVersionsString().prependIndent("\t"))
} }
val patches = loadPatchesFromJar(patchBundles) val patches = loadPatchesFromJar(patchesFiles)
patches.mostCommonCompatibleVersions( patches.mostCommonCompatibleVersions(
packageNames, packageNames,

View File

@ -11,16 +11,16 @@ import app.revanced.patcher.patch.Option as PatchOption
@Command( @Command(
name = "list-patches", name = "list-patches",
description = ["List patches from supplied patch bundles."], description = ["List patches from supplied files containing patches."],
) )
internal object ListPatchesCommand : Runnable { internal object ListPatchesCommand : Runnable {
private val logger = Logger.getLogger(this::class.java.name) private val logger = Logger.getLogger(this::class.java.name)
@Parameters( @Parameters(
description = ["Paths to patch bundles."], description = ["One or more paths to files containing patches."],
arity = "1..*", arity = "1..*",
) )
private lateinit var patchBundles: Set<File> private lateinit var patchesFiles: Set<File>
@Option( @Option(
names = ["-d", "--with-descriptions"], names = ["-d", "--with-descriptions"],
@ -59,7 +59,7 @@ internal object ListPatchesCommand : Runnable {
@Option( @Option(
names = ["-i", "--index"], names = ["-i", "--index"],
description = ["List the index of each patch in relation to the supplied patch bundles."], description = ["List the index of each patch in relation to the supplied files containing patches."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var withIndex: Boolean = true private var withIndex: Boolean = true
@ -111,6 +111,8 @@ internal object ListPatchesCommand : Runnable {
if (withDescriptions) append("\nDescription: ${patch.description}") if (withDescriptions) append("\nDescription: ${patch.description}")
append("Enabled: ${patch.use}")
if (withOptions && patch.options.isNotEmpty()) { if (withOptions && patch.options.isNotEmpty()) {
appendLine("\nOptions:") appendLine("\nOptions:")
append( append(
@ -135,7 +137,7 @@ internal object ListPatchesCommand : Runnable {
compatiblePackages?.any { (compatiblePackageName, _) -> compatiblePackageName == name } compatiblePackages?.any { (compatiblePackageName, _) -> compatiblePackageName == name }
?: withUniversalPatches ?: withUniversalPatches
val patches = loadPatchesFromJar(patchBundles).withIndex().toList() val patches = loadPatchesFromJar(patchesFiles).withIndex().toList()
val filtered = val filtered =
packageName?.let { patches.filter { (_, patch) -> patch.filterCompatiblePackages(it) } } ?: patches packageName?.let { patches.filter { (_, patch) -> patch.filterCompatiblePackages(it) } } ?: patches

View File

@ -34,23 +34,23 @@ internal object PatchCommand : Runnable {
internal class Selection { internal class Selection {
@ArgGroup(exclusive = false, multiplicity = "1") @ArgGroup(exclusive = false, multiplicity = "1")
internal var include: IncludeSelection? = null internal var enabled: EnableSelection? = null
internal class IncludeSelection { internal class EnableSelection {
@ArgGroup(multiplicity = "1") @ArgGroup(multiplicity = "1")
internal lateinit var selector: IncludeSelector internal lateinit var selector: EnableSelector
internal class IncludeSelector { internal class EnableSelector {
@CommandLine.Option( @CommandLine.Option(
names = ["-i", "--include"], names = ["-e", "--enable"],
description = ["The name of the patch."], description = ["Name of the patch."],
required = true, required = true,
) )
internal var name: String? = null internal var name: String? = null
@CommandLine.Option( @CommandLine.Option(
names = ["--ii"], names = ["--ei"],
description = ["The index of the patch in the combined list of all supplied patch bundles."], description = ["Index of the patch in the combined list of all supplied files containing patches."],
required = true, required = true,
) )
internal var index: Int? = null internal var index: Int? = null
@ -58,7 +58,7 @@ internal object PatchCommand : Runnable {
@CommandLine.Option( @CommandLine.Option(
names = ["-O", "--options"], names = ["-O", "--options"],
description = ["The option values keyed by the option keys."], description = ["Option values keyed by option keys."],
mapFallbackValue = CommandLine.Option.NULL_VALUE, mapFallbackValue = CommandLine.Option.NULL_VALUE,
converter = [OptionKeyConverter::class, OptionValueConverter::class], converter = [OptionKeyConverter::class, OptionValueConverter::class],
) )
@ -66,23 +66,23 @@ internal object PatchCommand : Runnable {
} }
@ArgGroup(exclusive = false, multiplicity = "1") @ArgGroup(exclusive = false, multiplicity = "1")
internal var exclude: ExcludeSelection? = null internal var disable: DisableSelection? = null
internal class ExcludeSelection { internal class DisableSelection {
@ArgGroup(multiplicity = "1") @ArgGroup(multiplicity = "1")
internal lateinit var selector: ExcludeSelector internal lateinit var selector: DisableSelector
internal class ExcludeSelector { internal class DisableSelector {
@CommandLine.Option( @CommandLine.Option(
names = ["-e", "--exclude"], names = ["-d", "--disable"],
description = ["The name of the patch."], description = ["Name of the patch."],
required = true, required = true,
) )
internal var name: String? = null internal var name: String? = null
@CommandLine.Option( @CommandLine.Option(
names = ["--ie"], names = ["--di"],
description = ["The index of the patch in the combined list of all supplied patch bundles."], description = ["Index of the patch in the combined list of all supplied files containing patches."],
required = true, required = true,
) )
internal var index: Int? = null internal var index: Int? = null
@ -92,14 +92,14 @@ internal object PatchCommand : Runnable {
@CommandLine.Option( @CommandLine.Option(
names = ["--exclusive"], names = ["--exclusive"],
description = ["Only include patches that are explicitly specified to be included."], description = ["Disable all patches except the ones enabled."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var exclusive = false private var exclusive = false
@CommandLine.Option( @CommandLine.Option(
names = ["-f", "--force"], names = ["-f", "--force"],
description = ["Bypass compatibility checks for the supplied APK's version."], description = ["Don't check for compatibility with the supplied APK's version."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var force: Boolean = false private var force: Boolean = false
@ -108,7 +108,7 @@ internal object PatchCommand : Runnable {
@CommandLine.Option( @CommandLine.Option(
names = ["-o", "--out"], names = ["-o", "--out"],
description = ["Path to save the patched APK file to. Defaults to the same directory as the supplied APK file."], description = ["Path to save the patched APK file to. Defaults to the same path as the supplied APK file."],
) )
@Suppress("unused") @Suppress("unused")
private fun setOutputFilePath(outputFilePath: File?) { private fun setOutputFilePath(outputFilePath: File?) {
@ -116,8 +116,8 @@ internal object PatchCommand : Runnable {
} }
@CommandLine.Option( @CommandLine.Option(
names = ["-d", "--device-serial"], names = ["-i", "--install"],
description = ["ADB device serial to install to. If not supplied, the first connected device will be used."], description = ["Serial of the ADB device to install to. If not specified, the first connected device will be used."],
// Empty string to indicate that the first connected device should be used. // Empty string to indicate that the first connected device should be used.
fallbackValue = "", fallbackValue = "",
arity = "0..1", arity = "0..1",
@ -126,7 +126,7 @@ internal object PatchCommand : Runnable {
@CommandLine.Option( @CommandLine.Option(
names = ["--mount"], names = ["--mount"],
description = ["Install by mounting the patched APK file."], description = ["Install the patched APK file by mounting."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var mount: Boolean = false private var mount: Boolean = false
@ -134,28 +134,28 @@ internal object PatchCommand : Runnable {
@CommandLine.Option( @CommandLine.Option(
names = ["--keystore"], names = ["--keystore"],
description = [ description = [
"Path to the keystore to sign the patched APK file with. " + "Path to the keystore file containing a private key and certificate pair to sign the patched APK file with. " +
"Defaults to the same directory as the supplied APK file.", "Defaults to the same directory as the supplied APK file.",
], ],
) )
private var keystoreFilePath: File? = null private var keyStoreFilePath: File? = null
@CommandLine.Option( @CommandLine.Option(
names = ["--keystore-password"], names = ["--keystore-password"],
description = ["The password of the keystore to sign the patched APK file with. Empty password by default."], description = ["Password of the keystore. Empty password by default."],
) )
private var keyStorePassword: String? = null // Empty password by default private var keyStorePassword: String? = null // Empty password by default
@CommandLine.Option( @CommandLine.Option(
names = ["--keystore-entry-alias"], names = ["--keystore-entry-alias"],
description = ["The alias of the keystore entry to sign the patched APK file with."], description = ["Alias of the private key and certificate pair keystore entry."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var keyStoreEntryAlias = "ReVanced Key" private var keyStoreEntryAlias = "ReVanced Key"
@CommandLine.Option( @CommandLine.Option(
names = ["--keystore-entry-password"], names = ["--keystore-entry-password"],
description = ["The password of the entry from the keystore for the key to sign the patched APK file with."], description = ["Password of the keystore entry."],
) )
private var keyStoreEntryPassword = "" // Empty password by default private var keyStoreEntryPassword = "" // Empty password by default
@ -175,15 +175,15 @@ internal object PatchCommand : Runnable {
private var aaptBinaryPath: File? = null private var aaptBinaryPath: File? = null
@CommandLine.Option( @CommandLine.Option(
names = ["-p", "--purge"], names = ["--purge"],
description = ["Purge the temporary resource cache directory after patching."], description = ["Purge temporary files directory after patching."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var purge: Boolean = false private var purge: Boolean = false
@CommandLine.Parameters( @CommandLine.Parameters(
description = ["APK file to be patched."], description = ["APK file to patch."],
arity = "1..1", arity = "1",
) )
@Suppress("unused") @Suppress("unused")
private fun setApk(apk: File) { private fun setApk(apk: File) {
@ -199,19 +199,19 @@ internal object PatchCommand : Runnable {
private lateinit var apk: File private lateinit var apk: File
@CommandLine.Option( @CommandLine.Option(
names = ["-b", "--patch-bundle"], names = ["-p", "--patches"],
description = ["One or more bundles of patches."], description = ["One or more path to files containing patches."],
required = true, required = true,
) )
@Suppress("unused") @Suppress("unused")
private fun setPatchBundles(patchBundles: Set<File>) { private fun setPatchesFile(patchesFiles: Set<File>) {
patchBundles.firstOrNull { !it.exists() }?.let { patchesFiles.firstOrNull { !it.exists() }?.let {
throw CommandLine.ParameterException(spec.commandLine(), "Patch bundle ${it.name} does not exist") throw CommandLine.ParameterException(spec.commandLine(), "${it.name} can't be found")
} }
this.patchBundles = patchBundles this.patchesFiles = patchesFiles
} }
private var patchBundles = emptySet<File>() private var patchesFiles = emptySet<File>()
@CommandLine.Option( @CommandLine.Option(
names = ["--custom-aapt2-binary"], names = ["--custom-aapt2-binary"],
@ -242,7 +242,7 @@ internal object PatchCommand : Runnable {
) )
val keystoreFilePath = val keystoreFilePath =
keystoreFilePath ?: outputFilePath.parentFile keyStoreFilePath ?: outputFilePath.parentFile
.resolve("${outputFilePath.nameWithoutExtension}.keystore") .resolve("${outputFilePath.nameWithoutExtension}.keystore")
// endregion // endregion
@ -251,7 +251,7 @@ internal object PatchCommand : Runnable {
logger.info("Loading patches") logger.info("Loading patches")
val patches = loadPatchesFromJar(patchBundles) val patches = loadPatchesFromJar(patchesFiles)
// endregion // endregion
@ -274,11 +274,11 @@ internal object PatchCommand : Runnable {
logger.info("Setting patch options") logger.info("Setting patch options")
val patchesList = patches.toList() val patchesList = patches.toList()
selection.filter { it.include != null }.associate { selection.filter { it.enabled != null }.associate {
val includeSelection = it.include!! val enabledSelection = it.enabled!!
(includeSelection.selector.name ?: patchesList[includeSelection.selector.index!!].name!!) to (enabledSelection.selector.name ?: patchesList[enabledSelection.selector.index!!].name!!) to
includeSelection.options enabledSelection.options
}.let(filteredPatches::setOptions) }.let(filteredPatches::setOptions)
patcher += filteredPatches patcher += filteredPatches
@ -365,21 +365,21 @@ internal object PatchCommand : Runnable {
packageName: String, packageName: String,
packageVersion: String, packageVersion: String,
): Set<Patch<*>> = buildSet { ): Set<Patch<*>> = buildSet {
val includedPatchesByName = val enabledPatchesByName =
selection.asSequence().mapNotNull { it.include?.selector?.name }.toSet() selection.asSequence().mapNotNull { it.enabled?.selector?.name }.toSet()
val includedPatchesByIndex = val enabledPatchesByIndex =
selection.asSequence().mapNotNull { it.include?.selector?.index }.toSet() selection.asSequence().mapNotNull { it.enabled?.selector?.index }.toSet()
val excludedPatches = val disabledPatches =
selection.asSequence().mapNotNull { it.exclude?.selector?.name }.toSet() selection.asSequence().mapNotNull { it.disable?.selector?.name }.toSet()
val excludedPatchesByIndex = val disabledPatchesByIndex =
selection.asSequence().mapNotNull { it.exclude?.selector?.index }.toSet() selection.asSequence().mapNotNull { it.disable?.selector?.index }.toSet()
this@filterPatchSelection.withIndex().forEach patchLoop@{ (i, patch) -> this@filterPatchSelection.withIndex().forEach patchLoop@{ (i, patch) ->
val patchName = patch.name!! val patchName = patch.name!!
val isManuallyExcluded = patchName in excludedPatches || i in excludedPatchesByIndex val isManuallyDisabled = patchName in disabledPatches || i in disabledPatchesByIndex
if (isManuallyExcluded) return@patchLoop logger.info("\"$patchName\" excluded manually") if (isManuallyDisabled) return@patchLoop logger.info("\"$patchName\" disabled manually")
// Make sure the patch is compatible with the supplied APK files package name and version. // Make sure the patch is compatible with the supplied APK files package name and version.
patch.compatiblePackages?.let { packages -> patch.compatiblePackages?.let { packages ->
@ -409,11 +409,11 @@ internal object PatchCommand : Runnable {
return@let return@let
} ?: logger.fine("\"$patchName\" has no package constraints") } ?: logger.fine("\"$patchName\" has no package constraints")
val isIncluded = !exclusive && patch.use val isEnabled = !exclusive && patch.use
val isManuallyIncluded = patchName in includedPatchesByName || i in includedPatchesByIndex val isManuallyEnabled = patchName in enabledPatchesByName || i in enabledPatchesByIndex
if (!(isIncluded || isManuallyIncluded)) { if (!(isEnabled || isManuallyEnabled)) {
return@patchLoop logger.info("\"$patchName\" excluded") return@patchLoop logger.info("\"$patchName\" disabled")
} }
add(patch) add(patch)

View File

@ -10,27 +10,27 @@ import java.util.logging.Logger
@Command( @Command(
name = "install", name = "install",
description = ["Install an APK file to devices with the supplied ADB device serials"], description = ["Install an APK file."],
) )
internal object InstallCommand : Runnable { internal object InstallCommand : Runnable {
private val logger = Logger.getLogger(this::class.java.name) private val logger = Logger.getLogger(this::class.java.name)
@Parameters( @Parameters(
description = ["ADB device serials. If not supplied, the first connected device will be used."], description = ["Serial of ADB devices. If not supplied, the first connected device will be used."],
arity = "0..*", arity = "0..*",
) )
private var deviceSerials: Array<String>? = null private var deviceSerials: Array<String>? = null
@Option( @Option(
names = ["-a", "--apk"], names = ["-a", "--apk"],
description = ["APK file to be installed"], description = ["APK file to be installed."],
required = true, required = true,
) )
private lateinit var apk: File private lateinit var apk: File
@Option( @Option(
names = ["-m", "--mount"], names = ["-m", "--mount"],
description = ["Mount the supplied APK file over the app with the supplied package name"], description = ["Mount the supplied APK file over the app with the supplied package name."],
) )
private var packageName: String? = null private var packageName: String? = null

View File

@ -13,27 +13,27 @@ import java.util.logging.Logger
@Command( @Command(
name = "uninstall", name = "uninstall",
description = ["Uninstall a patched app from the devices with the supplied ADB device serials"], description = ["Uninstall a patched app."],
) )
internal object UninstallCommand : Runnable { internal object UninstallCommand : Runnable {
private val logger = Logger.getLogger(this::class.java.name) private val logger = Logger.getLogger(this::class.java.name)
@Parameters( @Parameters(
description = ["ADB device serials. If not supplied, the first connected device will be used."], description = ["Serial of ADB devices. If not supplied, the first connected device will be used."],
arity = "0..*", arity = "0..*",
) )
private var deviceSerials: Array<String>? = null private var deviceSerials: Array<String>? = null
@Option( @Option(
names = ["-p", "--package-name"], names = ["-p", "--package-name"],
description = ["Package name of the app to uninstall"], description = ["Package name of the app to uninstall."],
required = true, required = true,
) )
private lateinit var packageName: String private lateinit var packageName: String
@Option( @Option(
names = ["-u", "--unmount"], names = ["-u", "--unmount"],
description = ["Uninstall by unmounting the patched APK file"], description = ["Uninstall the patched APK file by unmounting."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var unmount: Boolean = false private var unmount: Boolean = false