mirror of
https://github.com/ReVanced/revanced-cli.git
synced 2026-01-11 13:56:18 +00:00
Compare commits
24 Commits
v3.0.0-dev
...
v3.0.1-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48e1689223 | ||
|
|
7580f5c2fc | ||
|
|
dd6c1392eb | ||
|
|
2a3dbafd17 | ||
|
|
9e39a6f8e4 | ||
|
|
2d5a7fdf1e | ||
|
|
be5d812dff | ||
|
|
c93186fb97 | ||
|
|
3a198052bb | ||
|
|
0f3e090418 | ||
|
|
6aed946183 | ||
|
|
924c1f80ec | ||
|
|
8dd709b6ef | ||
|
|
139e7facac | ||
|
|
1d26e572f7 | ||
|
|
2c7fcaf4ad | ||
|
|
52c3be23f2 | ||
|
|
3dd875d14c | ||
|
|
0350b7f1a2 | ||
|
|
a3d8705e89 | ||
|
|
a536c9f815 | ||
|
|
11c3a6cfd4 | ||
|
|
a5851f0c1a | ||
|
|
963ae3a5fa |
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@@ -33,10 +33,11 @@ jobs:
|
||||
build
|
||||
node_modules
|
||||
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
|
||||
- name: Build with Gradle
|
||||
- name: Build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./gradlew build --no-daemon
|
||||
# Cleaning is necessary to avoid uploading two identical artifacts with different versions
|
||||
run: ./gradlew clean --no-daemon
|
||||
- name: Setup semantic-release
|
||||
run: npm install
|
||||
- name: Release
|
||||
|
||||
89
CHANGELOG.md
89
CHANGELOG.md
@@ -1,3 +1,92 @@
|
||||
## [3.0.1-dev.1](https://github.com/ReVanced/revanced-cli/compare/v3.0.0...v3.0.1-dev.1) (2023-08-28)
|
||||
|
||||
# [3.0.0](https://github.com/ReVanced/revanced-cli/compare/v2.22.0...v3.0.0) (2023-08-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* also delete temporary files when uninstalling ([52c3be2](https://github.com/ReVanced/revanced-cli/commit/52c3be23f2915dccaee7f9941413c8f81e14acc8))
|
||||
* delete temporary files after root installation ([a3d8705](https://github.com/ReVanced/revanced-cli/commit/a3d8705e89732a0dd4f51de28c405b6af13c8633))
|
||||
* do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459))
|
||||
* do not use absolute path from custom AAPT2 binary option ([a9c2a5f](https://github.com/ReVanced/revanced-cli/commit/a9c2a5f096627dbbf8ab1b8da26fb14529ce6bc3))
|
||||
* filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0))
|
||||
* fix running commands not running ([2c7fcaf](https://github.com/ReVanced/revanced-cli/commit/2c7fcaf4add65a12052afc5bef779dbc73debd69))
|
||||
* only check once for patch options ([11c3a6c](https://github.com/ReVanced/revanced-cli/commit/11c3a6cfd4fe59ba5d703358634a1853e1cc22a5))
|
||||
* print original instead of kebab cased names ([5eaad33](https://github.com/ReVanced/revanced-cli/commit/5eaad33dc1fbd24c36e1498f04e21d068e85f53e))
|
||||
* print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4))
|
||||
* specify correct class containing entry-point ([1fcc591](https://github.com/ReVanced/revanced-cli/commit/1fcc591222ab67112f2b78174a8b94106846838c))
|
||||
* use correct option name ([f8972ea](https://github.com/ReVanced/revanced-cli/commit/f8972eac3e5ee0a4a186c12cbe711925656d657b))
|
||||
|
||||
|
||||
* refactor!: restructure code ([07da528](https://github.com/ReVanced/revanced-cli/commit/07da528ce2223582f84bf64d2fec69714c647ddc))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090))
|
||||
* add options command ([9edbbf3](https://github.com/ReVanced/revanced-cli/commit/9edbbf31635603f89fc7bc5dcc6c023d4cdbb5a6))
|
||||
* Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de))
|
||||
* Improve command line argument descriptions ([f9cf7d2](https://github.com/ReVanced/revanced-cli/commit/f9cf7d21b7f1c2f11234d604a1047b9d2b165f83))
|
||||
* properly make use of logging facade ([41898d7](https://github.com/ReVanced/revanced-cli/commit/41898d7547690e3130372414515c5645e5dc2634))
|
||||
* show full package name when listing patches ([#240](https://github.com/ReVanced/revanced-cli/issues/240)) ([7174364](https://github.com/ReVanced/revanced-cli/commit/7174364ef8ef5d6ce8351a8340f9c1a5b58eac3c))
|
||||
* use better logging text ([b0e748d](https://github.com/ReVanced/revanced-cli/commit/b0e748daff527ee7f417b3069882e074896fc131))
|
||||
* use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134))
|
||||
* use separate command to list patches ([b74213f](https://github.com/ReVanced/revanced-cli/commit/b74213f66e0d04d3a0ae6197d069631388e06580))
|
||||
* use separate command to patch ([32da961](https://github.com/ReVanced/revanced-cli/commit/32da961d57537e99b39fd92b625a1c73f8314bc6))
|
||||
* use separate command to uninstall ([c0cc909](https://github.com/ReVanced/revanced-cli/commit/c0cc90962646cfffd5e2730ae556423271a7990b))
|
||||
* use simpler log ([ba758f0](https://github.com/ReVanced/revanced-cli/commit/ba758f00f4ce18791439b7e72fe1ad2e7f11f8af))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* This introduces major changes to how ReVanced CLI is used from the command line.
|
||||
|
||||
# [3.0.0-dev.10](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.9...v3.0.0-dev.10) (2023-08-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* filtration of patches malfunctioning ([2d5a7fd](https://github.com/ReVanced/revanced-cli/commit/2d5a7fdf1eb2e13f5013a790b03f09851b167fe0))
|
||||
|
||||
# [3.0.0-dev.9](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.8...v3.0.0-dev.9) (2023-08-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Check for missing integrations ([c93186f](https://github.com/ReVanced/revanced-cli/commit/c93186fb9700907e65f33442e88073783cc163de))
|
||||
|
||||
# [3.0.0-dev.8](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-08-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* do not delete output file ([0f3e090](https://github.com/ReVanced/revanced-cli/commit/0f3e090418771e951dfd15e5c193421f72cbe459))
|
||||
|
||||
# [3.0.0-dev.7](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-08-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* print stack trace when a patch failed ([924c1f8](https://github.com/ReVanced/revanced-cli/commit/924c1f80ec0d17a3bdc07a0fb2015e44c49162e4))
|
||||
|
||||
# [3.0.0-dev.6](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-08-24)
|
||||
|
||||
# [3.0.0-dev.5](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-08-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* also delete temporary files when uninstalling ([52c3be2](https://github.com/ReVanced/revanced-cli/commit/52c3be23f2915dccaee7f9941413c8f81e14acc8))
|
||||
* delete temporary files after root installation ([a3d8705](https://github.com/ReVanced/revanced-cli/commit/a3d8705e89732a0dd4f51de28c405b6af13c8633))
|
||||
* fix running commands not running ([2c7fcaf](https://github.com/ReVanced/revanced-cli/commit/2c7fcaf4add65a12052afc5bef779dbc73debd69))
|
||||
* only check once for patch options ([11c3a6c](https://github.com/ReVanced/revanced-cli/commit/11c3a6cfd4fe59ba5d703358634a1853e1cc22a5))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add install command ([0350b7f](https://github.com/ReVanced/revanced-cli/commit/0350b7f1a276d9dc795b22442ba4f202855ea090))
|
||||
* use friendly descriptions ([3dd875d](https://github.com/ReVanced/revanced-cli/commit/3dd875d14cca488ade6d21bbd4cce0d481692134))
|
||||
|
||||
# [3.0.0-dev.4](https://github.com/ReVanced/revanced-cli/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-08-24)
|
||||
|
||||
|
||||
|
||||
@@ -52,5 +52,6 @@ tasks {
|
||||
register<DefaultTask>("publish") {
|
||||
group = "publish"
|
||||
description = "Dummy task"
|
||||
dependsOn(build)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,26 @@ Learn how to ReVanced CLI.
|
||||
|
||||
> **Note**: Some patches may require 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.
|
||||
|
||||
- ### 🗑️ Uninstall a patched APK file
|
||||
```bash
|
||||
java -jar revanced-cli.jar uninstall \
|
||||
java -jar revanced-cli.jar utility uninstall \
|
||||
--package-name <package-name> \
|
||||
<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`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
kotlin.code.style = official
|
||||
version = 3.0.0-dev.4
|
||||
version = 3.0.1-dev.1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[versions]
|
||||
shadow = "8.1.1"
|
||||
apksig = "8.1.0"
|
||||
apksig = "8.1.1"
|
||||
bcpkix-jdk15on = "1.70"
|
||||
jackson-module-kotlin = "2.14.3"
|
||||
jadb = "2531a28109"
|
||||
@@ -8,7 +8,7 @@ kotlin-reflect = "1.9.0"
|
||||
kotlin-test = "1.8.20-RC"
|
||||
kotlinx-coroutines-core = "1.7.1"
|
||||
picocli = "4.7.3"
|
||||
revanced-patcher = "14.1.0"
|
||||
revanced-patcher = "14.2.1"
|
||||
|
||||
[libraries]
|
||||
apksig = { module = "com.android.tools.build:apksig", version.ref = "apksig" }
|
||||
|
||||
@@ -19,38 +19,33 @@ internal object ListPatchesCommand : Runnable {
|
||||
private val logger = Logger.getLogger(ListPatchesCommand::class.java.name)
|
||||
|
||||
@Parameters(
|
||||
description = ["Paths to patch bundles"],
|
||||
arity = "1..*"
|
||||
description = ["Paths to patch bundles"], arity = "1..*"
|
||||
)
|
||||
lateinit var patchBundles: Array<File>
|
||||
private lateinit var patchBundles: Array<File>
|
||||
|
||||
@Option(
|
||||
names = ["-d", "--with-descriptions"],
|
||||
description = ["List their descriptions"],
|
||||
showDefaultValue = ALWAYS
|
||||
names = ["-d", "--with-descriptions"], description = ["List their descriptions"], showDefaultValue = ALWAYS
|
||||
)
|
||||
var withDescriptions: Boolean = true
|
||||
private var withDescriptions: Boolean = true
|
||||
|
||||
@Option(
|
||||
names = ["-p", "--with-packages"],
|
||||
description = ["List the packages the patches are compatible with"],
|
||||
showDefaultValue = ALWAYS
|
||||
)
|
||||
var withPackages: Boolean = false
|
||||
private var withPackages: Boolean = false
|
||||
|
||||
@Option(
|
||||
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
|
||||
)
|
||||
var withVersions: Boolean = false
|
||||
private var withVersions: Boolean = false
|
||||
|
||||
@Option(
|
||||
names = ["-o", "--with-options"],
|
||||
description = ["List the options of the patches"],
|
||||
showDefaultValue = ALWAYS
|
||||
names = ["-o", "--with-options"], description = ["List the options of the patches"], showDefaultValue = ALWAYS
|
||||
)
|
||||
var withOptions: Boolean = false
|
||||
private var withOptions: Boolean = false
|
||||
|
||||
override fun run() {
|
||||
fun Package.buildString() = buildString {
|
||||
@@ -58,8 +53,7 @@ internal object ListPatchesCommand : Runnable {
|
||||
appendLine("Package name: $name")
|
||||
appendLine("Compatible versions:")
|
||||
append(versions.joinToString("\n") { version -> version }.prependIndent("\t"))
|
||||
} else
|
||||
append("Package name: $name")
|
||||
} else append("Package name: $name")
|
||||
}
|
||||
|
||||
fun PatchOption<*>.buildString() = buildString {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.cli.command
|
||||
|
||||
import app.revanced.cli.command.utility.UtilityCommand
|
||||
import app.revanced.patcher.patch.PatchClass
|
||||
import picocli.CommandLine
|
||||
import picocli.CommandLine.Command
|
||||
@@ -17,12 +18,11 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
object : Handler() {
|
||||
override fun publish(record: LogRecord) = formatter.format(record).let {
|
||||
if (record.level.intValue() > Level.INFO.intValue()) {
|
||||
System.err.write(it.toByteArray())
|
||||
} else {
|
||||
System.out.write(it.toByteArray())
|
||||
}
|
||||
override fun publish(record: LogRecord) = formatter.format(record).toByteArray().let {
|
||||
if (record.level.intValue() > Level.INFO.intValue())
|
||||
System.err.write(it)
|
||||
else
|
||||
System.out.write(it)
|
||||
}
|
||||
|
||||
override fun flush() {
|
||||
@@ -42,7 +42,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
internal typealias PatchList = List<PatchClass>
|
||||
|
||||
object CLIVersionProvider : IVersionProvider {
|
||||
private object CLIVersionProvider : IVersionProvider {
|
||||
override fun getVersion(): Array<String> {
|
||||
Properties().apply {
|
||||
load(MainCommand::class.java.getResourceAsStream("/app/revanced/cli/version.properties"))
|
||||
@@ -60,8 +60,8 @@ object CLIVersionProvider : IVersionProvider {
|
||||
subcommands = [
|
||||
ListPatchesCommand::class,
|
||||
PatchCommand::class,
|
||||
UninstallCommand::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)
|
||||
|
||||
@CommandLine.Parameters(
|
||||
description = ["Paths to patch bundles"],
|
||||
arity = "1..*"
|
||||
description = ["Paths to patch bundles"], arity = "1..*"
|
||||
)
|
||||
lateinit var patchBundles: Array<File>
|
||||
private lateinit var patchBundles: Array<File>
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-p", "--path"],
|
||||
description = ["Path to patch options JSON file"],
|
||||
showDefaultValue = ALWAYS
|
||||
names = ["-p", "--path"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
|
||||
)
|
||||
var path: File = File("options.json")
|
||||
private var path: File = File("options.json")
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-o", "--overwrite"],
|
||||
description = ["Overwrite existing options file"],
|
||||
showDefaultValue = ALWAYS
|
||||
names = ["-o", "--overwrite"], description = ["Overwrite existing options file"], showDefaultValue = ALWAYS
|
||||
)
|
||||
var overwrite: Boolean = false
|
||||
private var overwrite: Boolean = false
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-u", "--update"],
|
||||
description = ["Update existing options by adding missing and removing non-existent options"],
|
||||
showDefaultValue = ALWAYS
|
||||
)
|
||||
var update: Boolean = false
|
||||
private var update: Boolean = false
|
||||
|
||||
override fun run() = if (!path.exists() || overwrite)
|
||||
with(PatchBundleLoader.Jar(*patchBundles)) {
|
||||
if (update) setOptions(path)
|
||||
override fun run() = if (!path.exists() || overwrite) with(PatchBundleLoader.Jar(*patchBundles)) {
|
||||
if (update) setOptions(path)
|
||||
|
||||
Options.serialize(this, prettyPrint = true)
|
||||
.let(path::writeText)
|
||||
}
|
||||
Options.serialize(this, prettyPrint = true).let(path::writeText)
|
||||
}
|
||||
else logger.severe("Options file already exists, use --override to override it")
|
||||
}
|
||||
@@ -19,88 +19,75 @@ import kotlinx.coroutines.runBlocking
|
||||
import picocli.CommandLine
|
||||
import picocli.CommandLine.Help.Visibility.ALWAYS
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.util.logging.Logger
|
||||
|
||||
|
||||
@CommandLine.Command(
|
||||
name = "patch",
|
||||
description = ["Patch the supplied APK file with the supplied patches and integrations"]
|
||||
name = "patch", 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)
|
||||
|
||||
@CommandLine.Parameters(
|
||||
description = ["APK file to be patched"],
|
||||
arity = "1..1"
|
||||
description = ["APK file to be patched"], arity = "1..1"
|
||||
)
|
||||
lateinit var apk: File
|
||||
private lateinit var apk: File
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-b", "--patch-bundle"],
|
||||
description = ["One or more bundles of patches"],
|
||||
required = true
|
||||
names = ["-b", "--patch-bundle"], description = ["One or more bundles of patches"], required = true
|
||||
)
|
||||
var patchBundles = emptyList<File>()
|
||||
private var patchBundles = emptyList<File>()
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-m", "--merge"],
|
||||
description = ["One or more DEX files or containers to merge into the APK"]
|
||||
names = ["-m", "--merge"], description = ["One or more DEX files or containers to merge into the APK"]
|
||||
)
|
||||
var integrations = listOf<File>()
|
||||
private var integrations = listOf<File>()
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-i", "--include"],
|
||||
description = ["List of patches to include"]
|
||||
names = ["-i", "--include"], description = ["List of patches to include"]
|
||||
)
|
||||
var includedPatches = arrayOf<String>()
|
||||
private var includedPatches = arrayOf<String>()
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-e", "--exclude"],
|
||||
description = ["List of patches to exclude"]
|
||||
names = ["-e", "--exclude"], description = ["List of patches to exclude"]
|
||||
)
|
||||
var excludedPatches = arrayOf<String>()
|
||||
private var excludedPatches = arrayOf<String>()
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--options"],
|
||||
description = ["Path to patch options JSON file"],
|
||||
showDefaultValue = ALWAYS
|
||||
names = ["--options"], description = ["Path to patch options JSON file"], showDefaultValue = ALWAYS
|
||||
)
|
||||
var optionsFile: File = File("options.json")
|
||||
private var optionsFile: File = File("options.json")
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--exclusive"],
|
||||
description = ["Only include patches that are explicitly specified to be included"],
|
||||
showDefaultValue = ALWAYS
|
||||
)
|
||||
var exclusive = false
|
||||
private var exclusive = false
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--experimental"],
|
||||
description = ["Ignore patches incompatibility to versions"],
|
||||
names = ["-f","--force"],
|
||||
description = ["Force inclusion of patches that are incompatible with the supplied APK file's version"],
|
||||
showDefaultValue = ALWAYS
|
||||
)
|
||||
var experimental: Boolean = false
|
||||
private var force: Boolean = false
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-o", "--out"],
|
||||
description = ["Path to save the patched APK file to"],
|
||||
required = true
|
||||
names = ["-o", "--out"], description = ["Path to save the patched APK file to"], required = true
|
||||
)
|
||||
lateinit var outputFilePath: File
|
||||
private lateinit var outputFilePath: File
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-d", "--device-serial"],
|
||||
description = ["ADB device serial to install to"],
|
||||
showDefaultValue = ALWAYS
|
||||
names = ["-d", "--device-serial"], description = ["ADB device serial to install to"], showDefaultValue = ALWAYS
|
||||
)
|
||||
var deviceSerial: String? = null
|
||||
private var deviceSerial: String? = null
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--mount"],
|
||||
description = ["Install by mounting the patched package"],
|
||||
showDefaultValue = ALWAYS
|
||||
names = ["--mount"], description = ["Install by mounting the patched APK file"], showDefaultValue = ALWAYS
|
||||
)
|
||||
var mount: Boolean = false
|
||||
private var mount: Boolean = false
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--common-name"],
|
||||
@@ -108,45 +95,51 @@ internal object PatchCommand: Runnable {
|
||||
showDefaultValue = ALWAYS
|
||||
|
||||
)
|
||||
var commonName = "ReVanced"
|
||||
private var commonName = "ReVanced"
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--keystore"],
|
||||
description = ["Path to the keystore to sign the patched APK file with"]
|
||||
names = ["--keystore"], description = ["Path to the keystore to sign the patched APK file with"]
|
||||
)
|
||||
var keystorePath: String? = null
|
||||
private var keystorePath: String? = null
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--password"],
|
||||
description = ["The password of the keystore to sign the patched APK file with"]
|
||||
names = ["--password"], description = ["The password of the keystore to sign the patched APK file with"]
|
||||
)
|
||||
var password = "ReVanced"
|
||||
private var password = "ReVanced"
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-r", "--resource-cache"],
|
||||
description = ["Path to temporary resource cache directory"],
|
||||
showDefaultValue = ALWAYS
|
||||
)
|
||||
var resourceCachePath = File("revanced-resource-cache")
|
||||
private var resourceCachePath = File("revanced-resource-cache")
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["--custom-aapt2-binary"],
|
||||
description = ["Path to a custom AAPT binary to compile resources with"]
|
||||
names = ["--custom-aapt2-binary"], description = ["Path to a custom AAPT binary to compile resources with"]
|
||||
)
|
||||
var aaptBinaryPath = File("")
|
||||
private var aaptBinaryPath = File("")
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ["-p", "--purge"],
|
||||
description = ["Purge the temporary resource cache directory after patching"],
|
||||
showDefaultValue = ALWAYS
|
||||
)
|
||||
var purge: Boolean = false
|
||||
private var purge: Boolean = false
|
||||
|
||||
override fun run() {
|
||||
// region Prepare
|
||||
|
||||
if (!apk.exists()) {
|
||||
logger.severe("Input file ${apk.name} does not exist")
|
||||
logger.severe("APK file ${apk.name} does not exist")
|
||||
return
|
||||
}
|
||||
|
||||
integrations.filter { !it.exists() }.let {
|
||||
if (it.isEmpty()) return@let
|
||||
|
||||
it.forEach { integration ->
|
||||
logger.severe("Integration file ${integration.name} does not exist")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -192,7 +185,10 @@ internal object PatchCommand: Runnable {
|
||||
runBlocking {
|
||||
apply(false).collect { patchResult ->
|
||||
patchResult.exception?.let {
|
||||
logger.severe("${patchResult.patchName} failed:\n${patchResult.exception}")
|
||||
StringWriter().use { writer ->
|
||||
it.printStackTrace(PrintWriter(writer))
|
||||
logger.severe("${patchResult.patchName} failed: $writer")
|
||||
}
|
||||
} ?: logger.info("${patchResult.patchName} succeeded")
|
||||
}
|
||||
}
|
||||
@@ -206,8 +202,7 @@ internal object PatchCommand: Runnable {
|
||||
|
||||
val alignAndSignedFile = sign(
|
||||
apk.newAlignedFile(
|
||||
result,
|
||||
resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk")
|
||||
result, resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_aligned.apk")
|
||||
)
|
||||
)
|
||||
|
||||
@@ -218,7 +213,6 @@ internal object PatchCommand: Runnable {
|
||||
|
||||
if (purge) {
|
||||
logger.info("Purging temporary files")
|
||||
outputFilePath.delete()
|
||||
purge(resourceCachePath)
|
||||
}
|
||||
|
||||
@@ -231,8 +225,8 @@ internal object PatchCommand: Runnable {
|
||||
* - [includedPatches] (explicitly included)
|
||||
* - [excludedPatches] (explicitly excluded)
|
||||
* - [exclusive] (only include patches that are explicitly included)
|
||||
* - [experimental] (ignore patches incompatibility to versions)
|
||||
* - package name and version of the input APK file (if [experimental] is false)
|
||||
* - [force] (ignore patches incompatibility to versions)
|
||||
* - Package name and version of the input APK file (if [force] is false)
|
||||
*
|
||||
* @param patches The patches to filter.
|
||||
* @return The filtered patches.
|
||||
@@ -244,90 +238,38 @@ internal object PatchCommand: Runnable {
|
||||
patches.forEach patch@{ patch ->
|
||||
val formattedPatchName = patch.patchName.lowercase().replace(" ", "-")
|
||||
|
||||
/**
|
||||
* Check if the patch is explicitly excluded.
|
||||
*
|
||||
* Cases:
|
||||
* 1. -e patch.name
|
||||
* 2. -i patch.name -e patch.name
|
||||
*/
|
||||
val explicitlyExcluded = excludedPatches.contains(formattedPatchName)
|
||||
if (explicitlyExcluded) return@patch logger.info("Excluding ${patch.patchName}")
|
||||
|
||||
/**
|
||||
* Check if the patch is explicitly excluded.
|
||||
*
|
||||
* Cases:
|
||||
* 1. -e patch.name
|
||||
* 2. -i patch.name -e patch.name
|
||||
*/
|
||||
// If the patch is explicitly included, it will be included if [exclusive] is false.
|
||||
val explicitlyIncluded = exclusive && includedPatches.contains(formattedPatchName)
|
||||
|
||||
val excluded = excludedPatches.contains(formattedPatchName)
|
||||
if (excluded) return@patch logger.info("Excluding ${patch.patchName}")
|
||||
// If the patch is implicitly included, it will be only included if [exclusive] is false.
|
||||
val implicitlyIncluded = !exclusive && patch.include
|
||||
|
||||
/**
|
||||
* Check if the patch is constrained to packages.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check if the patch is constrained to packages.
|
||||
*/
|
||||
val included = implicitlyIncluded || explicitlyIncluded
|
||||
if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1.
|
||||
|
||||
// At last make sure the patch is compatible with the supplied APK files package name and version.
|
||||
patch.compatiblePackages?.let { packages ->
|
||||
packages.singleOrNull { it.name == packageName }?.let { `package` ->
|
||||
/**
|
||||
* Check if the package version matches.
|
||||
* If experimental is true, version matching will be skipped.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check if the package version matches.
|
||||
* If experimental is true, version matching will be skipped.
|
||||
*/
|
||||
|
||||
val matchesVersion = experimental || `package`.versions.let {
|
||||
val matchesVersion = force || `package`.versions.let {
|
||||
it.isEmpty() || it.any { version -> version == packageVersion }
|
||||
}
|
||||
|
||||
if (!matchesVersion) return@patch logger.warning(
|
||||
"${patch.patchName} is incompatible with version $packageVersion. " +
|
||||
"This patch is only compatible with version " +
|
||||
packages.joinToString(";") { pkg ->
|
||||
"${pkg.name}: ${pkg.versions.joinToString(", ")}"
|
||||
}
|
||||
)
|
||||
if (!matchesVersion) return@patch logger.warning("${patch.patchName} is incompatible with version $packageVersion. " + "This patch is only compatible with version " + packages.joinToString(
|
||||
";"
|
||||
) { pkg ->
|
||||
"${pkg.name}: ${pkg.versions.joinToString(", ")}"
|
||||
})
|
||||
|
||||
} ?: return@patch logger.fine(
|
||||
"${patch.patchName} is incompatible with $packageName. " +
|
||||
"This patch is only compatible with " +
|
||||
packages.joinToString(", ") { `package` -> `package`.name }
|
||||
)
|
||||
} ?: return@patch logger.fine("${patch.patchName} is incompatible with $packageName. "
|
||||
+ "This patch is only compatible with "
|
||||
+ packages.joinToString(", ") { `package` -> `package`.name })
|
||||
|
||||
return@let
|
||||
} ?: logger.fine("$formattedPatchName: No constraint on packages.")
|
||||
|
||||
/**
|
||||
* Check if the patch is explicitly included.
|
||||
*
|
||||
* Cases:
|
||||
* 1. --exclusive
|
||||
* 2. --exclusive -i patch.name
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check if the patch is explicitly included.
|
||||
*
|
||||
* Cases:
|
||||
* 1. --exclusive
|
||||
* 2. --exclusive -i patch.name
|
||||
*/
|
||||
|
||||
val explicitlyIncluded = includedPatches.contains(formattedPatchName)
|
||||
|
||||
val implicitlyIncluded = !exclusive && patch.include // Case 3.
|
||||
val exclusivelyIncluded = exclusive && explicitlyIncluded // Case 2.
|
||||
|
||||
val included = implicitlyIncluded || exclusivelyIncluded
|
||||
if (!included) return@patch logger.info("${patch.patchName} excluded by default") // Case 1.
|
||||
|
||||
logger.fine("Adding $formattedPatchName")
|
||||
|
||||
add(patch)
|
||||
@@ -341,8 +283,7 @@ internal object PatchCommand: Runnable {
|
||||
* @param outputFile The file to save the aligned APK to.
|
||||
*/
|
||||
private fun File.newAlignedFile(
|
||||
result: PatcherResult,
|
||||
outputFile: File
|
||||
result: PatcherResult, outputFile: File
|
||||
): File {
|
||||
logger.info("Aligning $name")
|
||||
|
||||
@@ -351,23 +292,20 @@ internal object PatchCommand: Runnable {
|
||||
ZipFile(outputFile).use { file ->
|
||||
result.dexFiles.forEach {
|
||||
file.addEntryCompressData(
|
||||
ZipEntry.createWithName(it.name),
|
||||
it.stream.readBytes()
|
||||
ZipEntry.createWithName(it.name), it.stream.readBytes()
|
||||
)
|
||||
}
|
||||
|
||||
result.resourceFile?.let {
|
||||
file.copyEntriesFromFileAligned(
|
||||
ZipFile(it),
|
||||
ZipAligner::getEntryAlignment
|
||||
ZipFile(it), ZipAligner::getEntryAlignment
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Do not compress result.doNotCompress
|
||||
|
||||
file.copyEntriesFromFileAligned(
|
||||
ZipFile(this),
|
||||
ZipAligner::getEntryAlignment
|
||||
ZipFile(this), ZipAligner::getEntryAlignment
|
||||
)
|
||||
}
|
||||
|
||||
@@ -380,32 +318,25 @@ internal object PatchCommand: Runnable {
|
||||
* @param inputFile The APK file to sign.
|
||||
* @return The signed APK file. If [mount] is true, the input file will be returned.
|
||||
*/
|
||||
private fun sign(inputFile: File) = if (mount)
|
||||
inputFile
|
||||
private fun sign(inputFile: File) = if (mount) inputFile
|
||||
else {
|
||||
logger.info("Signing ${inputFile.name}")
|
||||
|
||||
val keyStoreFilePath = keystorePath ?: outputFilePath
|
||||
.absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath
|
||||
val keyStoreFilePath = keystorePath
|
||||
?: outputFilePath.absoluteFile.parentFile.resolve("${outputFilePath.nameWithoutExtension}.keystore").canonicalPath
|
||||
|
||||
val options = SigningOptions(
|
||||
commonName,
|
||||
password,
|
||||
keyStoreFilePath
|
||||
commonName, password, keyStoreFilePath
|
||||
)
|
||||
|
||||
ApkSigner(options)
|
||||
.signApk(
|
||||
inputFile,
|
||||
resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk")
|
||||
ApkSigner(options).signApk(
|
||||
inputFile, resourceCachePath.resolve("${outputFilePath.nameWithoutExtension}_signed.apk")
|
||||
)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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 picocli.CommandLine.*
|
||||
@@ -8,33 +8,26 @@ import java.util.logging.Logger
|
||||
|
||||
@Command(
|
||||
name = "uninstall",
|
||||
description = ["Uninstall a patched APK file from the devices with the supplied ADB device serials"]
|
||||
description = ["Uninstall a patched app from the devices with the supplied ADB device serials"]
|
||||
)
|
||||
internal object UninstallCommand : Runnable {
|
||||
private val logger = Logger.getLogger(UninstallCommand::class.java.name)
|
||||
|
||||
@Parameters(
|
||||
description = ["ADB device serials"],
|
||||
arity = "1..*"
|
||||
)
|
||||
lateinit var deviceSerials: Array<String>
|
||||
@Parameters(description = ["ADB device serials"], arity = "1..*")
|
||||
private lateinit var deviceSerials: Array<String>
|
||||
|
||||
@Option(
|
||||
names = ["-p", "--package-name"],
|
||||
description = ["Package name to uninstall"],
|
||||
required = true
|
||||
)
|
||||
lateinit var packageName: String
|
||||
@Option(names = ["-p", "--package-name"], description = ["Package name of the app to uninstall"], required = true)
|
||||
private lateinit var packageName: String
|
||||
|
||||
@Option(
|
||||
names = ["-u", "--unmount"],
|
||||
description = ["Uninstall by unmounting the patched package"],
|
||||
description = ["Uninstall by unmounting the patched APK file"],
|
||||
showDefaultValue = ALWAYS
|
||||
)
|
||||
var unmount: Boolean = false
|
||||
private var unmount: Boolean = false
|
||||
|
||||
override fun run() = try {
|
||||
deviceSerials.forEach {deviceSerial ->
|
||||
deviceSerials.forEach { deviceSerial ->
|
||||
if (unmount) {
|
||||
AdbManager.RootAdbManager(deviceSerial)
|
||||
} 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
|
||||
@@ -62,14 +62,17 @@ internal object Options {
|
||||
|
||||
val patchOptions = deserialize(json)
|
||||
|
||||
patches.forEach { patch ->
|
||||
patches.forEach patch@{ patch ->
|
||||
patchOptions.find { option -> option.patchName == patch.patchName }?.let {
|
||||
it.options.forEach { option ->
|
||||
try {
|
||||
patch.options?.set(option.key, option.value)
|
||||
?: logger.warning("${patch.patchName} has no options")
|
||||
?: run{
|
||||
logger.warning("${patch.patchName} has no options")
|
||||
return@patch
|
||||
}
|
||||
} catch (e: NoSuchOptionException) {
|
||||
logger.info(e.message ?: "Unknown error")
|
||||
logger.info(e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package app.revanced.utils.adb
|
||||
|
||||
import app.revanced.utils.adb.AdbManager.Apk
|
||||
import app.revanced.utils.adb.Constants.COMMAND_CREATE_DIR
|
||||
import app.revanced.utils.adb.Constants.COMMAND_DELETE
|
||||
import app.revanced.utils.adb.Constants.COMMAND_INSTALL_MOUNT
|
||||
import app.revanced.utils.adb.Constants.COMMAND_PREPARE_MOUNT_APK
|
||||
import app.revanced.utils.adb.Constants.COMMAND_RESTART
|
||||
import app.revanced.utils.adb.Constants.COMMAND_UMOUNT
|
||||
import app.revanced.utils.adb.Constants.CONTENT_MOUNT_SCRIPT
|
||||
import app.revanced.utils.adb.Constants.PATH_INIT_PUSH
|
||||
import app.revanced.utils.adb.Constants.PATH_INSTALLATION
|
||||
import app.revanced.utils.adb.Constants.PATH_MOUNT
|
||||
import app.revanced.utils.adb.Constants.PATH_PATCHED_APK
|
||||
import app.revanced.utils.adb.Constants.CREATE_DIR
|
||||
import app.revanced.utils.adb.Constants.DELETE
|
||||
import app.revanced.utils.adb.Constants.INSTALLATION_PATH
|
||||
import app.revanced.utils.adb.Constants.INSTALL_MOUNT
|
||||
import app.revanced.utils.adb.Constants.INSTALL_PATCHED_APK
|
||||
import app.revanced.utils.adb.Constants.MOUNT_PATH
|
||||
import app.revanced.utils.adb.Constants.MOUNT_SCRIPT
|
||||
import app.revanced.utils.adb.Constants.PATCHED_APK_PATH
|
||||
import app.revanced.utils.adb.Constants.PLACEHOLDER
|
||||
import app.revanced.utils.adb.Constants.RESTART
|
||||
import app.revanced.utils.adb.Constants.TMP_PATH
|
||||
import app.revanced.utils.adb.Constants.UMOUNT
|
||||
import se.vidstige.jadb.JadbConnection
|
||||
import se.vidstige.jadb.managers.Package
|
||||
import se.vidstige.jadb.managers.PackageManager
|
||||
@@ -72,29 +72,31 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable {
|
||||
apk.packageName ?: throw IllegalArgumentException("Package name is required")
|
||||
)
|
||||
|
||||
device.copyFile(apk.file, PATH_INIT_PUSH)
|
||||
device.push(apk.file, TMP_PATH)
|
||||
|
||||
device.run("$COMMAND_CREATE_DIR $PATH_INSTALLATION")
|
||||
device.run(COMMAND_PREPARE_MOUNT_APK.applyReplacement())
|
||||
device.run("$CREATE_DIR $INSTALLATION_PATH")
|
||||
device.run(INSTALL_PATCHED_APK.applyReplacement())
|
||||
|
||||
device.createFile(PATH_INIT_PUSH, CONTENT_MOUNT_SCRIPT.applyReplacement())
|
||||
device.createFile(TMP_PATH, MOUNT_SCRIPT.applyReplacement())
|
||||
|
||||
device.run(COMMAND_INSTALL_MOUNT.applyReplacement())
|
||||
device.run(COMMAND_UMOUNT.applyReplacement()) // Sanity check.
|
||||
device.run(PATH_MOUNT.applyReplacement())
|
||||
device.run(COMMAND_RESTART.applyReplacement())
|
||||
device.run(INSTALL_MOUNT.applyReplacement())
|
||||
device.run(UMOUNT.applyReplacement()) // Sanity check.
|
||||
device.run(MOUNT_PATH.applyReplacement())
|
||||
device.run(RESTART.applyReplacement())
|
||||
device.run(DELETE.applyReplacement(TMP_PATH).applyReplacement())
|
||||
|
||||
super.install(apk)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
device.run(COMMAND_UMOUNT.applyReplacement(packageName))
|
||||
device.run(COMMAND_DELETE.applyReplacement(PATH_PATCHED_APK).applyReplacement())
|
||||
device.run(COMMAND_DELETE.applyReplacement(PATH_MOUNT).applyReplacement())
|
||||
device.run(UMOUNT.applyReplacement(packageName))
|
||||
device.run(DELETE.applyReplacement(PATCHED_APK_PATH).applyReplacement())
|
||||
device.run(DELETE.applyReplacement(MOUNT_PATH).applyReplacement())
|
||||
device.run(DELETE.applyReplacement(TMP_PATH).applyReplacement())
|
||||
|
||||
super.uninstall(packageName)
|
||||
}
|
||||
@@ -127,6 +129,7 @@ internal sealed class AdbManager(deviceSerial: String? = null) : Closeable {
|
||||
* Apk file for [AdbManager].
|
||||
*
|
||||
* @param file The [Apk] file.
|
||||
* @param packageName The package name of the [Apk] file.
|
||||
*/
|
||||
internal class Apk(val file: File, val packageName: String? = null)
|
||||
|
||||
|
||||
@@ -2,24 +2,28 @@ package app.revanced.utils.adb
|
||||
|
||||
import se.vidstige.jadb.JadbDevice
|
||||
import se.vidstige.jadb.RemoteFile
|
||||
import se.vidstige.jadb.ShellProcessBuilder
|
||||
import java.io.File
|
||||
import java.util.concurrent.Callable
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
// return the input or output stream, depending on which first returns a value
|
||||
internal fun JadbDevice.run(command: String, su: Boolean = false) = with(this.startCommand(command, su)) {
|
||||
Executors.newFixedThreadPool(2).let { service ->
|
||||
arrayOf(inputStream, errorStream).map { stream ->
|
||||
Callable { stream.bufferedReader().use { it.readLine() } }
|
||||
}.let { tasks -> service.invokeAny(tasks).also { service.shutdown() } }
|
||||
}
|
||||
|
||||
internal fun JadbDevice.buildCommand(command: String, su: Boolean = true): ShellProcessBuilder {
|
||||
if (su) return shellProcessBuilder("su -c \'$command\'")
|
||||
|
||||
val args = command.split(" ") as ArrayList<String>
|
||||
val cmd = args.removeFirst()
|
||||
|
||||
return shellProcessBuilder(cmd, *args.toTypedArray())
|
||||
}
|
||||
|
||||
internal fun JadbDevice.run(command: String, su: Boolean = true): Int {
|
||||
return this.buildCommand(command, su).start().waitFor()
|
||||
}
|
||||
|
||||
internal fun JadbDevice.hasSu() =
|
||||
this.startCommand("su -h", false).waitFor() == 0
|
||||
|
||||
internal fun JadbDevice.copyFile(file: File, targetFile: String) =
|
||||
push(file, RemoteFile(targetFile))
|
||||
internal fun JadbDevice.push(file: File, targetFilePath: String) =
|
||||
push(file, RemoteFile(targetFilePath))
|
||||
|
||||
internal fun JadbDevice.createFile(targetFile: String, content: String) =
|
||||
push(content.byteInputStream(), System.currentTimeMillis(), 644, RemoteFile(targetFile))
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
package app.revanced.utils.adb
|
||||
|
||||
internal object Constants {
|
||||
internal const val PLACEHOLDER = "TEMPLATE_PACKAGE_NAME"
|
||||
internal const val PLACEHOLDER = "PLACEHOLDER"
|
||||
|
||||
internal const val PATH_INIT_PUSH = "/data/local/tmp/revanced.delete"
|
||||
internal const val PATH_INSTALLATION = "/data/adb/revanced/"
|
||||
internal const val PATH_PATCHED_APK = "$PATH_INSTALLATION$PLACEHOLDER.apk"
|
||||
internal const val PATH_MOUNT = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh"
|
||||
internal const val TMP_PATH = "/data/local/tmp/revanced.tmp"
|
||||
internal const val INSTALLATION_PATH = "/data/adb/revanced/"
|
||||
internal const val PATCHED_APK_PATH = "$INSTALLATION_PATH$PLACEHOLDER.apk"
|
||||
internal const val MOUNT_PATH = "/data/adb/service.d/mount_revanced_$PLACEHOLDER.sh"
|
||||
|
||||
internal const val COMMAND_DELETE = "rm -rf $PLACEHOLDER"
|
||||
internal const val COMMAND_CREATE_DIR = "mkdir -p"
|
||||
internal const val COMMAND_RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | " +
|
||||
internal const val DELETE = "rm -rf $PLACEHOLDER"
|
||||
internal const val CREATE_DIR = "mkdir -p"
|
||||
internal const val RESTART = "pm resolve-activity --brief $PLACEHOLDER | tail -n 1 | " +
|
||||
"xargs am start -n && kill ${'$'}(pidof -s $PLACEHOLDER)"
|
||||
|
||||
internal const val COMMAND_PREPARE_MOUNT_APK = "base_path=\"$PATH_PATCHED_APK\" && " +
|
||||
"mv $PATH_INIT_PUSH ${'$'}base_path && " +
|
||||
internal const val INSTALL_PATCHED_APK = "base_path=\"$PATCHED_APK_PATH\" && " +
|
||||
"mv $TMP_PATH ${'$'}base_path && " +
|
||||
"chmod 644 ${'$'}base_path && " +
|
||||
"chown system:system ${'$'}base_path && " +
|
||||
"chcon u:object_r:apk_data_file:s0 ${'$'}base_path"
|
||||
|
||||
internal const val COMMAND_UMOUNT =
|
||||
internal const val UMOUNT =
|
||||
"grep $PLACEHOLDER /proc/mounts | while read -r line; do echo ${'$'}line | cut -d \" \" -f 2 | sed 's/apk.*/apk/' | xargs -r umount -l; done"
|
||||
|
||||
internal const val COMMAND_INSTALL_MOUNT = "mv $PATH_INIT_PUSH $PATH_MOUNT && chmod +x $PATH_MOUNT"
|
||||
internal const val INSTALL_MOUNT = "mv $TMP_PATH $MOUNT_PATH && chmod +x $MOUNT_PATH"
|
||||
|
||||
internal const val CONTENT_MOUNT_SCRIPT =
|
||||
internal val MOUNT_SCRIPT =
|
||||
"""
|
||||
#!/system/bin/sh
|
||||
MAGISKTMP="${'$'}(magisk --path)" || MAGISKTMP=/sbin
|
||||
MIRROR="${'$'}MAGISKTMP/.magisk/mirror"
|
||||
while [ "${'$'}(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done
|
||||
|
||||
base_path="$PATH_PATCHED_APK"
|
||||
base_path="$PATCHED_APK_PATH"
|
||||
stock_path=${'$'}( pm path $PLACEHOLDER | grep base | sed 's/package://g' )
|
||||
|
||||
chcon u:object_r:apk_data_file:s0 ${'$'}base_path
|
||||
mount -o bind ${'$'}MIRROR${'$'}base_path ${'$'}stock_path
|
||||
"""
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user