You've already forked revanced-patcher
mirror of
https://github.com/revanced/revanced-patcher
synced 2025-09-06 16:38:50 +02:00
Compare commits
135 Commits
v11.0.2-de
...
v15.0.1-de
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3f410bd39f | ||
![]() |
d51bc32e37 | ||
![]() |
b7f6aa94cc | ||
![]() |
ff965e6953 | ||
![]() |
468d5d7421 | ||
![]() |
fc95b28c49 | ||
![]() |
69184187d9 | ||
![]() |
a802d0df46 | ||
![]() |
8de30633ae | ||
![]() |
a1fbb7990f | ||
![]() |
aa71146b1b | ||
![]() |
9fdb8f087f | ||
![]() |
670f0153de | ||
![]() |
1d7aeca696 | ||
![]() |
4e7811ea07 | ||
![]() |
e11283744a | ||
![]() |
91cdfd53ef | ||
![]() |
bc7d6b9941 | ||
![]() |
6b1e0a1656 | ||
![]() |
72c9eb2129 | ||
![]() |
4bc4b0dc01 | ||
![]() |
637d48746f | ||
![]() |
9a109c129b | ||
![]() |
d49e4ee5ea | ||
![]() |
30f0ea29a3 | ||
![]() |
49930f6565 | ||
![]() |
909d89fa8d | ||
![]() |
81d1d7f544 | ||
![]() |
67b7dff67a | ||
![]() |
4b76d19596 | ||
![]() |
080fbe9feb | ||
![]() |
d3721229bf | ||
![]() |
86c1c9c772 | ||
![]() |
c299817193 | ||
![]() |
fcc1de45ed | ||
![]() |
a29931f2ec | ||
![]() |
3fc6a139ee | ||
![]() |
4dd04975d9 | ||
![]() |
3b4db3ddb7 | ||
![]() |
c4a7117ee8 | ||
![]() |
b4e900fde8 | ||
![]() |
9818d730e4 | ||
![]() |
11a3378659 | ||
![]() |
1bb05f22d3 | ||
![]() |
26b70554c4 | ||
![]() |
93b29d2e83 | ||
![]() |
072986374a | ||
![]() |
2c590d212a | ||
![]() |
6cc863efb3 | ||
![]() |
b832812767 | ||
![]() |
c44558cacd | ||
![]() |
6d83a720cd | ||
![]() |
8d0dd9c448 | ||
![]() |
64020eec49 | ||
![]() |
4dedfb85cb | ||
![]() |
55d694579a | ||
![]() |
86db64edff | ||
![]() |
983563efb6 | ||
![]() |
37abb2db99 | ||
![]() |
5ba0b47e60 | ||
![]() |
e8f2087a6f | ||
![]() |
6ce99f5cdf | ||
![]() |
13c0c9cdd3 | ||
![]() |
58ffdb60d7 | ||
![]() |
ba56a6a2ee | ||
![]() |
ccccf5b1d2 | ||
![]() |
b507ac0a54 | ||
![]() |
e985676c2d | ||
![]() |
f7f4ba6c55 | ||
![]() |
4292f43814 | ||
![]() |
30bd4fd9fe | ||
![]() |
76de39369d | ||
![]() |
88a703ce36 | ||
![]() |
5938f6b7ea | ||
![]() |
5c0c0d6c37 | ||
![]() |
0f15077225 | ||
![]() |
273dd8d388 | ||
![]() |
1795f376ef | ||
![]() |
e7360a7692 | ||
![]() |
e1fc86934f | ||
![]() |
6b8977f178 | ||
![]() |
12c6c73de0 | ||
![]() |
db62a1607b | ||
![]() |
58bb879ef5 | ||
![]() |
254912438a | ||
![]() |
0e48918bcc | ||
![]() |
783ccf8529 | ||
![]() |
8fb2f2dc1d | ||
![]() |
2a8cc283c7 | ||
![]() |
433fe3af9f | ||
![]() |
c2d89c622e | ||
![]() |
02d6ff15fe | ||
![]() |
f2cb7ee7df | ||
![]() |
a2ac44dcc1 | ||
![]() |
3cf9d74efa | ||
![]() |
d5f89a903f | ||
![]() |
496c2242bc | ||
![]() |
98fbff87df | ||
![]() |
ddb51a1c45 | ||
![]() |
8df1155215 | ||
![]() |
53f2a61409 | ||
![]() |
746544f9d5 | ||
![]() |
c65c3df11c | ||
![]() |
b29b8f12b3 | ||
![]() |
d6945677c4 | ||
![]() |
aedf4aea08 | ||
![]() |
dc28d414dc | ||
![]() |
9755bab298 | ||
![]() |
fae4029cfc | ||
![]() |
1790f0d706 | ||
![]() |
0ba2c51676 | ||
![]() |
03cd97b49c | ||
![]() |
16a162c1dd | ||
![]() |
c9bbcf2bf2 | ||
![]() |
86e1bf6078 | ||
![]() |
1bca84ef0b | ||
![]() |
e0f8e1b71a | ||
![]() |
416d69142f | ||
![]() |
426807aeaa | ||
![]() |
90cb075a97 | ||
![]() |
ac2ca8fbd3 | ||
![]() |
69e4a49065 | ||
![]() |
a4a030f2b2 | ||
![]() |
dcc4ecd237 | ||
![]() |
57f3036a96 | ||
![]() |
753e55dfc3 | ||
![]() |
9d81baf4b4 | ||
![]() |
7cb4d4c596 | ||
![]() |
2c8565508e | ||
![]() |
c7f156e4c9 | ||
![]() |
fcef4342e8 | ||
![]() |
72783a5e74 | ||
![]() |
a379b69eeb | ||
![]() |
0a8ccba33e | ||
![]() |
519359a9eb |
9
.gitattributes
vendored
Normal file
9
.gitattributes
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# https://help.github.com/articles/dealing-with-line-endings/
|
||||||
|
#
|
||||||
|
# Linux start script should use lf
|
||||||
|
/gradlew text eol=lf
|
||||||
|
|
||||||
|
# These are Windows script files and should use crlf
|
||||||
|
*.bat text eol=crlf
|
||||||
|
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: ./gradlew clean --no-daemon
|
run: ./gradlew build clean --no-daemon
|
||||||
- name: Setup semantic-release
|
- name: Setup semantic-release
|
||||||
run: npm install
|
run: npm install
|
||||||
- name: Release
|
- name: Release
|
||||||
|
@@ -7,7 +7,13 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@semantic-release/commit-analyzer",
|
[
|
||||||
|
"@semantic-release/commit-analyzer", {
|
||||||
|
"releaseRules": [
|
||||||
|
{ "type": "build", "scope": "Needs bump", "release": "patch" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"@semantic-release/release-notes-generator",
|
"@semantic-release/release-notes-generator",
|
||||||
"@semantic-release/changelog",
|
"@semantic-release/changelog",
|
||||||
"gradle-semantic-release-plugin",
|
"gradle-semantic-release-plugin",
|
||||||
|
439
CHANGELOG.md
439
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,71 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.8.10"
|
kotlin("jvm") version "1.9.0" apply false
|
||||||
`maven-publish`
|
alias(libs.plugins.binary.compatibility.validator)
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "app.revanced"
|
allprojects {
|
||||||
|
apply(plugin = "maven-publish")
|
||||||
|
|
||||||
val githubUsername: String = project.findProperty("gpr.user") as? String ?: System.getenv("GITHUB_ACTOR")
|
group = "app.revanced"
|
||||||
val githubPassword: String = project.findProperty("gpr.key") as? String ?: System.getenv("GITHUB_TOKEN")
|
}
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven {
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/multidexlib2")
|
|
||||||
credentials {
|
|
||||||
username = githubUsername
|
|
||||||
password = githubPassword
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("xpp3:xpp3:1.1.4c")
|
|
||||||
implementation("app.revanced:smali:2.5.3-a3836654")
|
|
||||||
implementation("app.revanced:multidexlib2:2.5.3-a3836654")
|
|
||||||
implementation("app.revanced:apktool-lib:2.7.0")
|
|
||||||
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-reflect:1.8.20-RC")
|
|
||||||
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.20-RC")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
testLogging {
|
|
||||||
events("PASSED", "SKIPPED", "FAILED")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processResources {
|
|
||||||
expand("projectVersion" to project.version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(11)
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
repositories {
|
|
||||||
if (System.getenv("GITHUB_ACTOR") != null)
|
|
||||||
maven {
|
|
||||||
name = "GitHubPackages"
|
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
|
||||||
credentials {
|
|
||||||
username = System.getenv("GITHUB_ACTOR")
|
|
||||||
password = System.getenv("GITHUB_TOKEN")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mavenLocal()
|
|
||||||
}
|
|
||||||
publications {
|
|
||||||
register<MavenPublication>("gpr") {
|
|
||||||
from(components["java"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,2 +1,4 @@
|
|||||||
|
org.gradle.parallel = true
|
||||||
|
org.gradle.caching = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 11.0.2-dev.3
|
version = 15.0.1-dev.1
|
||||||
|
31
gradle/libs.versions.toml
Normal file
31
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
[versions]
|
||||||
|
android = "4.1.1.4"
|
||||||
|
kotlin-reflect = "1.9.0"
|
||||||
|
apktool-lib = "2.8.2-6"
|
||||||
|
kotlin-test = "1.8.20-RC"
|
||||||
|
kotlinx-coroutines-core = "1.7.1"
|
||||||
|
multidexlib2 = "3.0.3.r2"
|
||||||
|
smali = "3.0.3"
|
||||||
|
symbol-processing-api = "1.9.0-1.0.11"
|
||||||
|
xpp3 = "1.1.4c"
|
||||||
|
binary-compatibility-validator = "0.13.2"
|
||||||
|
kotlin-compile-testing-ksp = "1.5.0"
|
||||||
|
kotlinpoet-ksp = "1.14.2"
|
||||||
|
ksp = "1.9.0-1.0.11"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
android = { module = "com.google.android:android", version.ref = "android" }
|
||||||
|
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin-reflect" }
|
||||||
|
apktool-lib = { module = "app.revanced:apktool-lib", version.ref = "apktool-lib" }
|
||||||
|
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin-test" }
|
||||||
|
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-core" }
|
||||||
|
multidexlib2 = { module = "app.revanced:multidexlib2", version.ref = "multidexlib2" }
|
||||||
|
smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
|
||||||
|
symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "symbol-processing-api" }
|
||||||
|
xpp3 = { module = "xpp3:xpp3", version.ref = "xpp3" }
|
||||||
|
kotlin-compile-testing = { module = "com.github.tschuchortdev:kotlin-compile-testing-ksp", version.ref = "kotlin-compile-testing-ksp" }
|
||||||
|
kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoet-ksp" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
|
||||||
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
40
gradlew
vendored
40
gradlew
vendored
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright © 2015-2021 the original authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -32,10 +32,10 @@
|
|||||||
# Busybox and similar reduced shells will NOT work, because this script
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
# requires all of these POSIX shell features:
|
# requires all of these POSIX shell features:
|
||||||
# * functions;
|
# * functions;
|
||||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
# * compound commands having a testable exit status, especially «case»;
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
# * various built-in commands including «command», «set», and «ulimit».
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
#
|
#
|
||||||
# Important for patching:
|
# Important for patching:
|
||||||
#
|
#
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -80,13 +80,10 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -133,22 +130,29 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -193,6 +197,10 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command;
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
@@ -205,6 +213,12 @@ set -- \
|
|||||||
org.gradle.wrapper.GradleWrapperMain \
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
# Use "xargs" to parse quoted args.
|
# Use "xargs" to parse quoted args.
|
||||||
#
|
#
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
|||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -25,7 +25,8 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
public abstract interface annotation class app/revanced/patcher/patch/annotation/CompatiblePackage : java/lang/annotation/Annotation {
|
||||||
|
public abstract fun name ()Ljava/lang/String;
|
||||||
|
public abstract fun versions ()[Ljava/lang/String;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract interface annotation class app/revanced/patcher/patch/annotation/Patch : java/lang/annotation/Annotation {
|
||||||
|
public abstract fun compatiblePackages ()[Lapp/revanced/patcher/patch/annotation/CompatiblePackage;
|
||||||
|
public abstract fun dependencies ()[Ljava/lang/Class;
|
||||||
|
public abstract fun description ()Ljava/lang/String;
|
||||||
|
public abstract fun name ()Ljava/lang/String;
|
||||||
|
public abstract fun requiresIntegrations ()Z
|
||||||
|
public abstract fun use ()Z
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/annotation/processor/PatchProcessor : com/google/devtools/ksp/processing/SymbolProcessor {
|
||||||
|
public fun process (Lcom/google/devtools/ksp/processing/Resolver;)Ljava/util/List;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patcher/patch/annotation/processor/PatchProcessorProvider : com/google/devtools/ksp/processing/SymbolProcessorProvider {
|
||||||
|
public fun <init> ()V
|
||||||
|
public fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lapp/revanced/patcher/patch/annotation/processor/PatchProcessor;
|
||||||
|
public synthetic fun create (Lcom/google/devtools/ksp/processing/SymbolProcessorEnvironment;)Lcom/google/devtools/ksp/processing/SymbolProcessor;
|
||||||
|
}
|
||||||
|
|
74
revanced-patch-annotation-processor/build.gradle.kts
Normal file
74
revanced-patch-annotation-processor/build.gradle.kts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.9.0"
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.symbol.processing.api)
|
||||||
|
implementation(libs.kotlinpoet.ksp)
|
||||||
|
implementation(project(":revanced-patcher"))
|
||||||
|
|
||||||
|
testImplementation(libs.kotlin.test)
|
||||||
|
testImplementation(libs.kotlin.compile.testing)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events("PASSED", "SKIPPED", "FAILED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin { jvmToolchain(11) }
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
maven {
|
||||||
|
name = "GitHubPackages"
|
||||||
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
||||||
|
credentials {
|
||||||
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("gpr") {
|
||||||
|
from(components["java"])
|
||||||
|
|
||||||
|
version = project.version.toString()
|
||||||
|
|
||||||
|
pom {
|
||||||
|
name = "ReVanced patch annotation processor"
|
||||||
|
description = "Annotation processor for patches."
|
||||||
|
url = "https://revanced.app"
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = "GNU General Public License v3.0"
|
||||||
|
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = "ReVanced"
|
||||||
|
name = "ReVanced"
|
||||||
|
email = "contact@revanced.app"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection = "scm:git:git://github.com/revanced/revanced-patcher.git"
|
||||||
|
developerConnection = "scm:git:git@github.com:revanced/revanced-patcher.git"
|
||||||
|
url = "https://github.com/revanced/revanced-patcher"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
revanced-patch-annotation-processor/settings.gradle.kts
Normal file
2
revanced-patch-annotation-processor/settings.gradle.kts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
rootProject.name = "revanced-patch-annotation-processor"
|
||||||
|
|
@@ -0,0 +1,38 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation
|
||||||
|
|
||||||
|
import java.lang.annotation.Inherited
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation for [app.revanced.patcher.patch.Patch] classes.
|
||||||
|
*
|
||||||
|
* @param name The name of the patch. If empty, the patch will be unnamed.
|
||||||
|
* @param description The description of the patch. If empty, no description will be used.
|
||||||
|
* @param dependencies The patches this patch depends on.
|
||||||
|
* @param compatiblePackages The packages this patch is compatible with.
|
||||||
|
* @param use Whether this patch should be used.
|
||||||
|
* @param requiresIntegrations Whether this patch requires integrations.
|
||||||
|
*/
|
||||||
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
@Inherited
|
||||||
|
annotation class Patch(
|
||||||
|
val name: String = "",
|
||||||
|
val description: String = "",
|
||||||
|
val dependencies: Array<KClass<out app.revanced.patcher.patch.Patch<*>>> = [],
|
||||||
|
val compatiblePackages: Array<CompatiblePackage> = [],
|
||||||
|
val use: Boolean = true,
|
||||||
|
// TODO: Remove this property, once integrations are coupled with patches.
|
||||||
|
val requiresIntegrations: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A package that a [app.revanced.patcher.patch.Patch] is compatible with.
|
||||||
|
*
|
||||||
|
* @param name The name of the package.
|
||||||
|
* @param versions The versions of the package.
|
||||||
|
*/
|
||||||
|
annotation class CompatiblePackage(
|
||||||
|
val name: String,
|
||||||
|
val versions: Array<String> = [],
|
||||||
|
)
|
@@ -0,0 +1,207 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import com.google.devtools.ksp.processing.CodeGenerator
|
||||||
|
import com.google.devtools.ksp.processing.Dependencies
|
||||||
|
import com.google.devtools.ksp.processing.Resolver
|
||||||
|
import com.google.devtools.ksp.processing.SymbolProcessor
|
||||||
|
import com.google.devtools.ksp.symbol.KSAnnotated
|
||||||
|
import com.google.devtools.ksp.symbol.KSAnnotation
|
||||||
|
import com.google.devtools.ksp.symbol.KSClassDeclaration
|
||||||
|
import com.google.devtools.ksp.symbol.KSType
|
||||||
|
import com.google.devtools.ksp.validate
|
||||||
|
import com.squareup.kotlinpoet.*
|
||||||
|
import com.squareup.kotlinpoet.ksp.toClassName
|
||||||
|
import com.squareup.kotlinpoet.ksp.writeTo
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
class PatchProcessor internal constructor(
|
||||||
|
private val codeGenerator: CodeGenerator,
|
||||||
|
) : SymbolProcessor {
|
||||||
|
|
||||||
|
private fun KSAnnotated.isSubclassOf(cls: KClass<*>): Boolean {
|
||||||
|
if (this !is KSClassDeclaration) return false
|
||||||
|
|
||||||
|
if (qualifiedName?.asString() == cls.qualifiedName) return true
|
||||||
|
|
||||||
|
return superTypes.any { it.resolve().declaration.isSubclassOf(cls) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun process(resolver: Resolver): List<KSAnnotated> {
|
||||||
|
val patches = buildMap {
|
||||||
|
resolver.getSymbolsWithAnnotation(Patch::class.qualifiedName!!).filter {
|
||||||
|
// Do not check here if Patch is super of the class, because it is expensive.
|
||||||
|
// Check it later when processing.
|
||||||
|
it.validate() && it.isSubclassOf(app.revanced.patcher.patch.Patch::class)
|
||||||
|
}.map {
|
||||||
|
it as KSClassDeclaration
|
||||||
|
}.forEach { patchDeclaration ->
|
||||||
|
patchDeclaration.annotations.find {
|
||||||
|
it.annotationType.resolve().declaration.qualifiedName!!.asString() == Patch::class.qualifiedName!!
|
||||||
|
}?.let { annotation ->
|
||||||
|
fun KSAnnotation.property(name: String) =
|
||||||
|
arguments.find { it.name!!.asString() == name }?.value!!
|
||||||
|
|
||||||
|
val name =
|
||||||
|
annotation.property("name").toString().ifEmpty { null }
|
||||||
|
|
||||||
|
val description =
|
||||||
|
annotation.property("description").toString().ifEmpty { null }
|
||||||
|
|
||||||
|
val dependencies =
|
||||||
|
(annotation.property("dependencies") as List<KSType>).ifEmpty { null }
|
||||||
|
|
||||||
|
val compatiblePackages =
|
||||||
|
(annotation.property("compatiblePackages") as List<KSAnnotation>).ifEmpty { null }
|
||||||
|
|
||||||
|
val use =
|
||||||
|
annotation.property("use") as Boolean
|
||||||
|
|
||||||
|
val requiresIntegrations =
|
||||||
|
annotation.property("requiresIntegrations") as Boolean
|
||||||
|
|
||||||
|
// Data class for KotlinPoet
|
||||||
|
data class PatchData(
|
||||||
|
val name: String?,
|
||||||
|
val description: String?,
|
||||||
|
val dependencies: List<ClassName>?,
|
||||||
|
val compatiblePackages: List<CodeBlock>?,
|
||||||
|
val use: Boolean,
|
||||||
|
val requiresIntegrations: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
this[patchDeclaration] = PatchData(
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
dependencies?.map { dependency -> dependency.toClassName() },
|
||||||
|
compatiblePackages?.map {
|
||||||
|
val packageName = it.property("name")
|
||||||
|
val packageVersions = (it.property("versions") as List<String>).ifEmpty { null }
|
||||||
|
?.joinToString(", ") { version -> "\"$version\"" }
|
||||||
|
|
||||||
|
CodeBlock.of(
|
||||||
|
"%T(%S, %L)",
|
||||||
|
app.revanced.patcher.patch.Patch.CompatiblePackage::class,
|
||||||
|
packageName,
|
||||||
|
packageVersions?.let { "setOf($packageVersions)" },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
use,
|
||||||
|
requiresIntegrations
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a patch depends on another, that is annotated, the dependency should be replaced with the generated patch,
|
||||||
|
// because the generated patch has all the necessary properties to invoke the super constructor with,
|
||||||
|
// unlike the annotated patch.
|
||||||
|
val dependencyResolutionMap = buildMap {
|
||||||
|
patches.values.filter { it.dependencies != null }.flatMap {
|
||||||
|
it.dependencies!!
|
||||||
|
}.distinct().forEach { dependency ->
|
||||||
|
patches.keys.find { it.qualifiedName?.asString() == dependency.toString() }
|
||||||
|
?.let { patch ->
|
||||||
|
this[dependency] = ClassName(
|
||||||
|
patch.packageName.asString(),
|
||||||
|
patch.simpleName.asString() + "Generated"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patches.forEach { (patchDeclaration, patchAnnotation) ->
|
||||||
|
val isBytecodePatch = patchDeclaration.isSubclassOf(BytecodePatch::class)
|
||||||
|
|
||||||
|
val superClass = if (isBytecodePatch) {
|
||||||
|
BytecodePatch::class
|
||||||
|
} else {
|
||||||
|
ResourcePatch::class
|
||||||
|
}
|
||||||
|
|
||||||
|
val contextClass = if (isBytecodePatch) {
|
||||||
|
BytecodeContext::class
|
||||||
|
} else {
|
||||||
|
ResourceContext::class
|
||||||
|
}
|
||||||
|
|
||||||
|
val generatedPatchClassName = ClassName(
|
||||||
|
patchDeclaration.packageName.asString(),
|
||||||
|
patchDeclaration.simpleName.asString() + "Generated"
|
||||||
|
)
|
||||||
|
|
||||||
|
FileSpec.builder(generatedPatchClassName)
|
||||||
|
.addType(
|
||||||
|
TypeSpec.objectBuilder(generatedPatchClassName)
|
||||||
|
.superclass(superClass).apply {
|
||||||
|
patchAnnotation.name?.let { name ->
|
||||||
|
addSuperclassConstructorParameter("name = %S", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
patchAnnotation.description?.let { description ->
|
||||||
|
addSuperclassConstructorParameter("description = %S", description)
|
||||||
|
}
|
||||||
|
|
||||||
|
patchAnnotation.compatiblePackages?.let { compatiblePackages ->
|
||||||
|
addSuperclassConstructorParameter(
|
||||||
|
"compatiblePackages = setOf(%L)",
|
||||||
|
compatiblePackages.joinToString(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The generated patch always depends on the source patch.
|
||||||
|
addSuperclassConstructorParameter(
|
||||||
|
"dependencies = setOf(%L)",
|
||||||
|
buildList {
|
||||||
|
patchAnnotation.dependencies?.forEach { dependency ->
|
||||||
|
add("${(dependencyResolutionMap[dependency] ?: dependency)}::class")
|
||||||
|
}
|
||||||
|
|
||||||
|
add("${patchDeclaration.toClassName()}::class")
|
||||||
|
}.joinToString(", "),
|
||||||
|
)
|
||||||
|
|
||||||
|
addSuperclassConstructorParameter(
|
||||||
|
"use = %L", patchAnnotation.use
|
||||||
|
)
|
||||||
|
|
||||||
|
addSuperclassConstructorParameter(
|
||||||
|
"requiresIntegrations = %L",
|
||||||
|
patchAnnotation.requiresIntegrations
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.addFunction(
|
||||||
|
FunSpec.builder("execute")
|
||||||
|
.addModifiers(KModifier.OVERRIDE)
|
||||||
|
.addParameter("context", contextClass)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.addInitializerBlock(
|
||||||
|
CodeBlock.builder()
|
||||||
|
.add(
|
||||||
|
"%T.options.forEach { (_, option) ->",
|
||||||
|
patchDeclaration.toClassName()
|
||||||
|
)
|
||||||
|
.addStatement(
|
||||||
|
"options.register(option)"
|
||||||
|
)
|
||||||
|
.add(
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
).build().writeTo(
|
||||||
|
codeGenerator,
|
||||||
|
Dependencies(false, patchDeclaration.containingFile!!)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor
|
||||||
|
|
||||||
|
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
|
||||||
|
import com.google.devtools.ksp.processing.SymbolProcessorProvider
|
||||||
|
|
||||||
|
class PatchProcessorProvider : SymbolProcessorProvider {
|
||||||
|
override fun create(environment: SymbolProcessorEnvironment) =
|
||||||
|
PatchProcessor(environment.codeGenerator)
|
||||||
|
}
|
@@ -0,0 +1 @@
|
|||||||
|
app.revanced.patcher.patch.annotation.processor.PatchProcessorProvider
|
@@ -0,0 +1,147 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import com.tschuchort.compiletesting.KotlinCompilation
|
||||||
|
import com.tschuchort.compiletesting.SourceFile
|
||||||
|
import com.tschuchort.compiletesting.kspWithCompilation
|
||||||
|
import com.tschuchort.compiletesting.symbolProcessorProviders
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
|
class TestPatchAnnotationProcessor {
|
||||||
|
// region Processing
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testProcessing() = assertEquals(
|
||||||
|
"Processable patch", compile(
|
||||||
|
getSourceFile(
|
||||||
|
"processing", "ProcessablePatch"
|
||||||
|
)
|
||||||
|
).loadPatch("$SAMPLE_PACKAGE.processing.ProcessablePatchGenerated").name
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generateNullProperties() = compile(
|
||||||
|
getSourceFile(
|
||||||
|
"null", "NullPropertiesPatch"
|
||||||
|
)
|
||||||
|
).loadPatch("$SAMPLE_PACKAGE.null.NullPropertiesPatchGenerated").let {
|
||||||
|
assertNull(it.description) // Because no description was provided.
|
||||||
|
assertNull(it.compatiblePackages!!.first().versions) // Because no versions were provided.
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Dependencies
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDependencies() {
|
||||||
|
compile(
|
||||||
|
getSourceFile(
|
||||||
|
"dependencies", "DependentPatch"
|
||||||
|
), getSourceFile(
|
||||||
|
"dependencies", "DependencyPatch"
|
||||||
|
)
|
||||||
|
).let { result ->
|
||||||
|
result.loadPatch("$SAMPLE_PACKAGE.dependencies.DependentPatchGenerated").let {
|
||||||
|
// Dependency as well as the source class of the generated class.
|
||||||
|
assertEquals(
|
||||||
|
2,
|
||||||
|
it.dependencies!!.size
|
||||||
|
)
|
||||||
|
|
||||||
|
// The last dependency is always the source class of the generated class to respect
|
||||||
|
// order of dependencies.
|
||||||
|
assertEquals(
|
||||||
|
result.loadPatch("$SAMPLE_PACKAGE.dependencies.DependentPatch")::class,
|
||||||
|
it.dependencies!!.last()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Options
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOptions() {
|
||||||
|
val patch = compile(
|
||||||
|
getSourceFile(
|
||||||
|
"options", "OptionsPatch"
|
||||||
|
)
|
||||||
|
).loadPatch("$SAMPLE_PACKAGE.options.OptionsPatchGenerated")
|
||||||
|
|
||||||
|
assert(patch.options.isNotEmpty())
|
||||||
|
assertEquals(patch.options["print"].title, "Print message")
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Limitations
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun failingManualDependency() = assertEquals(
|
||||||
|
1, // Generated patch is always dependent on source class.
|
||||||
|
compile(
|
||||||
|
getSourceFile(
|
||||||
|
"limitations/manualdependency", "DependentPatch"
|
||||||
|
), getSourceFile(
|
||||||
|
"limitations/manualdependency", "DependencyPatch"
|
||||||
|
)
|
||||||
|
).loadPatch("$SAMPLE_PACKAGE.limitations.manualdependency.DependentPatchGenerated").dependencies!!.size
|
||||||
|
)
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
private companion object Utils {
|
||||||
|
const val SAMPLE_PACKAGE = "app.revanced.patcher.patch.annotation.processor.samples"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a source file from the given sample and class name.
|
||||||
|
*
|
||||||
|
* @param sample The sample to get the source file from.
|
||||||
|
* @param className The name of the class to get the source file from.
|
||||||
|
* @return The source file.
|
||||||
|
*/
|
||||||
|
fun getSourceFile(sample: String, className: String): SourceFile {
|
||||||
|
val resourceName = "app/revanced/patcher/patch/annotation/processor/samples/$sample/$className.kt"
|
||||||
|
return SourceFile.kotlin(
|
||||||
|
"$className.kt",
|
||||||
|
TestPatchAnnotationProcessor::class.java.classLoader.getResourceAsStream(resourceName)
|
||||||
|
?.readAllBytes()
|
||||||
|
?.toString(Charsets.UTF_8)
|
||||||
|
?: error("Could not find resource $resourceName")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile the given source files and return the result.
|
||||||
|
*
|
||||||
|
* @param sourceFiles The source files to compile.
|
||||||
|
* @return The result of the compilation.
|
||||||
|
*/
|
||||||
|
fun compile(vararg sourceFiles: SourceFile) = KotlinCompilation().apply {
|
||||||
|
sources = sourceFiles.asList()
|
||||||
|
|
||||||
|
symbolProcessorProviders = listOf(PatchProcessorProvider())
|
||||||
|
|
||||||
|
// Required until https://github.com/tschuchortdev/kotlin-compile-testing/issues/312 closed.
|
||||||
|
kspWithCompilation = true
|
||||||
|
|
||||||
|
inheritClassPath = true
|
||||||
|
messageOutputStream = System.out
|
||||||
|
}.compile().also { result ->
|
||||||
|
assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Class loading
|
||||||
|
|
||||||
|
fun KotlinCompilation.Result.loadPatch(name: String) = classLoader.loadClass(name).loadPatch()
|
||||||
|
|
||||||
|
fun Class<*>.loadPatch() = this.getField("INSTANCE").get(null) as Patch<*>
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.dependencies
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
|
@Patch(name = "Dependency patch")
|
||||||
|
object DependencyPatch : ResourcePatch() {
|
||||||
|
override fun execute(context: ResourceContext) {}
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.dependencies
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
name = "Dependent patch",
|
||||||
|
dependencies = [DependencyPatch::class],
|
||||||
|
)
|
||||||
|
object DependentPatch : BytecodePatch() {
|
||||||
|
override fun execute(context: BytecodeContext) {}
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.limitations.manualdependency
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
|
@Patch(name = "Dependency patch")
|
||||||
|
object DependencyPatch : ResourcePatch() {
|
||||||
|
override fun execute(context: ResourceContext) { }
|
||||||
|
}
|
@@ -0,0 +1,17 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.limitations.manualdependency
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
|
@Patch(name = "Dependent patch")
|
||||||
|
object DependentPatch : BytecodePatch(
|
||||||
|
// Dependency will not be executed correctly if it is manually specified.
|
||||||
|
// The reason for this is that the dependency patch is annotated too,
|
||||||
|
// so the processor will generate a new patch class for it embedding the annotated information.
|
||||||
|
// Because the dependency is manually specified,
|
||||||
|
// the processor will not be able to change this dependency to the generated class,
|
||||||
|
// which means that the dependency will lose the annotated information.
|
||||||
|
dependencies = setOf(DependencyPatch::class)
|
||||||
|
) {
|
||||||
|
override fun execute(context: BytecodeContext) {}
|
||||||
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.`null`
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
"Patch with null properties",
|
||||||
|
compatiblePackages = [CompatiblePackage("com.google.android.youtube")],
|
||||||
|
)
|
||||||
|
object NullPropertiesPatch : BytecodePatch() {
|
||||||
|
override fun execute(context: BytecodeContext) {}
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.options
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patcher.patch.options.types.StringPatchOption.Companion.stringPatchOption
|
||||||
|
|
||||||
|
@Patch(name = "Options patch")
|
||||||
|
object OptionsPatch : ResourcePatch() {
|
||||||
|
override fun execute(context: ResourceContext) {}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
private val printOption by stringPatchOption(
|
||||||
|
"print",
|
||||||
|
null,
|
||||||
|
"Print message",
|
||||||
|
"The message to print."
|
||||||
|
)
|
||||||
|
}
|
@@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.patcher.patch.annotation.processor.samples.processing
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
|
||||||
|
@Patch("Processable patch")
|
||||||
|
object ProcessablePatch : BytecodePatch() {
|
||||||
|
override fun execute(context: BytecodeContext) {}
|
||||||
|
}
|
904
revanced-patcher/api/revanced-patcher.api
Normal file
904
revanced-patcher/api/revanced-patcher.api
Normal file
File diff suppressed because it is too large
Load Diff
82
revanced-patcher/build.gradle.kts
Normal file
82
revanced-patcher/build.gradle.kts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm") version "1.9.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
implementation(libs.xpp3)
|
||||||
|
implementation(libs.smali)
|
||||||
|
implementation(libs.multidexlib2)
|
||||||
|
implementation(libs.apktool.lib)
|
||||||
|
implementation(libs.kotlin.reflect)
|
||||||
|
|
||||||
|
compileOnly(libs.android)
|
||||||
|
|
||||||
|
testImplementation(project(":revanced-patch-annotation-processor"))
|
||||||
|
testImplementation(libs.kotlin.test)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events("PASSED", "SKIPPED", "FAILED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
expand("projectVersion" to project.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin { jvmToolchain(11) }
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
maven {
|
||||||
|
name = "GitHubPackages"
|
||||||
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
|
||||||
|
credentials {
|
||||||
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("gpr") {
|
||||||
|
from(components["java"])
|
||||||
|
|
||||||
|
version = project.version.toString()
|
||||||
|
|
||||||
|
pom {
|
||||||
|
name = "ReVanced Patcher"
|
||||||
|
description = "Patcher used by ReVanced."
|
||||||
|
url = "https://revanced.app"
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name = "GNU General Public License v3.0"
|
||||||
|
url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
developers {
|
||||||
|
developer {
|
||||||
|
id = "ReVanced"
|
||||||
|
name = "ReVanced"
|
||||||
|
email = "contact@revanced.app"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scm {
|
||||||
|
connection = "scm:git:git://github.com/revanced/revanced-patcher.git"
|
||||||
|
developerConnection = "scm:git:git@github.com:revanced/revanced-patcher.git"
|
||||||
|
url = "https://github.com/revanced/revanced-patcher"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
revanced-patcher/settings.gradle.kts
Normal file
1
revanced-patcher/settings.gradle.kts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = "revanced-patcher"
|
@@ -0,0 +1,8 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
interface IntegrationsConsumer {
|
||||||
|
fun acceptIntegrations(integrations: List<File>)
|
||||||
|
}
|
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import brut.androlib.apk.ApkInfo
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata about a package.
|
||||||
|
*/
|
||||||
|
class PackageMetadata internal constructor(internal val apkInfo: ApkInfo) {
|
||||||
|
lateinit var packageName: String
|
||||||
|
internal set
|
||||||
|
|
||||||
|
lateinit var packageVersion: String
|
||||||
|
internal set
|
||||||
|
}
|
@@ -0,0 +1,127 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package app.revanced.patcher
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.Patch
|
||||||
|
import dalvik.system.DexClassLoader
|
||||||
|
import lanchon.multidexlib2.BasicDexFileNamer
|
||||||
|
import lanchon.multidexlib2.MultiDexIO
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URLClassLoader
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
import java.util.logging.Logger
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of [Patch]es.
|
||||||
|
*/
|
||||||
|
typealias PatchSet = Set<Patch<*>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [Patch] class.
|
||||||
|
*/
|
||||||
|
typealias PatchClass = KClass<out Patch<*>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A loader of [Patch]es from patch bundles.
|
||||||
|
* This will load all [Patch]es from the given patch bundles that have a name.
|
||||||
|
*
|
||||||
|
* @param getBinaryClassNames A function that returns the binary names of all classes in a patch bundle.
|
||||||
|
* @param classLoader The [ClassLoader] to use for loading the classes.
|
||||||
|
* @param patchBundles A set of patches to initialize this instance with.
|
||||||
|
*/
|
||||||
|
sealed class PatchBundleLoader private constructor(
|
||||||
|
classLoader: ClassLoader,
|
||||||
|
patchBundles: Array<out File>,
|
||||||
|
getBinaryClassNames: (patchBundle: File) -> List<String>,
|
||||||
|
// This constructor parameter is unfortunately necessary,
|
||||||
|
// so that a reference to the mutable set is present in the constructor to be able to add patches to it.
|
||||||
|
// because the instance itself is a PatchSet, which is immutable, that is delegated by the parameter.
|
||||||
|
private val patchSet: MutableSet<Patch<*>> = mutableSetOf()
|
||||||
|
) : PatchSet by patchSet {
|
||||||
|
private val logger = Logger.getLogger(PatchBundleLoader::class.java.name)
|
||||||
|
|
||||||
|
init {
|
||||||
|
patchBundles.flatMap(getBinaryClassNames).asSequence().map {
|
||||||
|
classLoader.loadClass(it)
|
||||||
|
}.filter {
|
||||||
|
Patch::class.java.isAssignableFrom(it)
|
||||||
|
}.mapNotNull { patchClass ->
|
||||||
|
patchClass.getInstance(logger, silent = true)
|
||||||
|
}.filter {
|
||||||
|
it.name != null
|
||||||
|
}.let { patches ->
|
||||||
|
patchSet.addAll(patches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal companion object Utils {
|
||||||
|
/**
|
||||||
|
* Instantiates a [Patch]. If the class is a singleton, the INSTANCE field will be used.
|
||||||
|
*
|
||||||
|
* @param logger The [Logger] to use for logging.
|
||||||
|
* @param silent Whether to suppress logging.
|
||||||
|
* @return The instantiated [Patch] or `null` if the [Patch] could not be instantiated.
|
||||||
|
*/
|
||||||
|
internal fun Class<*>.getInstance(logger: Logger, silent: Boolean = false): Patch<*>? {
|
||||||
|
return try {
|
||||||
|
getField("INSTANCE").get(null)
|
||||||
|
} catch (exception: NoSuchFieldException) {
|
||||||
|
if (!silent) logger.fine(
|
||||||
|
"Patch class '${name}' has no INSTANCE field, therefor not a singleton. " +
|
||||||
|
"Will try to instantiate it."
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
getDeclaredConstructor().newInstance()
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
if (!silent) logger.severe(
|
||||||
|
"Patch class '${name}' is not singleton and has no suitable constructor, " +
|
||||||
|
"therefor cannot be instantiated and will be ignored."
|
||||||
|
)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
} as Patch<*>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchBundleLoader] for JAR files.
|
||||||
|
*
|
||||||
|
* @param patchBundles The path to patch bundles of JAR format.
|
||||||
|
*/
|
||||||
|
class Jar(vararg patchBundles: File) : PatchBundleLoader(
|
||||||
|
URLClassLoader(patchBundles.map { it.toURI().toURL() }.toTypedArray()),
|
||||||
|
patchBundles,
|
||||||
|
{ patchBundle ->
|
||||||
|
JarFile(patchBundle).entries().toList().filter { it.name.endsWith(".class") }
|
||||||
|
.map { it.name.replace('/', '.').replace(".class", "") }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [PatchBundleLoader] for [Dex] files.
|
||||||
|
*
|
||||||
|
* @param patchBundles The path to patch bundles of DEX format.
|
||||||
|
* @param optimizedDexDirectory The directory to store optimized DEX files in.
|
||||||
|
* This parameter is deprecated and has no effect since API level 26.
|
||||||
|
*/
|
||||||
|
class Dex(vararg patchBundles: File, optimizedDexDirectory: File? = null) : PatchBundleLoader(
|
||||||
|
DexClassLoader(
|
||||||
|
patchBundles.joinToString(File.pathSeparator) { it.absolutePath }, optimizedDexDirectory?.absolutePath,
|
||||||
|
null,
|
||||||
|
PatchBundleLoader::class.java.classLoader
|
||||||
|
),
|
||||||
|
patchBundles,
|
||||||
|
{ patchBundle ->
|
||||||
|
MultiDexIO.readDexFile(true, patchBundle, BasicDexFileNamer(), null, null).classes
|
||||||
|
.map { classDef ->
|
||||||
|
classDef.type.substring(1, classDef.length - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
@Deprecated("This constructor is deprecated. Use the constructor with the second parameter instead.")
|
||||||
|
constructor(vararg patchBundles: File) : this(*patchBundles, optimizedDexDirectory = null)
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user