Skip to content

Commit

Permalink
feat: Allow selecting first Adb device, if none supplied automaticall…
Browse files Browse the repository at this point in the history
…y by updating dependencies
  • Loading branch information
oSumAtrIX committed Nov 26, 2023
1 parent 3765957 commit e7c3d64
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 90 deletions.
4 changes: 2 additions & 2 deletions docs/1_usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ ReVanced CLI is divided into the following fundamental commands:
```bash
java -jar revanced-cli.jar utility uninstall \
--package-name <package-name> \
<device-serial>
[<device-serial>]
```
> [!NOTE]
Expand All @@ -128,7 +128,7 @@ ReVanced CLI is divided into the following fundamental commands:
```bash
java -jar revanced-cli.jar utility install \
-a input.apk \
<device-serial>
[<device-serial>]
```
> [!NOTE]
Expand Down
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[versions]
shadow = "8.1.1"
kotlin-test = "1.9.10"
kotlin-test = "1.9.20"
kotlinx-coroutines-core = "1.7.3"
picocli = "4.7.3"
revanced-patcher = "19.0.0"
revanced-library = "1.2.0"
revanced-library = "1.3.0"

[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" }
Expand Down
164 changes: 92 additions & 72 deletions src/main/kotlin/app/revanced/cli/command/PatchCommand.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ import java.io.PrintWriter
import java.io.StringWriter
import java.util.logging.Logger


@CommandLine.Command(
name = "patch",
description = ["Patch an APK file."]
description = ["Patch an APK file."],
)
internal object PatchCommand : Runnable {
private val logger = Logger.getLogger(PatchCommand::class.java.name)
Expand All @@ -37,25 +36,25 @@ internal object PatchCommand : Runnable {

@CommandLine.Option(
names = ["-i", "--include"],
description = ["List of patches to include."]
description = ["List of patches to include."],
)
private var includedPatches = hashSetOf<String>()

@CommandLine.Option(
names = ["--ii"],
description = ["List of patches to include by their index in relation to the supplied patch bundles."]
description = ["List of patches to include by their index in relation to the supplied patch bundles."],
)
private var includedPatchesByIndex = arrayOf<Int>()

@CommandLine.Option(
names = ["-e", "--exclude"],
description = ["List of patches to exclude."]
description = ["List of patches to exclude."],
)
private var excludedPatches = hashSetOf<String>()

@CommandLine.Option(
names = ["--ei"],
description = ["List of patches to exclude by their index in relation to the supplied patch bundles."]
description = ["List of patches to exclude by their index in relation to the supplied patch bundles."],
)
private var excludedPatchesByIndex = arrayOf<Int>()

Expand All @@ -68,14 +67,14 @@ internal object PatchCommand : Runnable {
@CommandLine.Option(
names = ["--exclusive"],
description = ["Only include patches that are explicitly specified to be included."],
showDefaultValue = ALWAYS
showDefaultValue = ALWAYS,
)
private var exclusive = false

@CommandLine.Option(
names = ["-f","--force"],
names = ["-f", "--force"],
description = ["Bypass compatibility checks for the supplied APK's version."],
showDefaultValue = ALWAYS
showDefaultValue = ALWAYS,
)
private var force: Boolean = false

Expand All @@ -91,48 +90,52 @@ internal object PatchCommand : Runnable {

@CommandLine.Option(
names = ["-d", "--device-serial"],
description = ["ADB device serial to install to."],
description = ["ADB device serial to install to. If not supplied, the first connected device will be used."],
fallbackValue = "", // Empty string to indicate that the first connected device should be used.
arity = "0..1",
)
private var deviceSerial: String? = null

@CommandLine.Option(
names = ["--mount"],
description = ["Install by mounting the patched APK file."],
showDefaultValue = ALWAYS
showDefaultValue = ALWAYS,
)
private var mount: Boolean = false

@CommandLine.Option(
names = ["--keystore"],
description = ["Path to the keystore to sign the patched APK file with. " +
"Defaults to the same directory as the supplied APK file."],
description = [
"Path to the keystore to sign the patched APK file with. " +
"Defaults to the same directory as the supplied APK file.",
],
)
private var keystoreFilePath: File? = null

// key store password
@CommandLine.Option(
names = ["--keystore-password"],
description = ["The password of the keystore to sign the patched APK file with. Empty password by default."]
description = ["The password of the keystore to sign the patched APK file with. Empty password by default."],
)
private var keyStorePassword: String? = null // Empty password by default

@CommandLine.Option(
names = ["--alias"],
description = ["The alias of the key from the keystore to sign the patched APK file with."],
showDefaultValue = ALWAYS
showDefaultValue = ALWAYS,
)
private var alias = "ReVanced Key"

@CommandLine.Option(
names = ["--keystore-entry-password"],
description = ["The password of the entry from the keystore for the key to sign the patched APK file with."]
description = ["The password of the entry from the keystore for the key to sign the patched APK file with."],
)
private var password = "" // Empty password by default

@CommandLine.Option(
names = ["--signer"],
description = ["The name of the signer to sign the patched APK file with."],
showDefaultValue = ALWAYS
showDefaultValue = ALWAYS,
)
private var signer = "ReVanced"

Expand All @@ -147,33 +150,35 @@ internal object PatchCommand : Runnable {
@CommandLine.Option(
names = ["-p", "--purge"],
description = ["Purge the temporary resource cache directory after patching."],
showDefaultValue = ALWAYS
showDefaultValue = ALWAYS,
)
private var purge: Boolean = false

@CommandLine.Option(
names = ["-w", "--warn"],
description = ["Warn if a patch can not be found in the supplied patch bundles."],
showDefaultValue = ALWAYS
showDefaultValue = ALWAYS,
)
private var warn: Boolean = false

@CommandLine.Parameters(
description = ["APK file to be patched."],
arity = "1..1"
arity = "1..1",
)
@Suppress("unused")
private fun setApk(apk: File) {
if (!apk.exists()) throw CommandLine.ParameterException(
spec.commandLine(),
"APK file ${apk.name} does not exist"
)
if (!apk.exists()) {
throw CommandLine.ParameterException(
spec.commandLine(),
"APK file ${apk.name} does not exist",
)
}
this.apk = apk
}

@CommandLine.Option(
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."],
)
@Suppress("unused")
private fun setIntegrations(integrations: Array<File>) {
Expand All @@ -186,7 +191,7 @@ internal object PatchCommand : Runnable {
@CommandLine.Option(
names = ["-b", "--patch-bundle"],
description = ["One or more bundles of patches."],
required = true
required = true,
)
@Suppress("unused")
private fun setPatchBundles(patchBundles: Array<File>) {
Expand All @@ -198,37 +203,37 @@ internal object PatchCommand : Runnable {

@CommandLine.Option(
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."],
)
@Suppress("unused")
private fun setAaptBinaryPath(aaptBinaryPath: File) {
if (!aaptBinaryPath.exists()) throw CommandLine.ParameterException(
spec.commandLine(),
"AAPT binary ${aaptBinaryPath.name} does not exist"
)
if (!aaptBinaryPath.exists()) {
throw CommandLine.ParameterException(
spec.commandLine(),
"AAPT binary ${aaptBinaryPath.name} does not exist",
)
}
this.aaptBinaryPath = aaptBinaryPath
}

override fun run() {
// region Setup

val outputFilePath = outputFilePath ?: File("").absoluteFile.resolve(
"${apk.nameWithoutExtension}-patched.${apk.extension}"
"${apk.nameWithoutExtension}-patched.${apk.extension}",
)

val resourceCachePath = resourceCachePath ?: outputFilePath.parentFile.resolve(
"${outputFilePath.nameWithoutExtension}-resource-cache"
"${outputFilePath.nameWithoutExtension}-resource-cache",
)

val optionsFile = optionsFile ?: outputFilePath.parentFile.resolve(
"${outputFilePath.nameWithoutExtension}-options.json"
"${outputFilePath.nameWithoutExtension}-options.json",
)

val keystoreFilePath = keystoreFilePath ?: outputFilePath.parentFile
.resolve("${outputFilePath.nameWithoutExtension}.keystore")

val adbManager = deviceSerial?.let { serial -> AdbManager.getAdbManager(serial, mount) }

// endregion

// region Load patches
Expand All @@ -238,13 +243,15 @@ internal object PatchCommand : Runnable {
val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray())

// Warn if a patch can not be found in the supplied patch bundles.
if (warn) patches.map { it.name }.toHashSet().let { availableNames ->
(includedPatches + excludedPatches).filter { name ->
!availableNames.contains(name)
if (warn) {
patches.map { it.name }.toHashSet().let { availableNames ->
(includedPatches + excludedPatches).filter { name ->
!availableNames.contains(name)
}
}.let { unknownPatches ->
if (unknownPatches.isEmpty()) return@let
logger.warning("Unknown input of patches:\n${unknownPatches.joinToString("\n")}")
}
}.let { unknownPatches ->
if (unknownPatches.isEmpty()) return@let
logger.warning("Unknown input of patches:\n${unknownPatches.joinToString("\n")}")
}

// endregion
Expand All @@ -255,14 +262,17 @@ internal object PatchCommand : Runnable {
resourceCachePath,
aaptBinaryPath?.path,
resourceCachePath.absolutePath,
true
)
true,
),
).use { patcher ->
val filteredPatches = patcher.filterPatchSelection(patches).also { patches ->
logger.info("Setting patch options")

if (optionsFile.exists()) patches.setOptions(optionsFile)
else Options.serialize(patches, prettyPrint = true).let(optionsFile::writeText)
if (optionsFile.exists()) {
patches.setOptions(optionsFile)
} else {
Options.serialize(patches, prettyPrint = true).let(optionsFile::writeText)
}
}

// region Patch
Expand Down Expand Up @@ -292,24 +302,29 @@ internal object PatchCommand : Runnable {
ApkUtils.copyAligned(apk, this, patcherResult)
}

if (!mount) ApkUtils.sign(
alignedFile,
outputFilePath,
ApkUtils.SigningOptions(
keystoreFilePath,
keyStorePassword,
alias,
password,
signer
if (!mount) {
ApkUtils.sign(
alignedFile,
outputFilePath,
ApkUtils.SigningOptions(
keystoreFilePath,
keyStorePassword,
alias,
password,
signer,
),
)
)
else alignedFile.renameTo(outputFilePath)
} else {
alignedFile.renameTo(outputFilePath)
}

// endregion

// region Install

adbManager?.install(AdbManager.Apk(outputFilePath, patcher.context.packageMetadata.packageName))
deviceSerial?.let { serial ->
AdbManager.getAdbManager(deviceSerial = serial.ifEmpty { null }, mount)
}?.install(AdbManager.Apk(outputFilePath, patcher.context.packageMetadata.packageName))

// endregion
}
Expand All @@ -320,7 +335,6 @@ internal object PatchCommand : Runnable {
}
}


/**
* Filter the patches to be added to the patcher. The filter is based on the following:
*
Expand All @@ -344,17 +358,20 @@ internal object PatchCommand : Runnable {
it.any { version -> version == packageVersion }
} ?: true

if (!matchesVersion) return@patch logger.warning(
"$patchName is incompatible with version $packageVersion. "
+ "This patch is only compatible with version "
+ packages.joinToString(";") { pkg ->
pkg.versions!!.joinToString(", ")
}
)
if (!matchesVersion) {
return@patch logger.warning(
"$patchName is incompatible with version $packageVersion. " +
"This patch is only compatible with version " +
packages.joinToString(";") { pkg ->
pkg.versions!!.joinToString(", ")
},
)
}
} ?: return@patch logger.fine(
"$patchName is incompatible with $packageName. "
+ "This patch is only compatible with "
+ packages.joinToString(", ") { `package` -> `package`.name })
"$patchName is incompatible with $packageName. " +
"This patch is only compatible with " +
packages.joinToString(", ") { `package` -> `package`.name },
)

return@let
} ?: logger.fine("$patchName has no constraint on packages.")
Expand All @@ -374,8 +391,11 @@ internal object PatchCommand : Runnable {
}

private fun purge(resourceCachePath: File) {
val result = if (resourceCachePath.deleteRecursively()) "Purged resource cache directory"
else "Failed to purge resource cache directory"
val result = if (resourceCachePath.deleteRecursively()) {
"Purged resource cache directory"
} else {
"Failed to purge resource cache directory"
}
logger.info(result)
}
}
}
Loading

0 comments on commit e7c3d64

Please sign in to comment.