mirror of
https://github.com/revanced/revanced-cli.git
synced 2024-11-11 06:09:24 +01:00
feat: add install command
This introduces a separate utility subcommand.
This commit is contained in:
parent
a3d8705e89
commit
0350b7f1a2
@ -86,12 +86,26 @@ Learn how to ReVanced CLI.
|
|||||||
|
|
||||||
> **Note**: Some patches may require integrations
|
> **Note**: Some patches may require integrations
|
||||||
such as [ReVanced Integrations](https://github.com/revanced/revanced-integrations).
|
such as [ReVanced Integrations](https://github.com/revanced/revanced-integrations).
|
||||||
Supply them with the option `-m`. If any patches accepted by ReVanced Patcher require ReVanced Integrations,
|
Supply them with the option `--merge`. If any patches accepted by ReVanced Patcher require ReVanced Integrations,
|
||||||
they will be merged into the APK file automatically.
|
they will be merged into the APK file automatically.
|
||||||
|
|
||||||
- ### 🗑️ Uninstall a patched APK file
|
- ### 🗑️ Uninstall a patched APK file
|
||||||
```bash
|
```bash
|
||||||
java -jar revanced-cli.jar uninstall \
|
java -jar revanced-cli.jar utility uninstall \
|
||||||
--package-name <package-name> \
|
--package-name <package-name> \
|
||||||
<device-serial>
|
<device-serial>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Note**: You can unmount an APK file
|
||||||
|
with the option `--unmount`.
|
||||||
|
|
||||||
|
- ### ️ ⚙️ Manually install an APK file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
java -jar revanced-cli.jar utility install \
|
||||||
|
-a input.apk \
|
||||||
|
<device-serial>
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: You can mount an APK file
|
||||||
|
by supplying the package name of the app to mount the supplied APK file to over the option `--mount`.
|
||||||
|
@ -19,38 +19,33 @@ internal object ListPatchesCommand : Runnable {
|
|||||||
private val logger = Logger.getLogger(ListPatchesCommand::class.java.name)
|
private val logger = Logger.getLogger(ListPatchesCommand::class.java.name)
|
||||||
|
|
||||||
@Parameters(
|
@Parameters(
|
||||||
description = ["Paths to patch bundles"],
|
description = ["Paths to patch bundles"], arity = "1..*"
|
||||||
arity = "1..*"
|
|
||||||
)
|
)
|
||||||
lateinit var patchBundles: Array<File>
|
private lateinit var patchBundles: Array<File>
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-d", "--with-descriptions"],
|
names = ["-d", "--with-descriptions"], description = ["List their descriptions"], showDefaultValue = ALWAYS
|
||||||
description = ["List their descriptions"],
|
|
||||||
showDefaultValue = ALWAYS
|
|
||||||
)
|
)
|
||||||
var withDescriptions: Boolean = true
|
private var withDescriptions: Boolean = true
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-p", "--with-packages"],
|
names = ["-p", "--with-packages"],
|
||||||
description = ["List the packages the patches are compatible with"],
|
description = ["List the packages the patches are compatible with"],
|
||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
)
|
)
|
||||||
var withPackages: Boolean = false
|
private var withPackages: Boolean = false
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-v", "--with-versions"],
|
names = ["-v", "--with-versions"],
|
||||||
description = ["List the versions of the packages the patches are compatible with"],
|
description = ["List the versions of the apps the patches are compatible with"],
|
||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
)
|
)
|
||||||
var withVersions: Boolean = false
|
private var withVersions: Boolean = false
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-o", "--with-options"],
|
names = ["-o", "--with-options"], description = ["List the options of the patches"], showDefaultValue = ALWAYS
|
||||||
description = ["List the options of the patches"],
|
|
||||||
showDefaultValue = ALWAYS
|
|
||||||
)
|
)
|
||||||
var withOptions: Boolean = false
|
private var withOptions: Boolean = false
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
fun Package.buildString() = buildString {
|
fun Package.buildString() = buildString {
|
||||||
@ -58,8 +53,7 @@ internal object ListPatchesCommand : Runnable {
|
|||||||
appendLine("Package name: $name")
|
appendLine("Package name: $name")
|
||||||
appendLine("Compatible versions:")
|
appendLine("Compatible versions:")
|
||||||
append(versions.joinToString("\n") { version -> version }.prependIndent("\t"))
|
append(versions.joinToString("\n") { version -> version }.prependIndent("\t"))
|
||||||
} else
|
} else append("Package name: $name")
|
||||||
append("Package name: $name")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PatchOption<*>.buildString() = buildString {
|
fun PatchOption<*>.buildString() = buildString {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package app.revanced.cli.command
|
package app.revanced.cli.command
|
||||||
|
|
||||||
|
import app.revanced.cli.command.utility.UtilityCommand
|
||||||
import app.revanced.patcher.patch.PatchClass
|
import app.revanced.patcher.patch.PatchClass
|
||||||
import picocli.CommandLine
|
import picocli.CommandLine
|
||||||
import picocli.CommandLine.Command
|
import picocli.CommandLine.Command
|
||||||
@ -42,7 +43,7 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
internal typealias PatchList = List<PatchClass>
|
internal typealias PatchList = List<PatchClass>
|
||||||
|
|
||||||
object CLIVersionProvider : IVersionProvider {
|
private object CLIVersionProvider : IVersionProvider {
|
||||||
override fun getVersion(): Array<String> {
|
override fun getVersion(): Array<String> {
|
||||||
Properties().apply {
|
Properties().apply {
|
||||||
load(MainCommand::class.java.getResourceAsStream("/app/revanced/cli/version.properties"))
|
load(MainCommand::class.java.getResourceAsStream("/app/revanced/cli/version.properties"))
|
||||||
@ -60,8 +61,8 @@ object CLIVersionProvider : IVersionProvider {
|
|||||||
subcommands = [
|
subcommands = [
|
||||||
ListPatchesCommand::class,
|
ListPatchesCommand::class,
|
||||||
PatchCommand::class,
|
PatchCommand::class,
|
||||||
UninstallCommand::class,
|
|
||||||
OptionsCommand::class,
|
OptionsCommand::class,
|
||||||
|
UtilityCommand::class,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
internal object MainCommand
|
private object MainCommand
|
@ -16,38 +16,31 @@ internal object OptionsCommand : Runnable {
|
|||||||
private val logger = Logger.getLogger(OptionsCommand::class.java.name)
|
private val logger = Logger.getLogger(OptionsCommand::class.java.name)
|
||||||
|
|
||||||
@CommandLine.Parameters(
|
@CommandLine.Parameters(
|
||||||
description = ["Paths to patch bundles"],
|
description = ["Paths to patch bundles"], arity = "1..*"
|
||||||
arity = "1..*"
|
|
||||||
)
|
)
|
||||||
lateinit var patchBundles: Array<File>
|
private lateinit var patchBundles: Array<File>
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-p", "--path"],
|
names = ["-p", "--path"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
|
||||||
description = ["Path to patch options JSON file"],
|
|
||||||
showDefaultValue = ALWAYS
|
|
||||||
)
|
)
|
||||||
var path: File = File("options.json")
|
private var path: File = File("options.json")
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-o", "--overwrite"],
|
names = ["-o", "--overwrite"], description = ["Overwrite existing options file"], showDefaultValue = ALWAYS
|
||||||
description = ["Overwrite existing options file"],
|
|
||||||
showDefaultValue = ALWAYS
|
|
||||||
)
|
)
|
||||||
var overwrite: Boolean = false
|
private var overwrite: Boolean = false
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-u", "--update"],
|
names = ["-u", "--update"],
|
||||||
description = ["Update existing options by adding missing and removing non-existent options"],
|
description = ["Update existing options by adding missing and removing non-existent options"],
|
||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
)
|
)
|
||||||
var update: Boolean = false
|
private var update: Boolean = false
|
||||||
|
|
||||||
override fun run() = if (!path.exists() || overwrite)
|
override fun run() = if (!path.exists() || overwrite) with(PatchBundleLoader.Jar(*patchBundles)) {
|
||||||
with(PatchBundleLoader.Jar(*patchBundles)) {
|
if (update) setOptions(path)
|
||||||
if (update) setOptions(path)
|
|
||||||
|
|
||||||
Options.serialize(this, prettyPrint = true)
|
Options.serialize(this, prettyPrint = true).let(path::writeText)
|
||||||
.let(path::writeText)
|
}
|
||||||
}
|
|
||||||
else logger.severe("Options file already exists, use --override to override it")
|
else logger.severe("Options file already exists, use --override to override it")
|
||||||
}
|
}
|
@ -23,84 +23,69 @@ import java.util.logging.Logger
|
|||||||
|
|
||||||
|
|
||||||
@CommandLine.Command(
|
@CommandLine.Command(
|
||||||
name = "patch",
|
name = "patch", description = ["Patch the supplied APK file with the supplied patches and integrations"]
|
||||||
description = ["Patch the supplied APK file with the supplied patches and integrations"]
|
|
||||||
)
|
)
|
||||||
internal object PatchCommand: Runnable {
|
internal object PatchCommand : Runnable {
|
||||||
private val logger = Logger.getLogger(PatchCommand::class.java.name)
|
private val logger = Logger.getLogger(PatchCommand::class.java.name)
|
||||||
|
|
||||||
@CommandLine.Parameters(
|
@CommandLine.Parameters(
|
||||||
description = ["APK file to be patched"],
|
description = ["APK file to be patched"], arity = "1..1"
|
||||||
arity = "1..1"
|
|
||||||
)
|
)
|
||||||
lateinit var apk: File
|
private lateinit var apk: File
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-b", "--patch-bundle"],
|
names = ["-b", "--patch-bundle"], description = ["One or more bundles of patches"], required = true
|
||||||
description = ["One or more bundles of patches"],
|
|
||||||
required = true
|
|
||||||
)
|
)
|
||||||
var patchBundles = emptyList<File>()
|
private var patchBundles = emptyList<File>()
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-m", "--merge"],
|
names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK"]
|
||||||
description = ["One or more DEX files or containers to merge into the APK"]
|
|
||||||
)
|
)
|
||||||
var integrations = listOf<File>()
|
private var integrations = listOf<File>()
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-i", "--include"],
|
names = ["-i", "--include"], description = ["List of patches to include"]
|
||||||
description = ["List of patches to include"]
|
|
||||||
)
|
)
|
||||||
var includedPatches = arrayOf<String>()
|
private var includedPatches = arrayOf<String>()
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-e", "--exclude"],
|
names = ["-e", "--exclude"], description = ["List of patches to exclude"]
|
||||||
description = ["List of patches to exclude"]
|
|
||||||
)
|
)
|
||||||
var excludedPatches = arrayOf<String>()
|
private var excludedPatches = arrayOf<String>()
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["--options"],
|
names = ["--options"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
|
||||||
description = ["Path to patch options JSON file"],
|
|
||||||
showDefaultValue = ALWAYS
|
|
||||||
)
|
)
|
||||||
var optionsFile: File = File("options.json")
|
private var optionsFile: File = File("options.json")
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["--exclusive"],
|
names = ["--exclusive"],
|
||||||
description = ["Only include patches that are explicitly specified to be included"],
|
description = ["Only include patches that are explicitly specified to be included"],
|
||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
)
|
)
|
||||||
var exclusive = false
|
private var exclusive = false
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["--experimental"],
|
names = ["--experimental"],
|
||||||
description = ["Ignore patches incompatibility to versions"],
|
description = ["Ignore patches incompatibility to versions"],
|
||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
)
|
)
|
||||||
var experimental: Boolean = false
|
private var experimental: Boolean = false
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-o", "--out"],
|
names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true
|
||||||
description = ["Path to save the patched APK file to"],
|
|
||||||
required = true
|
|
||||||
)
|
)
|
||||||
lateinit var outputFilePath: File
|
private lateinit var outputFilePath: File
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-d", "--device-serial"],
|
names = ["-d", "--device-serial"], description = ["ADB device serial to install to"], showDefaultValue = ALWAYS
|
||||||
description = ["ADB device serial to install to"],
|
|
||||||
showDefaultValue = ALWAYS
|
|
||||||
)
|
)
|
||||||
var deviceSerial: String? = null
|
private var deviceSerial: String? = null
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["--mount"],
|
names = ["--mount"], description = ["Install by mounting the patched APK file"], showDefaultValue = ALWAYS
|
||||||
description = ["Install by mounting the patched package"],
|
|
||||||
showDefaultValue = ALWAYS
|
|
||||||
)
|
)
|
||||||
var mount: Boolean = false
|
private var mount: Boolean = false
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["--common-name"],
|
names = ["--common-name"],
|
||||||
@ -108,39 +93,36 @@ internal object PatchCommand: Runnable {
|
|||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
|
|
||||||
)
|
)
|
||||||
var commonName = "ReVanced"
|
private var commonName = "ReVanced"
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["--keystore"],
|
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"]
|
||||||
description = ["Path to the keystore to sign the patched APK file with"]
|
|
||||||
)
|
)
|
||||||
var keystorePath: String? = null
|
private var keystorePath: String? = null
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["--password"],
|
names = ["--password"], description = ["The password of the keystore to sign the patched APK file with"]
|
||||||
description = ["The password of the keystore to sign the patched APK file with"]
|
|
||||||
)
|
)
|
||||||
var password = "ReVanced"
|
private var password = "ReVanced"
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-r", "--resource-cache"],
|
names = ["-r", "--resource-cache"],
|
||||||
description = ["Path to temporary resource cache directory"],
|
description = ["Path to temporary resource cache directory"],
|
||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
)
|
)
|
||||||
var resourceCachePath = File("revanced-resource-cache")
|
private var resourceCachePath = File("revanced-resource-cache")
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["--custom-aapt2-binary"],
|
names = ["--custom-aapt2-binary"], description = ["Path to a custom AAPT binary to compile resources with"]
|
||||||
description = ["Path to a custom AAPT binary to compile resources with"]
|
|
||||||
)
|
)
|
||||||
var aaptBinaryPath = File("")
|
private var aaptBinaryPath = File("")
|
||||||
|
|
||||||
@CommandLine.Option(
|
@CommandLine.Option(
|
||||||
names = ["-p", "--purge"],
|
names = ["-p", "--purge"],
|
||||||
description = ["Purge the temporary resource cache directory after patching"],
|
description = ["Purge the temporary resource cache directory after patching"],
|
||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
)
|
)
|
||||||
var purge: Boolean = false
|
private var purge: Boolean = false
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
// region Prepare
|
// region Prepare
|
||||||
@ -206,8 +188,7 @@ internal object PatchCommand: Runnable {
|
|||||||
|
|
||||||
val alignAndSignedFile = sign(
|
val alignAndSignedFile = sign(
|
||||||
apk.newAlignedFile(
|
apk.newAlignedFile(
|
||||||
result,
|
result, resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk")
|
||||||
resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk")
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -287,19 +268,16 @@ internal object PatchCommand: Runnable {
|
|||||||
it.isEmpty() || it.any { version -> version == packageVersion }
|
it.isEmpty() || it.any { version -> version == packageVersion }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!matchesVersion) return@patch logger.warning(
|
if (!matchesVersion) return@patch logger.warning("${patch.patchName} is incompatible with version $packageVersion. " + "This patch is only compatible with version " + packages.joinToString(
|
||||||
"${patch.patchName} is incompatible with version $packageVersion. " +
|
";"
|
||||||
"This patch is only compatible with version " +
|
) { pkg ->
|
||||||
packages.joinToString(";") { pkg ->
|
"${pkg.name}: ${pkg.versions.joinToString(", ")}"
|
||||||
"${pkg.name}: ${pkg.versions.joinToString(", ")}"
|
})
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
} ?: return@patch logger.fine(
|
}
|
||||||
"${patch.patchName} is incompatible with $packageName. " +
|
?: return@patch logger.fine("${patch.patchName} is incompatible with $packageName. " + "This patch is only compatible with " + packages.joinToString(
|
||||||
"This patch is only compatible with " +
|
", "
|
||||||
packages.joinToString(", ") { `package` -> `package`.name }
|
) { `package` -> `package`.name })
|
||||||
)
|
|
||||||
|
|
||||||
return@let
|
return@let
|
||||||
} ?: logger.fine("$formattedPatchName: No constraint on packages.")
|
} ?: logger.fine("$formattedPatchName: No constraint on packages.")
|
||||||
@ -341,8 +319,7 @@ internal object PatchCommand: Runnable {
|
|||||||
* @param outputFile The file to save the aligned APK to.
|
* @param outputFile The file to save the aligned APK to.
|
||||||
*/
|
*/
|
||||||
private fun File.newAlignedFile(
|
private fun File.newAlignedFile(
|
||||||
result: PatcherResult,
|
result: PatcherResult, outputFile: File
|
||||||
outputFile: File
|
|
||||||
): File {
|
): File {
|
||||||
logger.info("Aligning $name")
|
logger.info("Aligning $name")
|
||||||
|
|
||||||
@ -351,23 +328,20 @@ internal object PatchCommand: Runnable {
|
|||||||
ZipFile(outputFile).use { file ->
|
ZipFile(outputFile).use { file ->
|
||||||
result.dexFiles.forEach {
|
result.dexFiles.forEach {
|
||||||
file.addEntryCompressData(
|
file.addEntryCompressData(
|
||||||
ZipEntry.createWithName(it.name),
|
ZipEntry.createWithName(it.name), it.stream.readBytes()
|
||||||
it.stream.readBytes()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.resourceFile?.let {
|
result.resourceFile?.let {
|
||||||
file.copyEntriesFromFileAligned(
|
file.copyEntriesFromFileAligned(
|
||||||
ZipFile(it),
|
ZipFile(it), ZipAligner::getEntryAlignment
|
||||||
ZipAligner::getEntryAlignment
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Do not compress result.doNotCompress
|
// TODO: Do not compress result.doNotCompress
|
||||||
|
|
||||||
file.copyEntriesFromFileAligned(
|
file.copyEntriesFromFileAligned(
|
||||||
ZipFile(this),
|
ZipFile(this), ZipAligner::getEntryAlignment
|
||||||
ZipAligner::getEntryAlignment
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,32 +354,25 @@ internal object PatchCommand: Runnable {
|
|||||||
* @param inputFile The APK file to sign.
|
* @param inputFile The APK file to sign.
|
||||||
* @return The signed APK file. If [mount] is true, the input file will be returned.
|
* @return The signed APK file. If [mount] is true, the input file will be returned.
|
||||||
*/
|
*/
|
||||||
private fun sign(inputFile: File) = if (mount)
|
private fun sign(inputFile: File) = if (mount) inputFile
|
||||||
inputFile
|
|
||||||
else {
|
else {
|
||||||
logger.info("Signing ${inputFile.name}")
|
logger.info("Signing ${inputFile.name}")
|
||||||
|
|
||||||
val keyStoreFilePath = keystorePath ?: outputFilePath
|
val keyStoreFilePath = keystorePath
|
||||||
.absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath
|
?: outputFilePath.absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath
|
||||||
|
|
||||||
val options = SigningOptions(
|
val options = SigningOptions(
|
||||||
commonName,
|
commonName, password, keyStoreFilePath
|
||||||
password,
|
|
||||||
keyStoreFilePath
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ApkSigner(options)
|
ApkSigner(options).signApk(
|
||||||
.signApk(
|
inputFile, resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk")
|
||||||
inputFile,
|
|
||||||
resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk")
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun purge(resourceCachePath: File) {
|
private fun purge(resourceCachePath: File) {
|
||||||
val result = if (resourceCachePath.deleteRecursively())
|
val result = if (resourceCachePath.deleteRecursively()) "Purged resource cache directory"
|
||||||
"Purged resource cache directory"
|
else "Failed to purge resource cache directory"
|
||||||
else
|
|
||||||
"Failed to purge resource cache directory"
|
|
||||||
logger.info(result)
|
logger.info(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package app.revanced.cli.command.utility
|
||||||
|
|
||||||
|
import app.revanced.utils.adb.AdbManager
|
||||||
|
import picocli.CommandLine.*
|
||||||
|
import java.io.File
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "install", description = ["Install an APK file to devices with the supplied ADB device serials"]
|
||||||
|
)
|
||||||
|
internal object InstallCommand : Runnable {
|
||||||
|
private val logger = Logger.getLogger(InstallCommand::class.java.name)
|
||||||
|
|
||||||
|
@Parameters(
|
||||||
|
description = ["ADB device serials"], arity = "1..*"
|
||||||
|
)
|
||||||
|
private lateinit var deviceSerials: Array<String>
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-a", "--apk"], description = ["APK file to be installed"], required = true
|
||||||
|
)
|
||||||
|
private lateinit var apk: File
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-m", "--mount"],
|
||||||
|
description = ["Mount the supplied APK file over the app with the supplied package name"],
|
||||||
|
)
|
||||||
|
private var packageName: String? = null
|
||||||
|
|
||||||
|
override fun run() = try {
|
||||||
|
deviceSerials.forEach { deviceSerial ->
|
||||||
|
if (packageName != null) {
|
||||||
|
AdbManager.RootAdbManager(deviceSerial)
|
||||||
|
} else {
|
||||||
|
AdbManager.UserAdbManager(deviceSerial)
|
||||||
|
}.install(AdbManager.Apk(apk, packageName))
|
||||||
|
}
|
||||||
|
} catch (e: AdbManager.DeviceNotFoundException) {
|
||||||
|
logger.severe(e.toString())
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package app.revanced.cli.command
|
package app.revanced.cli.command.utility
|
||||||
|
|
||||||
import app.revanced.utils.adb.AdbManager
|
import app.revanced.utils.adb.AdbManager
|
||||||
import picocli.CommandLine.*
|
import picocli.CommandLine.*
|
||||||
@ -13,28 +13,21 @@ import java.util.logging.Logger
|
|||||||
internal object UninstallCommand : Runnable {
|
internal object UninstallCommand : Runnable {
|
||||||
private val logger = Logger.getLogger(UninstallCommand::class.java.name)
|
private val logger = Logger.getLogger(UninstallCommand::class.java.name)
|
||||||
|
|
||||||
@Parameters(
|
@Parameters(description = ["ADB device serials"], arity = "1..*")
|
||||||
description = ["ADB device serials"],
|
private lateinit var deviceSerials: Array<String>
|
||||||
arity = "1..*"
|
|
||||||
)
|
|
||||||
lateinit var deviceSerials: Array<String>
|
|
||||||
|
|
||||||
@Option(
|
@Option(names = ["-p", "--package-name"], description = ["Package name to uninstall"], required = true)
|
||||||
names = ["-p", "--package-name"],
|
private lateinit var packageName: String
|
||||||
description = ["Package name to uninstall"],
|
|
||||||
required = true
|
|
||||||
)
|
|
||||||
lateinit var packageName: String
|
|
||||||
|
|
||||||
@Option(
|
@Option(
|
||||||
names = ["-u", "--unmount"],
|
names = ["-u", "--unmount"],
|
||||||
description = ["Uninstall by unmounting the patched package"],
|
description = ["Uninstall by unmounting the patched APK file"],
|
||||||
showDefaultValue = ALWAYS
|
showDefaultValue = ALWAYS
|
||||||
)
|
)
|
||||||
var unmount: Boolean = false
|
private var unmount: Boolean = false
|
||||||
|
|
||||||
override fun run() = try {
|
override fun run() = try {
|
||||||
deviceSerials.forEach {deviceSerial ->
|
deviceSerials.forEach { deviceSerial ->
|
||||||
if (unmount) {
|
if (unmount) {
|
||||||
AdbManager.RootAdbManager(deviceSerial)
|
AdbManager.RootAdbManager(deviceSerial)
|
||||||
} else {
|
} else {
|
@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.cli.command.utility
|
||||||
|
|
||||||
|
import picocli.CommandLine
|
||||||
|
|
||||||
|
@CommandLine.Command(
|
||||||
|
name = "utility",
|
||||||
|
description = ["Commands for utility purposes"],
|
||||||
|
subcommands = [InstallCommand::class, UninstallCommand::class],
|
||||||
|
)
|
||||||
|
internal object UtilityCommand
|
@ -89,7 +89,7 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun uninstall(packageName: String) {
|
override fun uninstall(packageName: String) {
|
||||||
logger.info("Uninstalling $packageName by unmounting and deleting the package")
|
logger.info("Uninstalling $packageName by unmounting")
|
||||||
|
|
||||||
val applyReplacement = getPlaceholderReplacement(packageName)
|
val applyReplacement = getPlaceholderReplacement(packageName)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user