Compare commits

..

No commits in common. "main" and "v5.0.0-dev.1" have entirely different histories.

16 changed files with 1607 additions and 1187 deletions

View File

@ -10,8 +10,6 @@ on:
jobs: jobs:
release: release:
name: Release name: Release
permissions:
contents: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
@ -48,5 +46,5 @@ jobs:
- name: Release - name: Release
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
run: npm exec semantic-release run: npm exec semantic-release

View File

@ -23,8 +23,7 @@
"assets": [ "assets": [
"CHANGELOG.md", "CHANGELOG.md",
"gradle.properties" "gradle.properties"
], ]
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
} }
], ],
[ [

View File

@ -1,115 +1,3 @@
## [5.0.1](https://github.com/ReVanced/revanced-cli/compare/v5.0.0...v5.0.1) (2025-04-14)
### Bug Fixes
* Make mounting work again by bumping dependencies ([#359](https://github.com/ReVanced/revanced-cli/issues/359)) ([68a4872](https://github.com/ReVanced/revanced-cli/commit/68a48724ebf01a0c8f8adc0fec63037bff672dc9))
## [5.0.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v5.0.0...v5.0.1-dev.1) (2025-02-26)
### Bug Fixes
* Make mounting work again by bumping dependencies ([#359](https://github.com/ReVanced/revanced-cli/issues/359)) ([68a4872](https://github.com/ReVanced/revanced-cli/commit/68a48724ebf01a0c8f8adc0fec63037bff672dc9))
# [5.0.0](https://github.com/ReVanced/revanced-cli/compare/v4.6.0...v5.0.0) (2024-11-10)
### Bug Fixes
* Check for null when no device serial was specified ([1da8ae9](https://github.com/ReVanced/revanced-cli/commit/1da8ae9e46000dd3c4eecd793c559e75012cf535))
* List if patch option is required ([#346](https://github.com/ReVanced/revanced-cli/issues/346)) ([98ff0c3](https://github.com/ReVanced/revanced-cli/commit/98ff0c34fa71c3b3ecd96157d45a30ee2b8979c6))
* Make CLI ArgGroup non-exclusive to be able to disable and enable patches at the same time ([1bb0d13](https://github.com/ReVanced/revanced-cli/commit/1bb0d13726fd5790c59cb6d28df3618c7606710d))
* Make patches selectable by using a mutable collection for the selection option ([751fa1d](https://github.com/ReVanced/revanced-cli/commit/751fa1d889f40c51b291116029fd84f2b051f2f0))
* Make the patch command work without specifying any selection ([ba159a3](https://github.com/ReVanced/revanced-cli/commit/ba159a35a9a99d18a4c1e04128b08ae336a49b3e))
* Print in new line correctly ([c2dc9d7](https://github.com/ReVanced/revanced-cli/commit/c2dc9d76be33c98284741e23c406500483c47753))
* Use the first connected device when no ADB device is specified ([5f952f3](https://github.com/ReVanced/revanced-cli/commit/5f952f35f5cb388b6509b2b4d905b8143ebc7996))
### Features
* Set patch options via CLI ([#336](https://github.com/ReVanced/revanced-cli/issues/336)) ([2300243](https://github.com/ReVanced/revanced-cli/commit/23002434b2d51c2a3b30b33dd0526261432d90ce))
* Show error about no installation device found at the beginning ([3300e6b](https://github.com/ReVanced/revanced-cli/commit/3300e6b4333ed1c4e6785cb82eca9016fc6d4a20))
* Simplify command and option names and descriptions ([#338](https://github.com/ReVanced/revanced-cli/issues/338)) ([6e7797a](https://github.com/ReVanced/revanced-cli/commit/6e7797a3f0525a8f48af7182157da0d045600ac2))
* Simplify option descriptions ([45c998b](https://github.com/ReVanced/revanced-cli/commit/45c998b335b65ac233fece8b804dc7410142691c))
### BREAKING CHANGES
* Options have been renamed.
* This commit changes various CLI options and removes the `options.json` file. Instead, patch options can now be passed via CLI options
# [5.0.0-dev.11](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.10...v5.0.0-dev.11) (2024-11-10)
### Bug Fixes
* List if patch option is required ([#346](https://github.com/ReVanced/revanced-cli/issues/346)) ([98ff0c3](https://github.com/ReVanced/revanced-cli/commit/98ff0c34fa71c3b3ecd96157d45a30ee2b8979c6))
# [5.0.0-dev.10](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.9...v5.0.0-dev.10) (2024-11-05)
### Bug Fixes
* Make CLI ArgGroup non-exclusive to be able to disable and enable patches at the same time ([1bb0d13](https://github.com/ReVanced/revanced-cli/commit/1bb0d13726fd5790c59cb6d28df3618c7606710d))
# [5.0.0-dev.9](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.8...v5.0.0-dev.9) (2024-11-05)
### Bug Fixes
* Print in new line correctly ([c2dc9d7](https://github.com/ReVanced/revanced-cli/commit/c2dc9d76be33c98284741e23c406500483c47753))
# [5.0.0-dev.8](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.7...v5.0.0-dev.8) (2024-10-17)
# [5.0.0-dev.7](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.6...v5.0.0-dev.7) (2024-10-16)
### Bug Fixes
* Check for null when no device serial was specified ([1da8ae9](https://github.com/ReVanced/revanced-cli/commit/1da8ae9e46000dd3c4eecd793c559e75012cf535))
# [5.0.0-dev.6](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.5...v5.0.0-dev.6) (2024-10-10)
### Bug Fixes
* Use the first connected device when no ADB device is specified ([5f952f3](https://github.com/ReVanced/revanced-cli/commit/5f952f35f5cb388b6509b2b4d905b8143ebc7996))
# [5.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.4...v5.0.0-dev.5) (2024-09-30)
# [5.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.3...v5.0.0-dev.4) (2024-09-17)
### Bug Fixes
* Make patches selectable by using a mutable collection for the selection option ([751fa1d](https://github.com/ReVanced/revanced-cli/commit/751fa1d889f40c51b291116029fd84f2b051f2f0))
* Make the patch command work without specifying any selection ([ba159a3](https://github.com/ReVanced/revanced-cli/commit/ba159a35a9a99d18a4c1e04128b08ae336a49b3e))
### Features
* Show error about no installation device found at the beginning ([3300e6b](https://github.com/ReVanced/revanced-cli/commit/3300e6b4333ed1c4e6785cb82eca9016fc6d4a20))
# [5.0.0-dev.3](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.2...v5.0.0-dev.3) (2024-08-14)
### Features
* Simplify option descriptions ([45c998b](https://github.com/ReVanced/revanced-cli/commit/45c998b335b65ac233fece8b804dc7410142691c))
# [5.0.0-dev.2](https://github.com/ReVanced/revanced-cli/compare/v5.0.0-dev.1...v5.0.0-dev.2) (2024-08-14)
### Features
* Simplify command and option names and descriptions ([#338](https://github.com/ReVanced/revanced-cli/issues/338)) ([6e7797a](https://github.com/ReVanced/revanced-cli/commit/6e7797a3f0525a8f48af7182157da0d045600ac2))
### BREAKING CHANGES
* Options have been renamed.
# [5.0.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v4.6.0...v5.0.0-dev.1) (2024-08-12) # [5.0.0-dev.1](https://github.com/ReVanced/revanced-cli/compare/v4.6.0...v5.0.0-dev.1) (2024-08-12)

View File

@ -63,11 +63,11 @@
![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/ReVanced/revanced-cli/release.yml) ![GitHub Workflow Status (with event)](https://img.shields.io/github/actions/workflow/status/ReVanced/revanced-cli/release.yml)
![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-yellow.svg) ![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-yellow.svg)
Command-line application to use ReVanced. Command line application to use ReVanced.
## ❓ About ## ❓ About
ReVanced CLI is a command-line application that uses [ReVanced Patcher](https://github.com/revanced/revanced-patcher) to patch Android apps. ReVanced CLI is a command line application that uses [ReVanced Patcher](https://github.com/revanced/revanced-patcher) to patch Android apps.
## 💪 Features ## 💪 Features
@ -75,7 +75,7 @@ Some of the features ReVanced CLI provides are:
- 💉 **Patch apps**: Harness ReVanced Patcher to patch Android apps - 💉 **Patch apps**: Harness ReVanced Patcher to patch Android apps
- 💾 **Install and uninstall apps**: Install and uninstall Apps via ADB, - 💾 **Install and uninstall apps**: Install and uninstall Apps via ADB,
using the Android package manager or by mounting using root permissions using the Android package manager, or by mounting using root permissions
- 📃 **List patches from patch bundles**: List available patches, compatible packages, and versions - 📃 **List patches from patch bundles**: List available patches, compatible packages, and versions
- 💪 **Flexibility and functionality**: Apply any combination of patches to any version of Android apps - 💪 **Flexibility and functionality**: Apply any combination of patches to any version of Android apps

View File

@ -59,10 +59,10 @@ tasks {
} }
shadowJar { shadowJar {
exclude("/prebuilt/linux/aapt", "/prebuilt/windows/aapt.exe", "/prebuilt/*/aapt_*")
minimize { minimize {
exclude(dependency("org.jetbrains.kotlin:.*"))
exclude(dependency("org.bouncycastle:.*")) exclude(dependency("org.bouncycastle:.*"))
exclude(dependency("app.revanced:revanced-patcher")) exclude(dependency("app.revanced:.*"))
} }
} }

View File

@ -4,9 +4,9 @@ To use ReVanced CLI, you will need to fulfill specific requirements.
## 🤝 Requirements ## 🤝 Requirements
- Java Runtime Environment 11 ([Azul Zulu JRE](https://www.azul.com/downloads/?version=java-11-lts&package=jre#zulu) or [OpenJDK](https://jdk.java.net/archive/)) - Java Runtime Environment 11 ([Azul Zulu JRE](https://www.azul.com/downloads/?version=java-11-lts&package=jdk#zulu) or [OpenJDK](https://jdk.java.net/archive/))
- [Android Debug Bridge (ADB)](https://developer.android.com/studio/command-line/adb) if you want to install the patched APK file on your device - [Android Debug Bridge (ADB)](https://developer.android.com/studio/command-line/adb) if you want to install the patched APK file on your device
- x86 or x86-64 (For [other architectures](https://github.com/ReVanced/revanced-manager/tree/main/android/app/src/main/jniLibs) use the `--custom-aapt2-binary` option) - An ABI other than ARMv7 such as x86 or x86-64 (or a custom AAPT binary that supports ARMv7)
## ⏭️ Whats next ## ⏭️ Whats next

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 patches.rvp java -jar revanced-cli.jar list-patches --with-packages --with-versions --with-options revanced-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 -p patches.rvp input.apk java -jar revanced-cli.jar patch -b revanced-patches.rvp input.apk
``` ```
You can also use multiple RVP files: You can also use multiple patch bundles:
```bash ```bash
java -jar revanced-cli.jar patch -p patches.rvp -p another-patches.rvp input.apk java -jar revanced-cli.jar patch -b revanced-patches.rvp -b another-patches.rvp input.apk
``` ```
To change the default set of enabled or disabled patches, use the option `-e` or `-d` to enable or disable specific patches. To change the default set of used patches, use the option `-i` or `-e` to use or disuse specific patches.
You can use the `list-patches` command to see which patches are enabled by default. You can use the `list-patches` command to see which patches are used by default.
To only enable specific patches, you can use the option `--exclusive` combined with `-e`. To only use specific patches, you can use the option `--exclusive` combined with `-i`.
Remember that the options `-e` and `-d` match the patch's name exactly. Here is an example: Remember that the options `-i` and `-e` match the patch's name exactly. Here is an example:
```bash ```bash
java -jar revanced-cli.jar patch -p patches.rvp --exclusive -e "Patch name" -e "Another patch name" input.apk java -jar revanced-cli.jar patch -b revanced-patches.rvp --exclusive -i "Patch name" -i "Another patch name" input.apk
``` ```
You can also use the options `--ei` or `--di` to enable or disable patches by their index. You can also use the options `--ii` and `--ie` to use or disuse patches by their index.
This is useful, if two patches happen to have the same name, or if typing the names is too cumbersome. This is useful, if two patches happen to have the same name.
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 patches.rvp java -jar revanced-cli.jar list-patches revanced-patches.rvp
``` ```
Then you can use the indices to enable or disable patches: Then you can use the indices to use or disuse patches:
```bash ```bash
java -jar revanced-cli.jar patch -p patches.rvp --ei 123 --di 456 input.apk java -jar revanced-cli.jar patch -b revanced-patches.rvp --ii 123 --ie 456 input.apk
``` ```
You can combine the option `-e`, `-d`, `--ei`, `--di` and `--exclusive`. Here is an example: You can combine the option `-i`, `-e`, `--ii`, `--ie` and `--exclusive`. Here is an example:
```bash ```bash
java -jar revanced-cli.jar patch -p patches.rvp --exclusive -e "Patch name" --ei 123 input.apk java -jar revanced-cli.jar patch -b revanced-patches.rvp --exclusive -i "Patch name" --ii 123 input.apk
``` ```
> [!TIP] > [!TIP]
> You can use the option `-i` to automatically install the patched app after patching. > You can use the option `-d` 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 patches.rvp java -jar revanced-cli.jar list-patches --with-options revanced-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,18 +91,18 @@ 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 -p patches.rvp -e "Patch name" -Okey1=value1 -Okey2=value2 input.apk java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1=value1 -Okey2=value2 input.apk
``` ```
If you want to set the option value to `null`, you can omit the value: If you want to set a value to `null`, you can omit the value:
```bash ```bash
java -jar revanced-cli.jar patch -p patches.rvp -i "Patch name" -Okey1 input.apk java -jar revanced-cli.jar patch -b revanced-patches.rvp -i "Patch name" -Okey1 input.apk
``` ```
> [!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.
> The value types can be seen when listing patches with the option `--with-options`. > Option 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 -p patches.rvp -i "Patch name" -Okey1 input.apk
> 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 -p patches.rvp -e "Patch name" -OstringKey=\'1\' input.apk > java -jar revanced-cli.jar -b revanced-patches.rvp -i "Patch name" -OstringKey=\'1\' input.apk
> ``` > ```
## 📦 Install an app manually ## 📦 Install an app manually

View File

@ -1,4 +1,4 @@
org.gradle.parallel = true org.gradle.parallel = true
org.gradle.caching = true org.gradle.caching = true
kotlin.code.style = official kotlin.code.style = official
version = 5.0.1 version = 5.0.0-dev.1

View File

@ -1,10 +1,10 @@
[versions] [versions]
shadow = "8.1.1" shadow = "8.1.1"
kotlin = "2.0.20" kotlin = "2.0.0"
kotlinx = "1.8.1" kotlinx = "1.8.1"
picocli = "4.7.6" picocli = "4.7.6"
revanced-patcher = "21.0.0" revanced-patcher = "20.0.0"
revanced-library = "3.1.0" revanced-library = "3.0.0"
[libraries] [libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }

2401
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
"@saithodev/semantic-release-backmerge": "^4.0.1", "@saithodev/semantic-release-backmerge": "^4.0.1",
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.10.1", "gradle-semantic-release-plugin": "^1.9.1",
"semantic-release": "^24.1.2" "semantic-release": "^23.0.8"
} }
} }

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 from RVP files.", "with the patches in the supplied patch bundles.",
], ],
) )
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 RVP files."], description = ["Paths to patch bundles."],
arity = "1..*", arity = "1..*",
) )
private lateinit var patchesFiles: Set<File> private lateinit var patchBundles: 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(patchesFiles) val patches = loadPatchesFromJar(patchBundles)
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 RVP files."], description = ["List patches from supplied patch bundles."],
) )
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 RVP files."], description = ["Paths to patch bundles."],
arity = "1..*", arity = "1..*",
) )
private lateinit var patchesFiles: Set<File> private lateinit var patchBundles: 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 RVP files."], description = ["List the index of each patch in relation to the supplied patch bundles."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var withIndex: Boolean = true private var withIndex: Boolean = true
@ -89,7 +89,6 @@ internal object ListPatchesCommand : Runnable {
buildString { buildString {
appendLine("Title: $title") appendLine("Title: $title")
description?.let { appendLine("Description: $it") } description?.let { appendLine("Description: $it") }
appendLine("Required: $required")
default?.let { default?.let {
appendLine("Key: $key") appendLine("Key: $key")
append("Default: $it") append("Default: $it")
@ -112,8 +111,6 @@ internal object ListPatchesCommand : Runnable {
if (withDescriptions) append("\nDescription: ${patch.description}") if (withDescriptions) append("\nDescription: ${patch.description}")
append("\nEnabled: ${patch.use}")
if (withOptions && patch.options.isNotEmpty()) { if (withOptions && patch.options.isNotEmpty()) {
appendLine("\nOptions:") appendLine("\nOptions:")
append( append(
@ -138,7 +135,7 @@ internal object ListPatchesCommand : Runnable {
compatiblePackages?.any { (compatiblePackageName, _) -> compatiblePackageName == name } compatiblePackages?.any { (compatiblePackageName, _) -> compatiblePackageName == name }
?: withUniversalPatches ?: withUniversalPatches
val patches = loadPatchesFromJar(patchesFiles).withIndex().toList() val patches = loadPatchesFromJar(patchBundles).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

@ -29,28 +29,28 @@ internal object PatchCommand : Runnable {
@Spec @Spec
private lateinit var spec: CommandSpec private lateinit var spec: CommandSpec
@ArgGroup(exclusive = false, multiplicity = "0..*") @ArgGroup(multiplicity = "0..*")
private var selection = mutableSetOf<Selection>() private lateinit var selection: Set<Selection>
internal class Selection { internal class Selection {
@ArgGroup(exclusive = false) @ArgGroup(exclusive = false, multiplicity = "1")
internal var enabled: EnableSelection? = null internal var include: IncludeSelection? = null
internal class EnableSelection { internal class IncludeSelection {
@ArgGroup(multiplicity = "1") @ArgGroup(multiplicity = "1")
internal lateinit var selector: EnableSelector internal lateinit var selector: IncludeSelector
internal class EnableSelector { internal class IncludeSelector {
@CommandLine.Option( @CommandLine.Option(
names = ["-e", "--enable"], names = ["-i", "--include"],
description = ["Name of the patch."], description = ["The name of the patch."],
required = true, required = true,
) )
internal var name: String? = null internal var name: String? = null
@CommandLine.Option( @CommandLine.Option(
names = ["--ei"], names = ["--ii"],
description = ["Index of the patch in the combined list of the supplied RVP files."], description = ["The index of the patch in the combined list of all supplied patch bundles."],
required = true, required = true,
) )
internal var index: Int? = null internal var index: Int? = null
@ -58,31 +58,31 @@ internal object PatchCommand : Runnable {
@CommandLine.Option( @CommandLine.Option(
names = ["-O", "--options"], names = ["-O", "--options"],
description = ["Option values keyed by option keys."], description = ["The option values keyed by the option keys."],
mapFallbackValue = CommandLine.Option.NULL_VALUE, mapFallbackValue = CommandLine.Option.NULL_VALUE,
converter = [OptionKeyConverter::class, OptionValueConverter::class], converter = [OptionKeyConverter::class, OptionValueConverter::class],
) )
internal var options = mutableMapOf<String, Any?>() internal var options = mutableMapOf<String, Any?>()
} }
@ArgGroup(exclusive = false) @ArgGroup(exclusive = false, multiplicity = "1")
internal var disable: DisableSelection? = null internal var exclude: ExcludeSelection? = null
internal class DisableSelection { internal class ExcludeSelection {
@ArgGroup(multiplicity = "1") @ArgGroup(multiplicity = "1")
internal lateinit var selector: DisableSelector internal lateinit var selector: ExcludeSelector
internal class DisableSelector { internal class ExcludeSelector {
@CommandLine.Option( @CommandLine.Option(
names = ["-d", "--disable"], names = ["-e", "--exclude"],
description = ["Name of the patch."], description = ["The name of the patch."],
required = true, required = true,
) )
internal var name: String? = null internal var name: String? = null
@CommandLine.Option( @CommandLine.Option(
names = ["--di"], names = ["--ie"],
description = ["Index of the patch in the combined list of the supplied RVP files."], description = ["The index of the patch in the combined list of all supplied patch bundles."],
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 = ["Disable all patches except the ones enabled."], description = ["Only include patches that are explicitly specified to be included."],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var exclusive = false private var exclusive = false
@CommandLine.Option( @CommandLine.Option(
names = ["-f", "--force"], names = ["-f", "--force"],
description = ["Don't check for compatibility with the supplied APK's version."], description = ["Bypass compatibility checks for 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 path as the supplied APK file."], description = ["Path to save the patched APK file to. Defaults to the same directory 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 = ["-i", "--install"], names = ["-d", "--device-serial"],
description = ["Serial of the ADB device to install to. If not specified, the first connected device will be used."], description = ["ADB device serial to install to. If not supplied, 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 the patched APK file by mounting."], description = ["Install by mounting the patched APK file."],
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 file containing a private key and certificate pair to sign the patched APK file with. " + "Path to the keystore 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 = ["Password of the keystore. 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 private var keyStorePassword: String? = null // Empty password by default
@CommandLine.Option( @CommandLine.Option(
names = ["--keystore-entry-alias"], names = ["--keystore-entry-alias"],
description = ["Alias of the private key and certificate pair keystore entry."], description = ["The alias of the keystore entry to sign the patched APK file with."],
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 = ["Password of the keystore entry."], description = ["The password of the entry from the keystore for the key to sign the patched APK file with."],
) )
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 = ["--purge"], names = ["-p", "--purge"],
description = ["Purge temporary files directory after patching."], description = ["Purge the temporary resource cache 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 patch."], description = ["APK file to be patched."],
arity = "1", arity = "1..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 = ["-p", "--patches"], names = ["-b", "--patch-bundle"],
description = ["One or more path to RVP files."], description = ["One or more bundles of patches."],
required = true, required = true,
) )
@Suppress("unused") @Suppress("unused")
private fun setPatchesFile(patchesFiles: Set<File>) { private fun setPatchBundles(patchBundles: Set<File>) {
patchesFiles.firstOrNull { !it.exists() }?.let { patchBundles.firstOrNull { !it.exists() }?.let {
throw CommandLine.ParameterException(spec.commandLine(), "${it.name} can't be found") throw CommandLine.ParameterException(spec.commandLine(), "Patch bundle ${it.name} does not exist")
} }
this.patchesFiles = patchesFiles this.patchBundles = patchBundles
} }
private var patchesFiles = emptySet<File>() private var patchBundles = emptySet<File>()
@CommandLine.Option( @CommandLine.Option(
names = ["--custom-aapt2-binary"], names = ["--custom-aapt2-binary"],
@ -242,44 +242,16 @@ internal object PatchCommand : Runnable {
) )
val keystoreFilePath = val keystoreFilePath =
keyStoreFilePath ?: outputFilePath.parentFile keystoreFilePath ?: outputFilePath.parentFile
.resolve("${outputFilePath.nameWithoutExtension}.keystore") .resolve("${outputFilePath.nameWithoutExtension}.keystore")
val installer = if (deviceSerial != null) {
val deviceSerial = deviceSerial!!.ifEmpty { null }
try {
if (mount) {
AdbRootInstaller(deviceSerial)
} else {
AdbInstaller(deviceSerial)
}
} catch (e: DeviceNotFoundException) {
if (deviceSerial?.isNotEmpty() == true) {
logger.severe(
"Device with serial $deviceSerial not found to install to. " +
"Ensure the device is connected and the serial is correct when using the --install option.",
)
} else {
logger.severe(
"No device has been found to install to. " +
"Ensure a device is connected when using the --install option.",
)
}
return
}
} else {
null
}
// endregion // endregion
// region Load patches // region Load patches
logger.info("Loading patches") logger.info("Loading patches")
val patches = loadPatchesFromJar(patchesFiles) val patches = loadPatchesFromJar(patchBundles)
// endregion // endregion
@ -291,6 +263,7 @@ internal object PatchCommand : Runnable {
patcherTemporaryFilesPath, patcherTemporaryFilesPath,
aaptBinaryPath?.path, aaptBinaryPath?.path,
patcherTemporaryFilesPath.absolutePath, patcherTemporaryFilesPath.absolutePath,
true,
), ),
).use { patcher -> ).use { patcher ->
val packageName = patcher.context.packageMetadata.packageName val packageName = patcher.context.packageMetadata.packageName
@ -301,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.enabled != null }.associate { selection.filter { it.include != null }.associate {
val enabledSelection = it.enabled!! val includeSelection = it.include!!
(enabledSelection.selector.name ?: patchesList[enabledSelection.selector.index!!].name!!) to (includeSelection.selector.name ?: patchesList[includeSelection.selector.index!!].name!!) to
enabledSelection.options includeSelection.options
}.let(filteredPatches::setOptions) }.let(filteredPatches::setOptions)
patcher += filteredPatches patcher += filteredPatches
@ -356,8 +329,16 @@ internal object PatchCommand : Runnable {
// region Install. // region Install.
deviceSerial?.let { deviceSerial?.let {
val deviceSerial = it.ifEmpty { null }
runBlocking { runBlocking {
when (val result = installer!!.install(Installer.Apk(outputFilePath, packageName))) { val result = if (mount) {
AdbRootInstaller(deviceSerial)
} else {
AdbInstaller(deviceSerial)
}.install(Installer.Apk(outputFilePath, packageName))
when (result) {
RootInstallerResult.FAILURE -> logger.severe("Failed to mount the patched APK file") RootInstallerResult.FAILURE -> logger.severe("Failed to mount the patched APK file")
is AdbInstallerResult.Failure -> logger.severe(result.exception.toString()) is AdbInstallerResult.Failure -> logger.severe(result.exception.toString())
else -> logger.info("Installed the patched APK file") else -> logger.info("Installed the patched APK file")
@ -384,21 +365,21 @@ internal object PatchCommand : Runnable {
packageName: String, packageName: String,
packageVersion: String, packageVersion: String,
): Set<Patch<*>> = buildSet { ): Set<Patch<*>> = buildSet {
val enabledPatchesByName = val includedPatchesByName =
selection.mapNotNull { it.enabled?.selector?.name }.toSet() selection.asSequence().mapNotNull { it.include?.selector?.name }.toSet()
val enabledPatchesByIndex = val includedPatchesByIndex =
selection.mapNotNull { it.enabled?.selector?.index }.toSet() selection.asSequence().mapNotNull { it.include?.selector?.index }.toSet()
val disabledPatches = val excludedPatches =
selection.mapNotNull { it.disable?.selector?.name }.toSet() selection.asSequence().mapNotNull { it.exclude?.selector?.name }.toSet()
val disabledPatchesByIndex = val excludedPatchesByIndex =
selection.mapNotNull { it.disable?.selector?.index }.toSet() selection.asSequence().mapNotNull { it.exclude?.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 isManuallyDisabled = patchName in disabledPatches || i in disabledPatchesByIndex val isManuallyExcluded = patchName in excludedPatches || i in excludedPatchesByIndex
if (isManuallyDisabled) return@patchLoop logger.info("\"$patchName\" disabled manually") if (isManuallyExcluded) return@patchLoop logger.info("\"$patchName\" excluded 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 ->
@ -428,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 isEnabled = !exclusive && patch.use val isIncluded = !exclusive && patch.use
val isManuallyEnabled = patchName in enabledPatchesByName || i in enabledPatchesByIndex val isManuallyIncluded = patchName in includedPatchesByName || i in includedPatchesByIndex
if (!(isEnabled || isManuallyEnabled)) { if (!(isIncluded || isManuallyIncluded)) {
return@patchLoop logger.info("\"$patchName\" disabled") return@patchLoop logger.info("\"$patchName\" excluded")
} }
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."], description = ["Install an APK file to devices with the supplied ADB device serials"],
) )
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 = ["Serial of ADB devices. If not supplied, the first connected device will be used."], description = ["ADB device serials. 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."], description = ["Uninstall a patched app from the devices with the supplied ADB device serials"],
) )
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 = ["Serial of ADB devices. If not supplied, the first connected device will be used."], description = ["ADB device serials. 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 the patched APK file by unmounting."], description = ["Uninstall by unmounting the patched APK file"],
showDefaultValue = ALWAYS, showDefaultValue = ALWAYS,
) )
private var unmount: Boolean = false private var unmount: Boolean = false