Compare commits

..

2 Commits

Author SHA1 Message Date
semantic-release-bot
1041238632 chore(release): 2.200.1-dev.1 [skip ci]
## [2.200.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v2.200.0...v2.200.1-dev.1) (2023-11-23)

### Bug Fixes

* **YouTube - Enable tablet layout:** Respect the original device layout ([e88d6a8](e88d6a8bba))
2023-11-23 00:01:35 +00:00
oSumAtrIX
e88d6a8bba fix(YouTube - Enable tablet layout): Respect the original device layout 2023-11-23 00:59:32 +01:00
1065 changed files with 17774 additions and 80608 deletions

View File

@@ -1,3 +0,0 @@
[*.{kt,kts}]
ktlint_code_style = intellij_idea
ktlint_standard_no-wildcard-imports = disabled

View File

@@ -70,7 +70,7 @@ body:
Before creating a new bug report, please keep the following in mind:
- **Do not submit a duplicate bug report**: Search for existing bug reports [here](https://github.com/ReVanced/revanced-patches/issues?q=label%3A%22Bug+report%22).
- **Do not submit a duplicate bug report**: You can review existing bug reports [here](https://github.com/ReVanced/revanced-patches/labels/Bug%20report).
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patches/blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea
@@ -102,7 +102,7 @@ body:
label: Acknowledgements
description: Your bug report will be closed if you don't follow the checklist below.
options:
- label: I have checked all open and closed bug reports and this is not a duplicate.
- label: This issue is not a duplicate of an existing bug report.
required: true
- label: I have chosen an appropriate title.
required: true

View File

@@ -70,8 +70,8 @@ body:
Before creating a new feature request, please keep the following in mind:
- **Do not submit a duplicate feature request**: Search for existing feature requests [here](https://github.com/ReVanced/revanced-patches/issues?q=label%3A%22Feature+request%22).
- **Review the contribution guidelines**: Make sure your feature request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patches/blob/main/CONTRIBUTING.md).
- **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-patches/labels/Feature%20request).
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patches/blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea
attributes:
@@ -98,7 +98,7 @@ body:
label: Acknowledgements
description: Your feature request will be closed if you don't follow the checklist below.
options:
- label: I have checked all open and closed feature requests and this is not a duplicate
- label: This issue is not a duplicate of an existing feature request.
required: true
- label: I have chosen an appropriate title.
required: true

View File

@@ -1,22 +0,0 @@
version: 2
updates:
- package-ecosystem: github-actions
labels: []
directory: /
target-branch: dev
schedule:
interval: monthly
- package-ecosystem: npm
labels: []
directory: /
target-branch: dev
schedule:
interval: monthly
- package-ecosystem: gradle
labels: []
directory: /
target-branch: dev
schedule:
interval: monthly

View File

@@ -1,25 +0,0 @@
name: Build pull request
on:
workflow_dispatch:
pull_request:
branches:
- dev
jobs:
release:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cache Gradle
uses: burrunan/gradle-cache-action@v1
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew build --no-daemon

View File

@@ -20,13 +20,12 @@ jobs:
- name: Open pull request
uses: repo-sync/pull-request@v2
with:
destination_branch: "main"
pr_title: "chore: ${{ env.MESSAGE }}"
destination_branch: 'main'
pr_title: 'chore: ${{ env.MESSAGE }}'
pr_body: |
This pull request will ${{ env.MESSAGE }}.
## Dependencies before merge
## Before merging this PR
- [ ] Remember about https://github.com/revanced/revanced-integrations
- [ ] Pull translations from Crowdin
- [ ] https://github.com/revanced/revanced-integrations
pr_draft: true

View File

@@ -1,38 +0,0 @@
name: Pull strings
on:
workflow_dispatch:
schedule:
- cron: 0 0 1 * *
jobs:
pull:
name: Pull strings
permissions:
contents: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: dev
- name: Pull strings
uses: crowdin/github-action@v2
with:
config: crowdin.yml
download_translations: true
localization_branch_name: feat/translations
create_pull_request: true
pull_request_title: "chore: Sync translations"
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
pull_request_base_branch_name: "dev"
commit_message: "chore: Sync translations"
github_user_name: revanced-bot
github_user_email: github@revanced.app
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@@ -1,28 +0,0 @@
name: Push strings
on:
workflow_dispatch:
push:
branches:
- dev
paths:
- src/main/resources/addresources/values/strings.xml
jobs:
push:
name: Push strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Push strings
uses: crowdin/github-action@v2
with:
config: crowdin.yml
upload_sources: true
env:
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@@ -6,13 +6,14 @@ on:
branches:
- main
- dev
pull_request:
branches:
- main
- dev
jobs:
release:
name: Release
permissions:
contents: write
packages: write
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -23,31 +24,25 @@ jobs:
persist-credentials: false
fetch-depth: 0
- name: Cache Node modules
uses: actions/cache@v3
with:
path: |
node_modules
key: npm-${{ hashFiles('package-lock.json') }}
- name: Cache Gradle
uses: burrunan/gradle-cache-action@v1
- name: Build
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew generateMeta clean
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "lts/*"
cache: 'npm'
- name: Install dependencies
- name: Setup semantic-release
run: npm install
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
fingerprint: ${{ vars.GPG_FINGERPRINT }}
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
run: npm exec semantic-release

View File

@@ -1,18 +0,0 @@
name: Update Gradle wrapper
on:
schedule:
- cron: "0 0 1 * *"
workflow_dispatch:
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@v1
with:
target-branch: dev

View File

@@ -21,11 +21,11 @@
"@semantic-release/git",
{
"assets": [
"README.md",
"CHANGELOG.md",
"gradle.properties",
"patches.json"
],
"message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
]
}
],
[
@@ -33,7 +33,7 @@
{
"assets": [
{
"path": "build/libs/revanced-patches*"
"path": "build/libs/*.jar"
},
{
"path": "patches.json"

File diff suppressed because it is too large Load Diff

View File

@@ -64,15 +64,15 @@ This document describes how to contribute to ReVanced Patches.
## 📖 Resources to help you get started
* The [documentation](https://github.com/ReVanced/revanced-patcher/tree/main/docs) contains the fundamentals
of ReVanced Patcher and how to use ReVanced Patcher to create patches
* The [documentation](https://github.com/ReVanced/revanced-patches/tree/docs/docs) provides the fundamentals of patches
and everything necessary to create your own patch from scratch
* [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
* [Issues](https://github.com/ReVanced/revanced-patches/issues) are where we keep track of bugs and feature requests
## 🙏 Submitting a feature request
Features can be requested by opening an issue using the
[Feature request issue template](https://github.com/ReVanced/revanced-patches/issues/new?assignees=&labels=Feature+request&projects=&template=feature_request.yml&title=feat%3A+).
[Feature request issue template](https://github.com/ReVanced/revanced-patches/issues/new?assignees=&labels=Feature+request&projects=&template=feature-request.yml&title=feat%3A+).
> **Note**
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Patches.
@@ -81,11 +81,7 @@ Features can be requested by opening an issue using the
## 🐞 Submitting a bug report
If you encounter a bug while using ReVanced Patches, open an issue using the
[Bug report issue template](https://github.com/ReVanced/revanced-patches/issues/new?assignees=&labels=Bug+report&projects=&template=bug_report.yml&title=bug%3A+).
## 🌐 Submitting translations
You can contribute translations at [translate.revanced.app](https://translate.revanced.app).
[Bug report issue template](https://github.com/ReVanced/revanced-patches/issues/new?assignees=&labels=Bug+report&projects=&template=bug-report.yml&title=bug%3A+).
## 🧑‍⚖️ Guidelines for requesting or contributing patches
@@ -111,18 +107,19 @@ are unaffected by this change.
* Payment circumvention: We do not accept patches that exist solely to bypass payment for the app or any of its features
* Malicious patches: Patches that are malicious in nature are not allowed
## 📝 How to contribute
1. Before contributing, it is recommended to open an issue to discuss your change
1. Before contributing, it is recommended to open an issue to discuss your change
with the maintainers of ReVanced Patches. This will help you determine whether your change is acceptable
and whether it is worth your time to implement it
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
3. Commit your changes. In case you are contributing a new patch, make sure to follow the conventions for patches
described in the [ReVanced Patcher documentation](https://github.com/ReVanced/revanced-patcher/tree/main/docs)
described in the [documentation](https://github.com/ReVanced/revanced-patches/tree/docs/docs)
4. Submit a pull request to the `dev` branch of the repository and reference issues
that your pull request closes in the description of your pull request
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
it will be merged into the `dev` branch and will be included in the next release of ReVanced Patches
❤️ Thank you for considering contributing to ReVanced Patches,
ReVanced

View File

@@ -67,7 +67,7 @@ This repository contains a collection of ReVanced Patches.
## ❓ About
Patches are small modifications to Android apps that allow you to change the behavior of or add new features,
Patches are small modifications to Android apps that allow you to change the behaviour of or add new features,
block ads, customize the appearance, and much more.
## 💪 Features
@@ -77,11 +77,11 @@ Some of the features the patches provide are:
* 🚫 **Block ads**: Say goodbye to ads
***Customize your app**: Personalize the appearance of apps with various layouts and themes
* 🪄 **Add new features**: Extend the functionality of apps with lots of new features
* ⚙️ **Miscellaneous and general purpose**: Rename packages, enable debugging, disable screen capture restrictions,
export activities, etc.
* ⚙️ **Miscellaneous and general purpose**: Rename packages, enable debugging, disable screen capture restrictions,
export activities, etc.
***And much more!**
For a complete list of all available patches, visit [revanced.app/patches](https://revanced.app/patches).
For a full list of all available patches, visit [revanced.app/patches](https://revanced.app/patches).
## 🚀 How to get started
@@ -93,13 +93,17 @@ You can use [ReVanced CLI](https://github.com/ReVanced/revanced-cli) or [ReVance
Thank you for considering contributing to ReVanced Patches. You can find the contribution guidelines [here](CONTRIBUTING.md).
### 📃 Documentation
The documentation provides the fundamentals of patches and everything necessary to create your own patch from scratch.
You can find it [here](https://github.com/ReVanced/revanced-patches/tree/docs/docs).
### 🛠️ Building
To build ReVanced Patches, you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
In order to build ReVanced Patches, you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
## 📜 Licence
ReVanced Patches is licensed under the GPLv3 license. Please see the [license file](LICENSE) for more information.
[tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced Patches as long as you track changes/dates in source files.
Any modifications to ReVanced Patches must also be made available under the GPL,
along with build & install instructions.
ReVanced Patches is licensed under the GPLv3 licence. Please see the [licence file](LICENSE) for more information.
[tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced patches as long as you track changes/dates in source files.
Any modifications to ReVanced Patches must also be made available under the GPL along with build & install instructions.

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,8 @@
import org.gradle.kotlin.dsl.support.listFilesOrdered
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlin)
alias(libs.plugins.binary.compatibility.validator)
kotlin("jvm") version "1.9.10"
`maven-publish`
signing
}
group = "app.revanced"
@@ -14,12 +11,12 @@ repositories {
mavenCentral()
mavenLocal()
google()
maven { url = uri("https://jitpack.io") }
// Required for FlexVer-Java
maven {
// A repository must be specified for some reason. "registry" is a dummy.
url = uri("https://maven.pkg.github.com/revanced/registry")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
url = uri("https://repo.sleeping.town")
content {
includeGroup("com.unascribed")
}
}
}
@@ -31,40 +28,18 @@ dependencies {
implementation(libs.guava)
// Used in JsonGenerator.
implementation(libs.gson)
// Android API stubs defined here.
compileOnly(project(":stub"))
// A dependency to the Android library unfortunately fails the build, which is why this is required.
compileOnly(project("dummy"))
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
java {
targetCompatibility = JavaVersion.VERSION_11
jvmToolchain(11)
}
tasks {
withType(Jar::class) {
exclude("app/revanced/meta")
manifest {
attributes["Name"] = "ReVanced Patches"
attributes["Description"] = "Patches for ReVanced."
attributes["Version"] = version
attributes["Timestamp"] = System.currentTimeMillis().toString()
attributes["Source"] = "git@github.com:revanced/revanced-patches.git"
attributes["Author"] = "ReVanced"
attributes["Contact"] = "contact@revanced.app"
attributes["Origin"] = "https://revanced.app"
attributes["License"] = "GNU General Public License v3.0"
}
}
register("buildDexJar") {
description = "Build and add a DEX to the JAR file"
group = "build"
register<DefaultTask>("generateBundle") {
description = "Generate dex files from build and bundle them in the jar file"
dependsOn(build)
@@ -72,50 +47,39 @@ tasks {
val d8 = File(System.getenv("ANDROID_HOME")).resolve("build-tools")
.listFilesOrdered().last().resolve("d8").absolutePath
val patchesJar = configurations.archives.get().allArtifacts.files.files.first().absolutePath
val artifacts = configurations.archives.get().allArtifacts.files.files.first().absolutePath
val workingDirectory = layout.buildDirectory.dir("libs").get().asFile
exec {
workingDir = workingDirectory
commandLine = listOf(d8, "--release", patchesJar)
commandLine = listOf(d8, artifacts)
}
exec {
workingDir = workingDirectory
commandLine = listOf("zip", "-u", patchesJar, "classes.dex")
commandLine = listOf("zip", "-u", artifacts, "classes.dex")
}
}
}
register<JavaExec>("generatePatchesFiles") {
description = "Generate patches files"
register<JavaExec>("generateMeta") {
description = "Generate metadata for this bundle"
dependsOn(build)
classpath = sourceSets["main"].runtimeClasspath
mainClass.set("app.revanced.generator.MainKt")
mainClass.set("app.revanced.meta.PatchesFileGenerator")
}
// Needed by gradle-semantic-release-plugin.
// Required to run tasks because Gradle semantic-release plugin runs the publish task.
// Tracking: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
publish {
dependsOn("buildDexJar")
dependsOn("generatePatchesFiles")
named("publish") {
dependsOn("generateBundle")
dependsOn("generateMeta")
}
}
publishing {
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/revanced/revanced-patches")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
}
}
}
publications {
create<MavenPublication>("revanced-patches-publication") {
from(components["java"])
@@ -146,10 +110,4 @@ publishing {
}
}
}
}
signing {
useGpgCmd()
sign(publishing.publications["revanced-patches-publication"])
}
}

View File

@@ -1,8 +0,0 @@
project_id_env: "CROWDIN_PROJECT_ID"
api_token_env: "CROWDIN_PERSONAL_TOKEN"
preserve_hierarchy: false
files:
- source: src/main/resources/addresources/values/strings.xml
translation: src/main/resources/addresources/values-%android_code%/strings.xml
skip_untranslated_strings: true

9
dummy/build.gradle.kts Normal file
View File

@@ -0,0 +1,9 @@
plugins {
id("java")
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
}

View File

@@ -0,0 +1,9 @@
package android.os;
import java.io.File;
public final class Environment {
public static File getExternalStorageDirectory() {
throw new UnsupportedOperationException("Stub");
}
}

View File

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

View File

@@ -1,18 +1,11 @@
[versions]
revanced-patcher = "19.3.1"
#noinspection GradleDependency
smali = "3.0.5" # 3.0.7 breaks binary compatibility. Tracking https://github.com/google/smali/issues/58.
guava = "33.2.1-jre"
gson = "2.11.0"
binary-compatibility-validator = "0.15.1"
kotlin = "2.0.0"
revanced-patcher = "19.0.0"
smali = "3.0.3"
guava = "32.1.2-jre"
gson = "2.10.1"
[libraries]
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
smali = { module = "com.android.tools.smali:smali", version.ref = "smali" }
guava = { module = "com.google.guava:guava", version.ref = "guava" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
[plugins]
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

Binary file not shown.

View File

@@ -1,8 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
networkTimeout=10000
validateDistributionUrl=true
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStorePath=wrapper/dist

17
gradlew vendored
View File

@@ -83,8 +83,7 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -145,7 +144,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -153,7 +152,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -202,11 +201,11 @@ 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:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \

20
gradlew.bat vendored
View File

@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail

11494
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
{
"devDependencies": {
"@saithodev/semantic-release-backmerge": "^4.0.1",
"@saithodev/semantic-release-backmerge": "^3.2.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^24.1.2"
"gradle-semantic-release-plugin": "^1.8.0",
"semantic-release": "^22.0.8"
}
}

1
patches.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +1,9 @@
include("dummy")
rootProject.name = "revanced-patches"
buildCache {
local {
isEnabled = "CI" !in System.getenv()
isEnabled = !System.getenv().containsKey("CI")
}
}
include(":stub")

View File

@@ -0,0 +1,127 @@
package app.revanced.extensions
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.mapping.misc.ResourceMappingPatch
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
import com.android.tools.smali.dexlib2.iface.reference.Reference
import com.android.tools.smali.dexlib2.util.MethodUtil
import org.w3c.dom.Node
/**
* The [PatchException] of failing to resolve a [MethodFingerprint].
*
* @return The [PatchException].
*/
val MethodFingerprint.exception
get() = PatchException("Failed to resolve ${this.javaClass.simpleName}")
/**
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
*
* @param method The [Method] to find.
* @return The [MutableMethod].
*/
fun MutableClass.findMutableMethodOf(method: Method) = this.methods.first {
MethodUtil.methodSignaturesMatch(it, method)
}
/**
* apply a transform to all methods of the class.
*
* @param transform the transformation function. original method goes in, transformed method goes out.
*/
fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod) {
val transformedMethods = methods.map { it.transform() }
methods.clear()
methods.addAll(transformedMethods)
}
fun Node.doRecursively(action: (Node) -> Unit) {
action(this)
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
}
fun MutableMethod.injectHideViewCall(
insertIndex: Int,
viewRegister: Int,
classDescriptor: String,
targetMethod: String
) = addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V"
)
/**
* Find the index of the first instruction with the id of the given resource name.
*
* @param resourceName the name of the resource to find the id for.
* @return the index of the first instruction with the id of the given resource name, or -1 if not found.
*/
fun Method.findIndexForIdResource(resourceName: String): Int {
fun getIdResourceId(resourceName: String) = ResourceMappingPatch.resourceMappings.single {
it.type == "id" && it.name == resourceName
}.id
return indexOfFirstWideLiteralInstructionValue(getIdResourceId(resourceName))
}
/**
* Find the index of the first wide literal instruction with the given value.
*
* @return the first literal instruction with the value, or -1 if not found.
*/
fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let {
it.instructions.indexOfFirst { instruction ->
(instruction as? WideLiteralInstruction)?.wideLiteral == literal
}
} ?: -1
/**
* Check if the method contains a literal with the given value.
*
* @return if the method contains a literal with the given value.
*/
fun Method.containsWideLiteralInstructionValue(literal: Long) =
indexOfFirstWideLiteralInstructionValue(literal) >= 0
/**
* Traverse the class hierarchy starting from the given root class.
*
* @param targetClass the class to start traversing the class hierarchy from.
* @param callback function that is called for every class in the hierarchy.
*/
fun BytecodeContext.traverseClassHierarchy(targetClass: MutableClass, callback: MutableClass.() -> Unit) {
callback(targetClass)
this.findClass(targetClass.superclass ?: return)?.mutableClass?.let {
traverseClassHierarchy(it, callback)
}
}
/**
* Get the [Reference] of an [Instruction] as [T].
*
* @param T The type of [Reference] to cast to.
* @return The [Reference] as [T] or null
* if the [Instruction] is not a [ReferenceInstruction] or the [Reference] is not of type [T].
* @see ReferenceInstruction
*/
inline fun <reified T : Reference> Instruction.getReference() = (this as? ReferenceInstruction)?.reference as? T
/**
* Get the index of the first [Instruction] that matches the predicate.
*
* @param predicate The predicate to match.
* @return The index of the first [Instruction] that matches the predicate.
*/
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) =
this.implementation!!.instructions.indexOfFirst(predicate)

View File

@@ -1,12 +0,0 @@
package app.revanced.generator
import app.revanced.patcher.PatchBundleLoader
import java.io.File
internal fun main() = PatchBundleLoader.Jar(
File("build/libs/").listFiles { it -> it.name.endsWith(".jar") }!!.first(),
).also { loader ->
if (loader.isEmpty()) throw IllegalStateException("No patches found")
}.let { bundle ->
arrayOf(JsonPatchesFileGenerator()).forEach { generator -> generator.generate(bundle) }
}

View File

@@ -1,7 +0,0 @@
package app.revanced.generator
import app.revanced.patcher.PatchSet
internal interface PatchesFileGenerator {
fun generate(patches: PatchSet)
}

View File

@@ -1,11 +1,11 @@
package app.revanced.generator
package app.revanced.meta
import app.revanced.patcher.PatchSet
import app.revanced.patcher.patch.Patch
import com.google.gson.GsonBuilder
import java.io.File
internal class JsonPatchesFileGenerator : PatchesFileGenerator {
internal class JsonGenerator : PatchesFileGenerator {
override fun generate(patches: PatchSet) = patches.map {
JsonPatch(
it.name!!,
@@ -20,9 +20,9 @@ internal class JsonPatchesFileGenerator : PatchesFileGenerator {
option.values,
option.title,
option.description,
option.required,
option.required
)
},
}
)
}.let {
File("patches.json").writeText(GsonBuilder().serializeNulls().create().toJson(it))
@@ -35,7 +35,7 @@ internal class JsonPatchesFileGenerator : PatchesFileGenerator {
val compatiblePackages: Set<Patch.CompatiblePackage>? = null,
val use: Boolean = true,
val requiresIntegrations: Boolean = false,
val options: List<Option>,
val options: List<Option>
) {
class Option(
val key: String,
@@ -46,4 +46,4 @@ internal class JsonPatchesFileGenerator : PatchesFileGenerator {
val required: Boolean,
)
}
}
}

View File

@@ -0,0 +1,20 @@
package app.revanced.meta
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.PatchSet
import java.io.File
internal interface PatchesFileGenerator {
fun generate(patches: PatchSet)
private companion object {
@JvmStatic
fun main(args: Array<String>) = PatchBundleLoader.Jar(
File("build/libs/").listFiles { it -> it.name.endsWith(".jar") }!!.first()
).also { loader ->
if (loader.isEmpty()) throw IllegalStateException("No patches found")
}.let { bundle ->
arrayOf(JsonGenerator()).forEach { generator -> generator.generate(bundle) }
}
}
}

View File

@@ -7,34 +7,29 @@ import app.revanced.patcher.patch.annotation.Patch
@Patch(
name = "Export all activities",
description = "Makes all app activities exportable.",
use = false,
use = false
)
@Suppress("unused")
object ExportAllActivitiesPatch : ResourcePatch() {
private const val EXPORTED_FLAG = "android:exported"
override fun execute(context: ResourceContext) {
context.xmlEditor["AndroidManifest.xml"].use { editor ->
val document = editor.file
val activities = document.getElementsByTagName("activity")
for (i in 0..activities.length) {
for(i in 0..activities.length) {
activities.item(i)?.apply {
val exportedAttribute = attributes.getNamedItem(EXPORTED_FLAG)
if (exportedAttribute != null) {
if (exportedAttribute.nodeValue != "true") {
if (exportedAttribute.nodeValue != "true")
exportedAttribute.nodeValue = "true"
}
}
// Reason why the attribute is added in the case it does not exist:
// https://github.com/revanced/revanced-patches/pull/1751/files#r1141481604
else {
document.createAttribute(EXPORTED_FLAG)
.apply { value = "true" }
.let(attributes::setNamedItem)
}
else document.createAttribute(EXPORTED_FLAG)
.apply { value = "true" }
.let(attributes::setNamedItem)
}
}
}

View File

@@ -2,10 +2,10 @@ package app.revanced.patches.all.connectivity.wifi.spoof
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.Instruction35cInfo
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.util.patch.AbstractTransformInstructionsPatch
import app.revanced.util.patch.IMethodCall
import app.revanced.util.patch.Instruction35cInfo
import app.revanced.util.patch.filterMapInstruction35c
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
@@ -17,8 +17,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction
requiresIntegrations = true
)
@Suppress("unused")
object SpoofWifiPatch : BaseTransformInstructionsPatch<Instruction35cInfo>() {
private const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX = "Lapp/revanced/integrations/all/connectivity/wifi/spoof/SpoofWifiPatch"
object SpoofWifiPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
private const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX = "Lapp/revanced/all/connectivity/wifi/spoof/SpoofWifiPatch"
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "$INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX;"
override fun filterMap(
@@ -40,7 +40,7 @@ object SpoofWifiPatch : BaseTransformInstructionsPatch<Instruction35cInfo>() {
// Information about method calls we want to replace
private enum class MethodCall(
enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,

View File

@@ -1,73 +0,0 @@
package app.revanced.patches.all.directory
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
@Patch(
name = "Change data directory location",
description = "Changes the data directory in the application from " +
"the app internal storage directory to /sdcard/android/data accessible by root-less devices." +
"Using this patch can cause unexpected issues with some apps.",
use = false,
)
@Suppress("unused")
object ChangeDataDirectoryLocationPatch : BaseTransformInstructionsPatch<Int>() {
override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int,
): Int? {
val reference = instruction.getReference<MethodReference>() ?: return null
if (!MethodUtil.methodSignaturesMatch(reference, MethodCall.GetDir.reference)) {
return null
}
return instructionIndex
}
override fun transform(
mutableMethod: MutableMethod,
entry: Int,
) = transformMethodCall(entry, mutableMethod)
private fun transformMethodCall(
instructionIndex: Int,
mutableMethod: MutableMethod,
) {
val getDirInstruction = mutableMethod.getInstruction<Instruction35c>(instructionIndex)
val contextRegister = getDirInstruction.registerC
val dataRegister = getDirInstruction.registerD
mutableMethod.replaceInstruction(
instructionIndex,
"invoke-virtual { v$contextRegister, v$dataRegister }, " +
"Landroid/content/Context;->getExternalFilesDir(Ljava/lang/String;)Ljava/io/File;",
)
}
private enum class MethodCall(
val reference: MethodReference,
) {
GetDir(
ImmutableMethodReference(
"Landroid/content/Context;",
"getDir",
listOf("Ljava/lang/String;", "I"),
"Ljava/io/File;",
),
),
}
}

View File

@@ -7,7 +7,7 @@ import app.revanced.patcher.patch.annotation.Patch
@Patch(
name = "Predictive back gesture",
description = "Enables the predictive back gesture introduced on Android 13.",
use = false,
use = false
)
@Suppress("unused")
object PredictiveBackGesturePatch : ResourcePatch() {

View File

@@ -1,56 +0,0 @@
@file:Suppress("unused")
package app.revanced.patches.all.location.hide
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.fromMethodReference
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Hide mock location",
description = "Prevents the app from knowing the device location is being mocked by a third party app.",
use = false
)
object HideMockLocationPatch : BaseTransformInstructionsPatch<Pair<Instruction, Int>>() {
override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
): Pair<Instruction, Int>? {
val reference = instruction.getReference<MethodReference>() ?: return null
if (fromMethodReference<MethodCall>(reference) == null) return null
return instruction to instructionIndex
}
override fun transform(mutableMethod: MutableMethod, entry: Pair<Instruction, Int>) {
val (instruction, index) = entry
instruction as FiveRegisterInstruction
// Replace return value with a constant `false` boolean.
mutableMethod.replaceInstruction(
index + 1,
"const/4 v${instruction.registerC}, 0x0"
)
}
}
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String
) : IMethodCall {
IsMock("Landroid/location/Location;", "isMock", emptyArray(), "Z"),
IsFromMockProvider("Landroid/location/Location;", "isFromMockProvider", emptyArray(), "Z")
}

View File

@@ -1,120 +0,0 @@
package app.revanced.patches.all.misc.build
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
abstract class BaseSpoofBuildInfoPatch : BaseTransformInstructionsPatch<Pair<Int, Pair<String, String>>>() {
// The build information supported32BitAbis, supported64BitAbis, and supportedAbis are not supported for now,
// because initializing an array in transform is a bit more complex.
protected open val board: String? = null
protected open val bootloader: String? = null
protected open val brand: String? = null
protected open val cpuAbi: String? = null
protected open val cpuAbi2: String? = null
protected open val device: String? = null
protected open val display: String? = null
protected open val fingerprint: String? = null
protected open val hardware: String? = null
protected open val host: String? = null
protected open val id: String? = null
protected open val manufacturer: String? = null
protected open val model: String? = null
protected open val odmSku: String? = null
protected open val product: String? = null
protected open val radio: String? = null
protected open val serial: String? = null
protected open val sku: String? = null
protected open val socManufacturer: String? = null
protected open val socModel: String? = null
protected open val tags: String? = null
protected open val time: Long? = null
protected open val type: String? = null
protected open val user: String? = null
// Lazy, so that patch options above are initialized before they are accessed.
private val replacements: Map<String, Pair<String, String>> by lazy {
buildMap {
if (board != null) put("BOARD", "const-string" to "\"$board\"")
if (bootloader != null) put("BOOTLOADER", "const-string" to "\"$bootloader\"")
if (brand != null) put("BRAND", "const-string" to "\"$brand\"")
if (cpuAbi != null) put("CPU_ABI", "const-string" to "\"$cpuAbi\"")
if (cpuAbi2 != null) put("CPU_ABI2", "const-string" to "\"$cpuAbi2\"")
if (device != null) put("DEVICE", "const-string" to "\"$device\"")
if (display != null) put("DISPLAY", "const-string" to "\"$display\"")
if (fingerprint != null) put("FINGERPRINT", "const-string" to "\"$fingerprint\"")
if (hardware != null) put("HARDWARE", "const-string" to "\"$hardware\"")
if (host != null) put("HOST", "const-string" to "\"$host\"")
if (id != null) put("ID", "const-string" to "\"$id\"")
if (manufacturer != null) put("MANUFACTURER", "const-string" to "\"$manufacturer\"")
if (model != null) put("MODEL", "const-string" to "\"$model\"")
if (odmSku != null) put("ODM_SKU", "const-string" to "\"$odmSku\"")
if (product != null) put("PRODUCT", "const-string" to "\"$product\"")
if (radio != null) put("RADIO", "const-string" to "\"$radio\"")
if (serial != null) put("SERIAL", "const-string" to "\"$serial\"")
if (sku != null) put("SKU", "const-string" to "\"$sku\"")
if (socManufacturer != null) put("SOC_MANUFACTURER", "const-string" to "\"$socManufacturer\"")
if (socModel != null) put("SOC_MODEL", "const-string" to "\"$socModel\"")
if (tags != null) put("TAGS", "const-string" to "\"$tags\"")
if (time != null) put("TIME", "const-wide" to "$time")
if (type != null) put("TYPE", "const-string" to "\"$type\"")
if (user != null) put("USER", "const-string" to "\"$user\"")
}
}
override fun filterMap(
classDef: ClassDef,
method: Method,
instruction: Instruction,
instructionIndex: Int
): Pair<Int, Pair<String, String>>? {
val reference = instruction.getReference<FieldReference>() ?: return null
if (reference.definingClass != BUILD_CLASS_DESCRIPTOR) return null
return replacements[reference.name]?.let { instructionIndex to it }
}
override fun transform(mutableMethod: MutableMethod, entry: Pair<Int, Pair<String, String>>) {
val (index, replacement) = entry
val (opcode, operand) = replacement
val register = mutableMethod.getInstruction<OneRegisterInstruction>(index).registerA
mutableMethod.replaceInstruction(index, "$opcode v$register, $operand")
}
private companion object {
private const val BUILD_CLASS_DESCRIPTOR = "Landroid/os/Build;"
}
}

View File

@@ -1,183 +0,0 @@
package app.revanced.patches.all.misc.build
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.longPatchOption
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
@Patch(
name = "Spoof build info",
description = "Spoof the information about the current build.",
use = false
)
@Suppress("unused")
class SpoofBuildInfoPatch : BaseSpoofBuildInfoPatch() {
override val board by stringPatchOption(
key = "board",
default = null,
title = "Board",
description = "The name of the underlying board, like \"goldfish\"."
)
override val bootloader by stringPatchOption(
key = "bootloader",
default = null,
title = "Bootloader",
description = "The system bootloader version number."
)
override val brand by stringPatchOption(
key = "brand",
default = null,
title = "Brand",
description = "The consumer-visible brand with which the product/hardware will be associated, if any."
)
override val cpuAbi by stringPatchOption(
key = "cpu-abi",
default = null,
title = "CPU ABI",
description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead."
)
override val cpuAbi2 by stringPatchOption(
key = "cpu-abi-2",
default = null,
title = "CPU ABI 2",
description = "This field was deprecated in API level 21. Use SUPPORTED_ABIS instead."
)
override val device by stringPatchOption(
key = "device",
default = null,
title = "Device",
description = "The name of the industrial design."
)
override val display by stringPatchOption(
key = "display",
default = null,
title = "Display",
description = "A build ID string meant for displaying to the user."
)
override val fingerprint by stringPatchOption(
key = "fingerprint",
default = null,
title = "Fingerprint",
description = "A string that uniquely identifies this build."
)
override val hardware by stringPatchOption(
key = "hardware",
default = null,
title = "Hardware",
description = "The name of the hardware (from the kernel command line or /proc)."
)
override val host by stringPatchOption(
key = "host",
default = null,
title = "Host",
description = "The host."
)
override val id by stringPatchOption(
key = "id",
default = null,
title = "ID",
description = "Either a changelist number, or a label like \"M4-rc20\"."
)
override val manufacturer by stringPatchOption(
key = "manufacturer",
default = null,
title = "Manufacturer",
description = "The manufacturer of the product/hardware."
)
override val model by stringPatchOption(
key = "model",
default = null,
title = "Model",
description = "The end-user-visible name for the end product."
)
override val odmSku by stringPatchOption(
key = "odm-sku",
default = null,
title = "ODM SKU",
description = "The SKU of the device as set by the original design manufacturer (ODM)."
)
override val product by stringPatchOption(
key = "product",
default = null,
title = "Product",
description = "The name of the overall product."
)
override val radio by stringPatchOption(
key = "radio",
default = null,
title = "Radio",
description = "This field was deprecated in API level 15. " +
"The radio firmware version is frequently not available when this class is initialized, " +
"leading to a blank or \"unknown\" value for this string. Use getRadioVersion() instead."
)
override val serial by stringPatchOption(
key = "serial",
default = null,
title = "Serial",
description = "This field was deprecated in API level 26. Use getSerial() instead."
)
override val sku by stringPatchOption(
key = "sku",
default = null,
title = "SKU",
description = "The SKU of the hardware (from the kernel command line)."
)
override val socManufacturer by stringPatchOption(
key = "soc-manufacturer",
default = null,
title = "SOC Manufacturer",
description = "The manufacturer of the device's primary system-on-chip."
)
override val socModel by stringPatchOption(
key = "soc-model",
default = null,
title = "SOC Model",
description = "The model name of the device's primary system-on-chip."
)
override val tags by stringPatchOption(
key = "tags",
default = null,
title = "Tags",
description = "Comma-separated tags describing the build, like \"unsigned,debug\"."
)
override val time by longPatchOption(
key = "time",
default = null,
title = "Time",
description = "The time at which the build was produced, given in milliseconds since the UNIX epoch."
)
override val type by stringPatchOption(
key = "type",
default = null,
title = "Type",
description = "The type of build, like \"user\" or \"eng\"."
)
override val user by stringPatchOption(
key = "user",
default = null,
title = "User",
description = "The user."
)
}

View File

@@ -8,18 +8,16 @@ import org.w3c.dom.Element
@Patch(
name = "Enable Android debugging",
description = "Enables Android debugging capabilities. This can slow down the app.",
use = false,
use = false
)
@Suppress("unused")
object EnableAndroidDebuggingPatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
context.xmlEditor["AndroidManifest.xml"].use { editor ->
val document = editor.file
val applicationNode =
document
.getElementsByTagName("application")
.item(0) as Element
context.xmlEditor["AndroidManifest.xml"].use { dom ->
val applicationNode = dom
.file
.getElementsByTagName("application")
.item(0) as Element
// set application as debuggable
applicationNode.setAttribute("android:debuggable", "true")

View File

@@ -1,55 +0,0 @@
package app.revanced.patches.all.misc.hex
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.registerNewPatchOption
import app.revanced.patches.shared.misc.hex.BaseHexPatch
import app.revanced.util.Utils.trimIndentMultiline
import app.revanced.patcher.patch.Patch as PatchClass
@Patch(
name = "Hex",
description = "Replaces a hexadecimal patterns of bytes of files in an APK.",
use = false,
)
@Suppress("unused")
class HexPatch : BaseHexPatch() {
// TODO: Instead of stringArrayOption, use a custom option type to work around
// https://github.com/ReVanced/revanced-library/issues/48.
// Replace the custom option type with a stringArrayOption once the issue is resolved.
private val replacementsOption by registerNewPatchOption<PatchClass<*>, List<String>>(
key = "replacements",
title = "Replacements",
description = """
Hexadecimal patterns to search for and replace with another in a target file.
A pattern is a sequence of case insensitive strings, each representing hexadecimal bytes, separated by spaces.
An example pattern is 'aa 01 02 FF'.
Every pattern must be followed by a pipe ('|'), the replacement pattern,
another pipe ('|'), and the path to the file to make the changes in relative to the APK root.
The replacement pattern must have the same length as the original pattern.
Full example of a valid input:
'aa 01 02 FF|00 00 00 00|path/to/file'
""".trimIndentMultiline(),
required = true,
valueType = "StringArray",
)
override val replacements
get() = replacementsOption!!.map { from ->
val (pattern, replacementPattern, targetFilePath) = try {
from.split("|", limit = 3)
} catch (e: Exception) {
throw PatchException(
"Invalid input: $from.\n" +
"Every pattern must be followed by a pipe ('|'), " +
"the replacement pattern, another pipe ('|'), " +
"and the path to the file to make the changes in relative to the APK root. ",
)
}
Replacement(pattern, replacementPattern, targetFilePath)
}
}

View File

@@ -4,7 +4,6 @@ import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.debugging.EnableAndroidDebuggingPatch
import app.revanced.util.Utils.trimIndentMultiline
import org.w3c.dom.Element
import java.io.File
@@ -12,17 +11,16 @@ import java.io.File
name = "Override certificate pinning",
description = "Overrides certificate pinning, allowing to inspect traffic via a proxy.",
dependencies = [EnableAndroidDebuggingPatch::class],
use = false,
use = false
)
@Suppress("unused")
object OverrideCertificatePinningPatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
val resXmlDirectory = context.get("res/xml")
val resXmlDirectory = context["res/xml"]
// Add android:networkSecurityConfig="@xml/network_security_config" and the "networkSecurityConfig" attribute if it does not exist.
context.xmlEditor["AndroidManifest.xml"].use { editor ->
val document = editor.file
val applicationNode = document.getElementsByTagName("application").item(0) as Element
if (!applicationNode.hasAttribute("networkSecurityConfig")) {
@@ -33,8 +31,10 @@ object OverrideCertificatePinningPatch : ResourcePatch() {
// In case the file does not exist create the "network_security_config.xml" file.
File(resXmlDirectory, "network_security_config.xml").apply {
writeText(
"""
if (!exists()) {
createNewFile()
writeText(
"""
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
@@ -54,8 +54,22 @@ object OverrideCertificatePinningPatch : ResourcePatch() {
</trust-anchors>
</debug-overrides>
</network-security-config>
""".trimIndentMultiline(),
)
"""
)
} else {
// If the file already exists.
readText().let { text ->
if (!text.contains("<certificates src=\"user\" />")) {
writeText(
text.replace(
"<trust-anchors>",
"<trust-anchors>\n<certificates src=\"user\" overridePins=\"true\" />\n<certificates src=\"system\" />"
)
)
}
}
}
}
}
}

View File

@@ -10,22 +10,21 @@ import java.io.Closeable
@Patch(
name = "Change package name",
description = "Appends \".revanced\" to the package name by default. Changing the package name of the app can lead to unexpected issues.",
use = false,
description = "Appends \".revanced\" to the package name by default.",
use = false
)
@Suppress("unused")
object ChangePackageNamePatch : ResourcePatch(), Closeable {
private val packageNameOption =
stringPatchOption(
key = "packageName",
default = "Default",
values = mapOf("Default" to "Default"),
title = "Package name",
description = "The name of the package to rename the app to.",
required = true,
) {
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
}
private val packageNameOption = stringPatchOption(
key = "packageName",
default = "Default",
values = mapOf("Default" to "Default"),
title = "Package name",
description = "The name of the package to rename the app to.",
required = true
) {
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
}
private lateinit var context: ResourceContext
@@ -42,29 +41,22 @@ object ChangePackageNamePatch : ResourcePatch(), Closeable {
* @throws PatchOptionException.ValueValidationException If the package name is invalid.
*/
fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
val packageName = packageNameOption.value!!
val packageName = this.packageNameOption.value!!
return if (packageName == packageNameOption.default) {
fallbackPackageName.also { packageNameOption.value = it }
} else {
return if (packageName == this.packageNameOption.default)
fallbackPackageName.also { this.packageNameOption.value = it }
else
packageName
}
}
override fun close() =
context.xmlEditor["AndroidManifest.xml"].use { editor ->
val document = editor.file
override fun close() = context.xmlEditor["AndroidManifest.xml"].use { editor ->
val manifest = editor.file.getElementsByTagName("manifest").item(0) as Element
val originalPackageName = manifest.getAttribute("package")
val replacementPackageName = packageNameOption.value
var replacementPackageName = this.packageNameOption.value
if (replacementPackageName == this.packageNameOption.default)
replacementPackageName = "$originalPackageName.revanced"
val manifest = document.getElementsByTagName("manifest").item(0) as Element
manifest.setAttribute(
"package",
if (replacementPackageName != packageNameOption.default) {
replacementPackageName
} else {
"${manifest.getAttribute("package")}.revanced"
},
)
}
}
manifest.setAttribute("package", replacementPackageName)
}
}

View File

@@ -1,384 +0,0 @@
package app.revanced.patches.all.misc.resources
import app.revanced.patcher.PatchClass
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.DomFileEditor
import app.revanced.patches.all.misc.resources.AddResourcesPatch.resources
import app.revanced.util.*
import app.revanced.util.resource.ArrayResource
import app.revanced.util.resource.BaseResource
import app.revanced.util.resource.StringResource
import org.w3c.dom.Node
import java.io.Closeable
/**
* An identifier of an app. For example, `youtube`.
*/
private typealias AppId = String
/**
* An identifier of a patch. For example, `ad.general.HideAdsPatch`.
*/
private typealias PatchId = String
/**
* A set of resources of a patch.
*/
private typealias PatchResources = MutableSet<BaseResource>
/**
* A map of resources belonging to a patch.
*/
private typealias AppResources = MutableMap<PatchId, PatchResources>
/**
* A map of resources belonging to an app.
*/
private typealias Resources = MutableMap<AppId, AppResources>
/**
* The value of a resource.
* For example, `values` or `values-de`.
*/
private typealias Value = String
@Patch(description = "Add resources such as strings or arrays to the app.")
object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseResource>> by mutableMapOf(), Closeable {
private lateinit var context: ResourceContext
/**
* A map of all resources associated by their value staged by [execute].
*/
private lateinit var resources: Map<Value, Resources>
/**
* Map of Crowdin locales to Android resource locale names.
*
* Fixme: Instead this patch should detect what locale regions are present in both patches and the target app,
* and automatically merge into the appropriate existing target file.
* So if a target app has only 'es', then the Crowdin file of 'es-rES' should merge into that.
* But if a target app has specific regions (such as 'pt-rBR'),
* then the Crowdin region specific file should merged into that.
*/
private val locales = mapOf(
"af-rZA" to "af",
"am-rET" to "am",
"ar-rSA" to "ar",
"as-rIN" to "as",
"az-rAZ" to "az",
"be-rBY" to "be",
"bg-rBG" to "bg",
"bn-rBD" to "bn",
"bs-rBA" to "bs",
"ca-rES" to "ca",
"cs-rCZ" to "cs",
"da-rDK" to "da",
"de-rDE" to "de",
"el-rGR" to "el",
"es-rES" to "es",
"et-rEE" to "et",
"eu-rES" to "eu",
"fa-rIR" to "fa",
"fi-rFI" to "fi",
"fil-rPH" to "tl",
"fr-rFR" to "fr",
"ga-rIE" to "ga",
"gl-rES" to "gl",
"gu-rIN" to "gu",
"hi-rIN" to "hi",
"hr-rHR" to "hr",
"hu-rHU" to "hu",
"hy-rAM" to "hy",
"in-rID" to "in",
"is-rIS" to "is",
"it-rIT" to "it",
"iw-rIL" to "iw",
"ja-rJP" to "ja",
"ka-rGE" to "ka",
"kk-rKZ" to "kk",
"km-rKH" to "km",
"kn-rIN" to "kn",
"ko-rKR" to "ko",
"ky-rKG" to "ky",
"lo-rLA" to "lo",
"lt-rLT" to "lt",
"lv-rLV" to "lv",
"mk-rMK" to "mk",
"ml-rIN" to "ml",
"mn-rMN" to "mn",
"mr-rIN" to "mr",
"ms-rMY" to "ms",
"my-rMM" to "my",
"nb-rNO" to "nb",
"ne-rIN" to "ne",
"nl-rNL" to "nl",
"or-rIN" to "or",
"pa-rIN" to "pa",
"pl-rPL" to "pl",
"pt-rBR" to "pt-rBR",
"pt-rPT" to "pt-rPT",
"ro-rRO" to "ro",
"ru-rRU" to "ru",
"si-rLK" to "si",
"sk-rSK" to "sk",
"sl-rSI" to "sl",
"sq-rAL" to "sq",
"sr-rCS" to "b+sr+Latn",
"sr-rSP" to "sr",
"sv-rSE" to "sv",
"sw-rKE" to "sw",
"ta-rIN" to "ta",
"te-rIN" to "te",
"th-rTH" to "th",
"tl-rPH" to "tl",
"tr-rTR" to "tr",
"uk-rUA" to "uk",
"ur-rIN" to "ur",
"uz-rUZ" to "uz",
"vi-rVN" to "vi",
"zh-rCN" to "zh-rCN",
"zh-rTW" to "zh-rTW",
"zu-rZA" to "zu",
)
/*
The strategy of this patch is to stage resources present in `/resources/addresources`.
These resources are organized by their respective value and patch.
On AddResourcesPatch#execute, all resources are staged in a temporary map.
After that, other patches that depend on AddResourcesPatch can call
AddResourcesPatch#invoke(PatchClass) to stage resources belonging to that patch
from the temporary map to AddResourcesPatch.
After all patches that depend on AddResourcesPatch have been executed,
AddResourcesPatch#close is finally called to add all staged resources to the app.
*/
override fun execute(context: ResourceContext) {
this.context = context
resources =
buildMap {
/**
* Puts resources under `/resources/addresources/<value>/<resourceKind>.xml` into the map.
*
* @param sourceValue The source value of the resource. For example, `values` or `values-de-rDE`.
* @param destValue The destination value of the resource. For example, 'values' or 'values-de'.
* @param resourceKind The kind of the resource. For example, `strings` or `arrays`.
* @param transform A function that transforms the [Node]s from the XML files to a [BaseResource].
*/
fun addResources(
sourceValue: Value,
destValue: Value = sourceValue,
resourceKind: String,
transform: (Node) -> BaseResource,
) {
inputStreamFromBundledResource(
"addresources",
"$sourceValue/$resourceKind.xml",
)?.let { stream ->
// Add the resources associated with the given value to the map,
// instead of overwriting it.
// This covers the example case such as adding strings and arrays of the same value.
getOrPut(destValue, ::mutableMapOf).apply {
context.xmlEditor[stream].use { editor ->
val document = editor.file
document.getElementsByTagName("app").asSequence().forEach { app ->
val appId = app.attributes.getNamedItem("id").textContent
getOrPut(appId, ::mutableMapOf).apply {
app.forEachChildElement { patch ->
val patchId = patch.attributes.getNamedItem("id").textContent
getOrPut(patchId, ::mutableSetOf).apply {
patch.forEachChildElement { resourceNode ->
val resource = transform(resourceNode)
add(resource)
}
}
}
}
}
}
}
}
}
// Stage all resources to a temporary map.
// Staged resources consumed by AddResourcesPatch#invoke(PatchClass)
// are later used in AddResourcesPatch#close.
try {
val addStringResources = { source: Value, dest: Value ->
addResources(source, dest, "strings", StringResource::fromNode)
}
locales.forEach { (source, dest) -> addStringResources("values-$source", "values-$dest") }
addStringResources("values", "values")
addResources("values", "values", "arrays", ArrayResource::fromNode)
} catch (e: Exception) {
throw PatchException("Failed to read resources", e)
}
}
}
/**
* Adds a [BaseResource] to the map using [MutableMap.getOrPut].
*
* @param value The value of the resource. For example, `values` or `values-de`.
* @param resource The resource to add.
*
* @return True if the resource was added, false if it already existed.
*/
operator fun invoke(
value: Value,
resource: BaseResource,
) = getOrPut(value, ::mutableSetOf).add(resource)
/**
* Adds a list of [BaseResource]s to the map using [MutableMap.getOrPut].
*
* @param value The value of the resource. For example, `values` or `values-de`.
* @param resources The resources to add.
*
* @return True if the resources were added, false if they already existed.
*/
operator fun invoke(
value: Value,
resources: Iterable<BaseResource>,
) = getOrPut(value, ::mutableSetOf).addAll(resources)
/**
* Adds a [StringResource].
*
* @param name The name of the string resource.
* @param value The value of the string resource.
* @param formatted Whether the string resource is formatted. Defaults to `true`.
* @param resourceValue The value of the resource. For example, `values` or `values-de`.
*
* @return True if the resource was added, false if it already existed.
*/
operator fun invoke(
name: String,
value: String,
formatted: Boolean = true,
resourceValue: Value = "values",
) = invoke(resourceValue, StringResource(name, value, formatted))
/**
* Adds an [ArrayResource].
*
* @param name The name of the array resource.
* @param items The items of the array resource.
*
* @return True if the resource was added, false if it already existed.
*/
operator fun invoke(
name: String,
items: List<String>,
) = invoke("values", ArrayResource(name, items))
/**
* Puts all resources of any [Value] staged in [resources] for the given [PatchClass] to [AddResourcesPatch].
*
* @param patch The class of the patch to add resources for.
* @param parseIds A function that parses the [AppId] and [PatchId] from the given [PatchClass].
* This is used to access the resources in [resources] to stage them in [AddResourcesPatch].
* The default implementation assumes that the [PatchClass] qualified name has the following format:
* `<any>.<any>.<any>.<app id>.<patch id>`.
*
* @return True if any resources were added, false if none were added.
*
* @see AddResourcesPatch.close
*/
operator fun invoke(
patch: PatchClass,
parseIds: PatchClass.() -> Pair<AppId, PatchId> = {
val qualifiedName = qualifiedName ?: throw PatchException("Patch qualified name is null")
// This requires qualifiedName to have the following format:
// `<any>.<any>.<any>.<app id>.<patch id>`
with(qualifiedName.split(".")) {
if (size < 5) throw PatchException("Patch qualified name has invalid format")
val appId = this[3]
val patchId = subList(4, size).joinToString(".")
appId to patchId
}
},
): Boolean {
val (appId, patchId) = patch.parseIds()
var result = false
// Stage resources for the given patch to AddResourcesPatch associated with their value.
resources.forEach { (value, resources) ->
resources[appId]?.get(patchId)?.let { patchResources ->
if (invoke(value, patchResources)) result = true
}
}
return result
}
/**
* Adds all resources staged in [AddResourcesPatch] to the app.
* This is called after all patches that depend on [AddResourcesPatch] have been executed.
*/
override fun close() {
operator fun MutableMap<String, Pair<DomFileEditor, Node>>.invoke(
value: Value,
resource: BaseResource,
) {
// TODO: Fix open-closed principle violation by modifying BaseResource#serialize so that it accepts
// a Value and the map of documents. It will then get or put the document suitable for its resource type
// to serialize itself to it.
val resourceFileName =
when (resource) {
is StringResource -> "strings"
is ArrayResource -> "arrays"
else -> throw NotImplementedError("Unsupported resource type")
}
getOrPut(resourceFileName) {
val targetFile =
context.get("res/$value/$resourceFileName.xml").also {
it.parentFile?.mkdirs()
if(it.createNewFile()) {
it.writeText("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>")
}
}
context.xmlEditor[targetFile.path].let { editor ->
val document = editor.file
// Save the target node here as well
// in order to avoid having to call document.getNode("resources")
// but also save the document so that it can be closed later.
editor to document.getNode("resources")
}
}.let { (_, targetNode) ->
targetNode.addResource(resource) { invoke(value, it) }
}
}
forEach { (value, resources) ->
// A map of document associated by their kind (e.g. strings, arrays).
// Each document is accompanied by the target node to which resources are added.
// A map is used because Map#getOrPut allows opening a new document for the duration of a resource value.
// This is done to prevent having to open the files for every resource that is added.
// Instead, it is cached once and reused for resources of the same value.
// This map is later accessed to close all documents for the current resource value.
val documents = mutableMapOf<String, Pair<DomFileEditor, Node>>()
resources.forEach { resource -> documents(value, resource) }
documents.values.forEach { (document, _) -> document.close() }
}
}
}

View File

@@ -1,39 +0,0 @@
package app.revanced.patches.all.misc.versioncode
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.PatchOption.PatchExtensions.intPatchOption
import app.revanced.util.getNode
import org.w3c.dom.Element
@Patch(
name = "Change version code",
description = "Changes the version code of the app. By default the highest version code is set. " +
"This allows older versions of an app to be installed " +
"if their version code is set to the same or a higher value and can stop app stores to update the app.",
use = false,
)
@Suppress("unused")
object ChangeVersionCodePatch : ResourcePatch() {
private val versionCode by intPatchOption(
key = "versionCode",
default = Int.MAX_VALUE,
values = mapOf(
"Lowest" to 1,
"Highest" to Int.MAX_VALUE,
),
title = "Version code",
description = "The version code to use",
required = true,
) {
it!! >= 1
}
override fun execute(context: ResourceContext) {
context.document["AndroidManifest.xml"].use { document ->
val manifestElement = document.getNode("manifest") as Element
manifestElement.setAttribute("android:versionCode", "$versionCode")
}
}
}

View File

@@ -2,10 +2,10 @@ package app.revanced.patches.all.screencapture.removerestriction
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.Instruction35cInfo
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.util.patch.AbstractTransformInstructionsPatch
import app.revanced.util.patch.IMethodCall
import app.revanced.util.patch.Instruction35cInfo
import app.revanced.util.patch.filterMapInstruction35c
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
@@ -18,9 +18,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.Instruction
requiresIntegrations = true
)
@Suppress("unused")
object RemoveCaptureRestrictionPatch : BaseTransformInstructionsPatch<Instruction35cInfo>() {
object RemoveCaptureRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
private const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/integrations/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch"
"Lapp/revanced/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch"
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "$INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX;"
// Information about method calls we want to replace
enum class MethodCall(

View File

@@ -8,17 +8,16 @@ import org.w3c.dom.Element
@Patch(description = "Sets allowAudioPlaybackCapture in manifest to true.")
internal object RemoveCaptureRestrictionResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
context.xmlEditor["AndroidManifest.xml"].use { editor ->
val document = editor.file
// create an xml editor instance
context.xmlEditor["AndroidManifest.xml"].use { dom ->
// get the application node
val applicationNode =
document
.getElementsByTagName("application")
.item(0) as Element
val applicationNode = dom
.file
.getElementsByTagName("application")
.item(0) as Element
// set allowAudioPlaybackCapture attribute to true
applicationNode.setAttribute("android:allowAudioPlaybackCapture", "true")
}
}
}
}

View File

@@ -4,10 +4,10 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.Instruction35cInfo
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.util.patch.AbstractTransformInstructionsPatch
import app.revanced.util.patch.IMethodCall
import app.revanced.util.patch.Instruction35cInfo
import app.revanced.util.patch.filterMapInstruction35c
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
@@ -22,9 +22,9 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
requiresIntegrations = true,
)
@Suppress("unused")
object RemoveScreenshotRestrictionPatch : BaseTransformInstructionsPatch<Instruction35cInfo>() {
object RemoveScreenshotRestrictionPatch : AbstractTransformInstructionsPatch<Instruction35cInfo>() {
private const val INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/integrations/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
"Lapp/revanced/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "$INTEGRATIONS_CLASS_DESCRIPTOR_PREFIX;"
override fun execute(context: BytecodeContext) {
@@ -71,7 +71,7 @@ object RemoveScreenshotRestrictionPatch : BaseTransformInstructionsPatch<Instruc
}
}
private class ModifyLayoutParamsFlags : BaseTransformInstructionsPatch<Pair<Instruction22c, Int>>() {
private class ModifyLayoutParamsFlags : AbstractTransformInstructionsPatch<Pair<Instruction22c, Int>>() {
override fun filterMap(
classDef: ClassDef,
method: Method,

View File

@@ -1,32 +0,0 @@
package app.revanced.patches.all.shortcut.sharetargets
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.util.asSequence
import app.revanced.util.getNode
import org.w3c.dom.Element
import java.io.FileNotFoundException
import java.util.logging.Logger
@Patch(
name = "Remove share targets",
description = "Removes share targets like directly sharing to a frequent contact.",
use = false,
)
@Suppress("unused")
object RemoveShareTargetsPatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
try {
context.document["res/xml/shortcuts.xml"]
} catch (_: FileNotFoundException) {
return Logger.getLogger(this::class.java.name).warning("The app has no shortcuts")
}.use { document ->
val rootNode = document.getNode("shortcuts") as? Element ?: return@use
document.getElementsByTagName("share-target").asSequence().forEach {
rootNode.removeChild(it)
}
}
}
}

View File

@@ -3,9 +3,10 @@ package app.revanced.patches.all.telephony.sim.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.transformation.BaseTransformInstructionsPatch
import app.revanced.util.patch.AbstractTransformInstructionsPatch
import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
@@ -23,30 +24,27 @@ import java.util.*
use = false,
)
@Suppress("unused")
object SpoofSimCountryPatch : BaseTransformInstructionsPatch<Pair<Int, String>>() {
private val countries = Locale.getISOCountries().associateBy { Locale("", it).displayCountry }
object SpoofSimCountryPatch : AbstractTransformInstructionsPatch<Pair<Int, String>>() {
private val isoValidator: PatchOption<String>.(String?) -> Boolean =
{ it: String? -> it?.uppercase() in Locale.getISOCountries() || it == null }
private val networkCountryIso by isoCountryPatchOption(
private val networkCountryIso by stringPatchOption(
"networkCountryIso",
"Network ISO Country Code",
)
private val simCountryIso by isoCountryPatchOption(
"simCountryIso",
"Sim ISO Country Code",
)
private fun isoCountryPatchOption(
key: String,
title: String,
) = stringPatchOption(
key,
null,
countries,
title,
null,
"Network ISO Country Code",
"ISO-3166-1 alpha-2 country code equivalent of the MCC (Mobile Country Code) " +
"of the current registered operator or the cell nearby.",
validator = isoValidator
)
private val simCountryIso by stringPatchOption(
"simCountryIso",
null,
null,
"Sim ISO Country Code",
"ISO-3166-1 alpha-2 country code equivalent for the SIM provider's country code.",
false,
validator = { it: String? -> it == null || it.uppercase() in countries.values }
validator = isoValidator
)
override fun filterMap(

View File

@@ -1,11 +0,0 @@
package app.revanced.patches.amazon.deeplinking
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object DeepLinkingFingerprint : MethodFingerprint(
"Z",
parameters = listOf("L"),
accessFlags = AccessFlags.PRIVATE.value,
strings = listOf("https://www.", "android.intent.action.VIEW")
)

View File

@@ -1,28 +0,0 @@
package app.revanced.patches.amazon.deeplinking
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.util.exception
@Patch(
name = "Always allow deep-linking",
description = "Open Amazon links, even if the app is not set to handle Amazon links.",
compatiblePackages = [CompatiblePackage("com.amazon.mShop.android.shopping")]
)
@Suppress("unused")
object DeepLinkingPatch : BytecodePatch(
setOf(DeepLinkingFingerprint)
) {
override fun execute(context: BytecodeContext) {
DeepLinkingFingerprint.result?.mutableMethod?.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
) ?: throw DeepLinkingFingerprint.exception
}
}

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.backdrops.misc.pro
import app.revanced.util.exception
import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
@@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Pro unlock",
compatiblePackages = [CompatiblePackage("com.backdrops.wallpapers")]
compatiblePackages = [CompatiblePackage("com.backdrops.wallpapers", ["4.52"])]
)
@Suppress("unused")
object ProUnlockPatch : BytecodePatch(
@@ -34,4 +34,4 @@ object ProUnlockPatch : BytecodePatch(
} ?: throw ProUnlockFingerprint.exception
}
}
}

View File

@@ -3,7 +3,7 @@ package app.revanced.patches.backdrops.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object ProUnlockFingerprint : MethodFingerprint(
object ProUnlockFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,

View File

@@ -1,23 +0,0 @@
package app.revanced.patches.bandcamp.limitations
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.bandcamp.limitations.fingerprints.HandlePlaybackLimitsPatch
import app.revanced.util.exception
@Patch(
name = "Remove play limits",
description = "Disables purchase nagging and playback limits of not purchased tracks.",
compatiblePackages = [CompatiblePackage("com.bandcamp.android")],
)
@Suppress("unused")
object RemovePlayLimitsPatch : BytecodePatch(
setOf(HandlePlaybackLimitsPatch),
) {
override fun execute(context: BytecodeContext) =
HandlePlaybackLimitsPatch.result?.mutableMethod?.addInstructions(0, "return-void")
?: throw HandlePlaybackLimitsPatch.exception
}

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.bandcamp.limitations.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object HandlePlaybackLimitsPatch : MethodFingerprint(
strings = listOf("play limits processing track", "found play_count"),
)

View File

@@ -1,23 +1,20 @@
package app.revanced.patches.candylinkvpn
import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.candylinkvpn.fingerprints.IsPremiumPurchasedFingerprint
import app.revanced.util.exception
@Patch(
compatiblePackages = [CompatiblePackage("com.candylink.openvpn")],
)
@Deprecated(
"This patch does not work anymore and will be removed in the future, " +
"because the servers now check the purchase status.",
name = "Unlock pro",
compatiblePackages = [CompatiblePackage("com.candylink.openvpn")]
)
@Suppress("unused")
object UnlockProPatch : BytecodePatch(
setOf(IsPremiumPurchasedFingerprint),
setOf(IsPremiumPurchasedFingerprint)
) {
override fun execute(context: BytecodeContext) {
IsPremiumPurchasedFingerprint.result?.mutableMethod?.addInstructions(
@@ -25,7 +22,7 @@ object UnlockProPatch : BytecodePatch(
"""
const/4 v0, 0x1
return v0
""",
"""
) ?: throw IsPremiumPurchasedFingerprint.exception
}
}
}

View File

@@ -2,7 +2,7 @@ package app.revanced.patches.candylinkvpn.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object IsPremiumPurchasedFingerprint : MethodFingerprint(
object IsPremiumPurchasedFingerprint : MethodFingerprint(
customFingerprint = { methodDef, _ ->
methodDef.definingClass.endsWith("PreferenceProvider;") &&
methodDef.name == "isPremiumPurchased"

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.cieid.restrictions.root
import app.revanced.util.exception
import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.BytecodePatch

View File

@@ -2,7 +2,7 @@ package app.revanced.patches.cieid.restrictions.root.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object CheckRootFingerprint : MethodFingerprint(
object CheckRootFingerprint : MethodFingerprint(
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lit/ipzs/cieid/BaseActivity;" && methodDef.name == "onResume"
}

View File

@@ -1,41 +0,0 @@
package app.revanced.patches.duolingo.ad
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.duolingo.ad.fingerprints.InitializeMonetizationDebugSettingsFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@Patch(
name = "Disable ads",
compatiblePackages = [CompatiblePackage("com.duolingo")]
)
@Suppress("unused")
object DisableAdsPatch : BytecodePatch(
setOf(InitializeMonetizationDebugSettingsFingerprint)
) {
override fun execute(context: BytecodeContext) {
// Couple approaches to remove ads exist:
//
// MonetizationDebugSettings has a boolean value for "disableAds".
// OnboardingState has a getter to check if the user has any "adFreeSessions".
// SharedPreferences has a debug boolean value with key "disable_ads", which maps to "DebugCategory.DISABLE_ADS".
//
// MonetizationDebugSettings seems to be the most general setting to work fine.
InitializeMonetizationDebugSettingsFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex,
"const/4 v$register, 0x1"
)
}
}
}
}

View File

@@ -1,21 +0,0 @@
package app.revanced.patches.duolingo.ad.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object InitializeMonetizationDebugSettingsFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf(
"Z", // disableAds
"Z", // useDebugBilling
"Z", // showManageSubscriptions
"Z", // alwaysShowSuperAds
"Lcom/duolingo/debug/FamilyQuestOverride;",
),
opcodes = listOf(
Opcode.IPUT_BOOLEAN
)
)

View File

@@ -1,35 +0,0 @@
package app.revanced.patches.duolingo.debug
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.duolingo.debug.fingerprints.InitializeBuildConfigProviderFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@Patch(
name = "Enable debug menu",
compatiblePackages = [CompatiblePackage("com.duolingo", ["5.158.4"])],
use = false
)
@Suppress("unused")
object EnableDebugMenuPatch : BytecodePatch(
setOf(InitializeBuildConfigProviderFingerprint)
) {
override fun execute(context: BytecodeContext) {
InitializeBuildConfigProviderFingerprint.resultOrThrow().let {
it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex,
"const/4 v$register, 0x1"
)
}
}
}
}

View File

@@ -1,25 +0,0 @@
package app.revanced.patches.duolingo.debug.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* The `BuildConfigProvider` class has two booleans:
*
* - `isChina`: (usually) compares "play" with "china"...except for builds in China
* - `isDebug`: compares "release" with "debug" <-- we want to force this to `true`
*/
internal object InitializeBuildConfigProviderFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
strings = listOf(
"debug",
"release",
"china",
),
opcodes = listOf(
Opcode.IPUT_BOOLEAN
)
)

View File

@@ -1,96 +0,0 @@
package app.revanced.patches.facebook.ads.mainfeed
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.or
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.BaseModelMapperFingerprint
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.GetSponsoredDataModelTemplateFingerprint
import app.revanced.patches.facebook.ads.mainfeed.fingerprints.GetStoryVisibilityFingerprint
import app.revanced.util.exception
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
@Patch(
name = "Hide 'Sponsored Stories'",
compatiblePackages = [CompatiblePackage("com.facebook.katana")],
)
@Suppress("unused")
object HideSponsoredStoriesPatch : BytecodePatch(
setOf(GetStoryVisibilityFingerprint, GetSponsoredDataModelTemplateFingerprint, BaseModelMapperFingerprint),
) {
private const val GRAPHQL_STORY_TYPE = "Lcom/facebook/graphql/model/GraphQLStory;"
override fun execute(context: BytecodeContext) {
GetStoryVisibilityFingerprint.result?.apply {
val sponsoredDataModelTemplateMethod = GetSponsoredDataModelTemplateFingerprint.resultOrThrow().method
val baseModelMapperMethod = BaseModelMapperFingerprint.resultOrThrow().method
val baseModelWithTreeType = baseModelMapperMethod.returnType
// The "SponsoredDataModelTemplate" methods has the ids in its body to extract sponsored data
// from GraphQL models, but targets the wrong derived type of "BaseModelWithTree". Since those ids
// could change in future version, we need to extract them and call the base implementation directly.
val getSponsoredDataHelperMethod = ImmutableMethod(
classDef.type,
"getSponsoredData",
listOf(ImmutableMethodParameter(GRAPHQL_STORY_TYPE, null, null)),
baseModelWithTreeType,
AccessFlags.PRIVATE or AccessFlags.STATIC,
null,
null,
MutableMethodImplementation(4),
).toMutable().apply {
// Extract the ids of the original method. These ids seem to correspond to model types for
// GraphQL data structure. They are then fed to a method of BaseModelWithTree that populate
// and cast the requested GraphQL subtype. The Ids are found in the two first "CONST" instructions.
val constInstructions = sponsoredDataModelTemplateMethod.implementation!!.instructions
.asSequence()
.filterIsInstance<Instruction31i>()
.take(2)
.toList()
val storyTypeId = constInstructions[0].narrowLiteral
val sponsoredDataTypeId = constInstructions[1].narrowLiteral
addInstructions(
"""
const-class v2, $baseModelWithTreeType
const v1, $storyTypeId
const v0, $sponsoredDataTypeId
invoke-virtual {p0, v2, v1, v0}, $baseModelMapperMethod
move-result-object v0
check-cast v0, $baseModelWithTreeType
return-object v0
""",
)
}
mutableClass.methods.add(getSponsoredDataHelperMethod)
// Check if the parameter type is GraphQLStory and if sponsoredDataModelGetter returns a non-null value.
// If so, hide the story by setting the visibility to StoryVisibility.GONE.
mutableMethod.addInstructionsWithLabels(
scanResult.patternScanResult!!.startIndex,
"""
instance-of v0, p0, $GRAPHQL_STORY_TYPE
if-eqz v0, :resume_normal
invoke-static {p0}, $getSponsoredDataHelperMethod
move-result-object v0
if-eqz v0, :resume_normal
const-string v0, "GONE"
return-object v0
:resume_normal
nop
""",
)
} ?: throw GetStoryVisibilityFingerprint.exception
}
}

View File

@@ -1,21 +0,0 @@
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object BaseModelMapperFingerprint : MethodFingerprint(
accessFlags = (AccessFlags.PUBLIC or AccessFlags.FINAL),
parameters = listOf("Ljava/lang/Class","I","I"),
returnType = "Lcom/facebook/graphql/modelutil/BaseModelWithTree;",
opcodes = listOf(
Opcode.SGET_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.IF_EQ
)
)

View File

@@ -1,23 +0,0 @@
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object GetSponsoredDataModelTemplateFingerprint : MethodFingerprint(
accessFlags = (AccessFlags.PUBLIC or AccessFlags.FINAL),
parameters = listOf(),
returnType = "L",
opcodes = listOf(
Opcode.CONST,
Opcode.CONST,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT
),
customFingerprint = { methodDef, classDef ->
classDef.type == "Lcom/facebook/graphql/model/GraphQLFBMultiAdsFeedUnit;"
}
)

View File

@@ -1,23 +0,0 @@
package app.revanced.patches.facebook.ads.mainfeed.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Annotation
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
internal object GetStoryVisibilityFingerprint : MethodFingerprint(
returnType = "Ljava/lang/String;",
accessFlags = (AccessFlags.PUBLIC or AccessFlags.STATIC),
opcodes = listOf(
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.INSTANCE_OF,
Opcode.IF_NEZ,
Opcode.CONST
),
strings = listOf("This should not be called for base class object"),
)

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.facebook.ads.story
import app.revanced.util.exception
import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch

View File

@@ -1,3 +1,3 @@
package app.revanced.patches.facebook.ads.story.fingerprints
internal object AdsInsertionFingerprint : FieldMethodFingerprint(fieldValue = "AdBucketDataSourceUtil\$attemptAdsInsertion\$1")
object AdsInsertionFingerprint : FieldMethodFingerprint(fieldValue = "AdBucketDataSourceUtil\$attemptAdsInsertion\$1")

View File

@@ -1,3 +1,3 @@
package app.revanced.patches.facebook.ads.story.fingerprints
internal object FetchMoreAdsFingerprint : FieldMethodFingerprint(fieldValue = "AdBucketDataSourceUtil\$attemptFetchMoreAds\$1")
object FetchMoreAdsFingerprint : FieldMethodFingerprint(fieldValue = "AdBucketDataSourceUtil\$attemptFetchMoreAds\$1")

View File

@@ -3,7 +3,7 @@ package app.revanced.patches.facebook.ads.story.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.iface.value.StringEncodedValue
internal abstract class FieldMethodFingerprint(fieldValue: String) : MethodFingerprint(
abstract class FieldMethodFingerprint(fieldValue: String) : MethodFingerprint(
returnType = "V",
parameters = listOf(),
customFingerprint = { methodDef, classDef ->

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.finanzonline.detection.bootloader
import app.revanced.util.exception
import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch

View File

@@ -5,7 +5,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#isBootStateOk (3.0.1)
internal object BootStateFingerprint : MethodFingerprint(
object BootStateFingerprint : MethodFingerprint(
"Z",
accessFlags = AccessFlags.PUBLIC.value,
opcodes = listOf(

View File

@@ -4,7 +4,7 @@ import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.AttestationHelper#createKey (3.0.1)
internal object CreateKeyFingerprint : MethodFingerprint(
object CreateKeyFingerprint : MethodFingerprint(
"Z",
accessFlags = AccessFlags.PUBLIC.value,
strings = listOf("attestation", "SHA-256", "random", "EC", "AndroidKeyStore")

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.finanzonline.detection.root
import app.revanced.util.exception
import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch

View File

@@ -6,7 +6,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
// Located @ at.gv.bmf.bmf2go.taxequalization.tools.utils.RootDetection#isRooted (3.0.1)
internal object RootDetectionFingerprint : MethodFingerprint(
object RootDetectionFingerprint : MethodFingerprint(
"L",
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
parameters = listOf("L"),

View File

@@ -1,32 +0,0 @@
package app.revanced.patches.googlenews.customtabs
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.googlenews.customtabs.fingerprints.LaunchCustomTabFingerprint
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Enable CustomTabs",
description = "Enables CustomTabs to open articles in your default browser.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.magazines")],
)
@Suppress("unused")
object EnableCustomTabs : BytecodePatch(
setOf(LaunchCustomTabFingerprint)
) {
override fun execute(context: BytecodeContext) {
LaunchCustomTabFingerprint.resultOrThrow().let { result ->
result.mutableMethod.apply {
val checkIndex = result.scanResult.patternScanResult!!.endIndex + 1
val register = getInstruction<OneRegisterInstruction>(checkIndex).registerA
replaceInstruction(checkIndex, "const/4 v$register, 0x1")
}
}
}
}

View File

@@ -1,18 +0,0 @@
package app.revanced.patches.googlenews.customtabs.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object LaunchCustomTabFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
opcodes = listOf(
Opcode.IPUT_OBJECT,
Opcode.CONST_4,
Opcode.IPUT,
Opcode.CONST_4,
Opcode.IPUT_BOOLEAN,
),
customFingerprint = { _, classDef -> classDef.endsWith("CustomTabsArticleLauncher;") },
)

View File

@@ -1,6 +0,0 @@
package app.revanced.patches.googlenews.misc.gms
internal object Constants {
const val MAGAZINES_PACKAGE_NAME = "com.google.android.apps.magazines"
const val REVANCED_MAGAZINES_PACKAGE_NAME = "app.revanced.android.magazines"
}

View File

@@ -1,23 +0,0 @@
package app.revanced.patches.googlenews.misc.gms
import app.revanced.patches.googlenews.misc.gms.Constants.MAGAZINES_PACKAGE_NAME
import app.revanced.patches.googlenews.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME
import app.revanced.patches.googlenews.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.googlenews.misc.gms.fingerprints.MagazinesActivityOnCreateFingerprint
import app.revanced.patches.googlenews.misc.integrations.IntegrationsPatch
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch
@Suppress("unused")
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
fromPackageName = MAGAZINES_PACKAGE_NAME,
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
primeMethodFingerprint = null,
mainActivityOnCreateFingerprint = MagazinesActivityOnCreateFingerprint,
integrationsPatchDependency = IntegrationsPatch::class,
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
// Remove version constraint,
// once https://github.com/ReVanced/revanced-patches/pull/3111#issuecomment-2240877277 is resolved.
compatiblePackages = setOf(CompatiblePackage(MAGAZINES_PACKAGE_NAME, setOf("5.108.0.644447823"))),
) {
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
}

View File

@@ -1,11 +0,0 @@
package app.revanced.patches.googlenews.misc.gms
import app.revanced.patches.googlenews.misc.gms.Constants.MAGAZINES_PACKAGE_NAME
import app.revanced.patches.googlenews.misc.gms.Constants.REVANCED_MAGAZINES_PACKAGE_NAME
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportResourcePatch
object GmsCoreSupportResourcePatch : BaseGmsCoreSupportResourcePatch(
fromPackageName = MAGAZINES_PACKAGE_NAME,
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
)

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.googlenews.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object MagazinesActivityOnCreateFingerprint : MethodFingerprint(
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;")
},
)

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.googlenews.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object PrimeMethodFingerprint : MethodFingerprint(
strings = listOf("com.google.android.GoogleCamera", "com.android.vending"),
)

View File

@@ -1,10 +0,0 @@
package app.revanced.patches.googlenews.misc.integrations
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.googlenews.misc.integrations.fingerprints.StartActivityInitFingerprint
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
@Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch(
setOf(StartActivityInitFingerprint),
)

View File

@@ -1,41 +0,0 @@
package app.revanced.patches.googlenews.misc.integrations.fingerprints
import app.revanced.patches.googlenews.misc.integrations.fingerprints.StartActivityInitFingerprint.getApplicationContextIndex
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object StartActivityInitFingerprint : IntegrationsFingerprint(
opcodes = listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
Opcode.IF_EQZ,
Opcode.CONST,
Opcode.INVOKE_VIRTUAL,
Opcode.IPUT_OBJECT,
Opcode.IPUT_BOOLEAN,
Opcode.INVOKE_VIRTUAL, // Calls startActivity.getApplicationContext().
Opcode.MOVE_RESULT_OBJECT,
),
insertIndexResolver = { method ->
getApplicationContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "getApplicationContext"
}
getApplicationContextIndex + 2 // Below the move-result-object instruction.
},
contextRegisterResolver = { method ->
val moveResultInstruction = method.implementation!!.instructions.elementAt(getApplicationContextIndex + 1)
as OneRegisterInstruction
moveResultInstruction.registerA
},
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/StartActivity;")
},
) {
private var getApplicationContextIndex = -1
}

View File

@@ -1,14 +0,0 @@
package app.revanced.patches.googlephotos.features
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.build.BaseSpoofBuildInfoPatch
@Patch(description = "Spoof build info to Google Pixel XL.")
internal class SpoofBuildInfoPatch : BaseSpoofBuildInfoPatch() {
override val brand = "google"
override val manufacturer = "Google"
override val device = "marlin"
override val product = "marlin"
override val model = "Pixel XL"
override val fingerprint = "google/marlin/marlin:10/QP1A.191005.007.A3/5972272:user/release-keys"
}

View File

@@ -1,87 +0,0 @@
package app.revanced.patches.googlephotos.features
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringArrayPatchOption
import app.revanced.patches.googlephotos.features.fingerprints.InitializeFeaturesEnumFingerprint
import app.revanced.util.getReference
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.StringReference
@Patch(
name = "Spoof features",
description = "Spoofs the device to enable Google Pixel exclusive features, including unlimited storage.",
dependencies = [SpoofBuildInfoPatch::class],
compatiblePackages = [CompatiblePackage("com.google.android.apps.photos")],
)
@Suppress("unused")
object SpoofFeaturesPatch : BytecodePatch(setOf(InitializeFeaturesEnumFingerprint)) {
private val featuresToEnable by stringArrayPatchOption(
"featuresToEnable",
arrayOf(
"com.google.android.apps.photos.NEXUS_PRELOAD",
"com.google.android.apps.photos.nexus_preload",
),
title = "Features to enable",
description = "Google Pixel exclusive features to enable. Features up to Pixel XL enable the unlimited storage feature.",
required = true,
)
private val featuresToDisable by stringArrayPatchOption(
"featuresToDisable",
arrayOf(
"com.google.android.apps.photos.PIXEL_2017_PRELOAD",
"com.google.android.apps.photos.PIXEL_2018_PRELOAD",
"com.google.android.apps.photos.PIXEL_2019_MIDYEAR_PRELOAD",
"com.google.android.apps.photos.PIXEL_2019_PRELOAD",
"com.google.android.feature.PIXEL_2020_MIDYEAR_EXPERIENCE",
"com.google.android.feature.PIXEL_2020_EXPERIENCE",
"com.google.android.feature.PIXEL_2021_MIDYEAR_EXPERIENCE",
"com.google.android.feature.PIXEL_2021_EXPERIENCE",
"com.google.android.feature.PIXEL_2022_MIDYEAR_EXPERIENCE",
"com.google.android.feature.PIXEL_2022_EXPERIENCE",
"com.google.android.feature.PIXEL_2023_MIDYEAR_EXPERIENCE",
"com.google.android.feature.PIXEL_2023_EXPERIENCE",
"com.google.android.feature.PIXEL_2024_MIDYEAR_EXPERIENCE",
"com.google.android.feature.PIXEL_2024_EXPERIENCE",
"com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE",
),
title = "Features to disable",
description = "Google Pixel exclusive features to disable." +
"Features after Pixel XL may have to be disabled for unlimited storage depending on the device.",
required = true,
)
override fun execute(context: BytecodeContext) {
val featuresToEnable = featuresToEnable!!.toSet()
val featuresToDisable = featuresToDisable!!.toSet()
InitializeFeaturesEnumFingerprint.resultOrThrow().let { result ->
result.mutableMethod.apply {
getInstructions().filter { it.opcode == Opcode.CONST_STRING }.forEach {
val feature = it.getReference<StringReference>()!!.string
val spoofedFeature = when (feature) {
in featuresToEnable -> "android.hardware.wifi"
in featuresToDisable -> "dummy"
else -> return@forEach
}
val constStringIndex = it.location.index
val constStringRegister = (it as OneRegisterInstruction).registerA
replaceInstruction(
constStringIndex,
"const-string v$constStringRegister, \"$spoofedFeature\"",
)
}
}
}
}
}

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.googlephotos.features.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
object InitializeFeaturesEnumFingerprint : MethodFingerprint(
strings = listOf("com.google.android.apps.photos.NEXUS_PRELOAD"),
)

View File

@@ -1,6 +0,0 @@
package app.revanced.patches.googlephotos.misc.gms
internal object Constants {
const val PHOTOS_PACKAGE_NAME = "com.google.android.apps.photos"
const val REVANCED_PHOTOS_PACKAGE_NAME = "app.revanced.android.photos"
}

View File

@@ -1,21 +0,0 @@
package app.revanced.patches.googlephotos.misc.gms
import app.revanced.patches.googlephotos.misc.gms.Constants.PHOTOS_PACKAGE_NAME
import app.revanced.patches.googlephotos.misc.gms.Constants.REVANCED_PHOTOS_PACKAGE_NAME
import app.revanced.patches.googlephotos.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorGroupIdOption
import app.revanced.patches.googlephotos.misc.gms.fingerprints.PhotosActivityOnCreateFingerprint
import app.revanced.patches.googlephotos.misc.integrations.IntegrationsPatch
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportPatch
@Suppress("unused")
object GmsCoreSupportPatch : BaseGmsCoreSupportPatch(
fromPackageName = PHOTOS_PACKAGE_NAME,
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
primeMethodFingerprint = null,
mainActivityOnCreateFingerprint = PhotosActivityOnCreateFingerprint,
integrationsPatchDependency = IntegrationsPatch::class,
gmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
compatiblePackages = setOf(CompatiblePackage(PHOTOS_PACKAGE_NAME)),
) {
override val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
}

View File

@@ -1,11 +0,0 @@
package app.revanced.patches.googlephotos.misc.gms
import app.revanced.patches.googlephotos.misc.gms.Constants.PHOTOS_PACKAGE_NAME
import app.revanced.patches.googlephotos.misc.gms.Constants.REVANCED_PHOTOS_PACKAGE_NAME
import app.revanced.patches.shared.misc.gms.BaseGmsCoreSupportResourcePatch
object GmsCoreSupportResourcePatch : BaseGmsCoreSupportResourcePatch(
fromPackageName = PHOTOS_PACKAGE_NAME,
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
)

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.googlephotos.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object PhotosActivityOnCreateFingerprint : MethodFingerprint(
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;")
},
)

View File

@@ -1,10 +0,0 @@
package app.revanced.patches.googlephotos.misc.integrations
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.googlephotos.misc.integrations.fingerprints.HomeActivityInitFingerprint
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
@Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch(
setOf(HomeActivityInitFingerprint),
)

View File

@@ -1,37 +0,0 @@
package app.revanced.patches.googlephotos.misc.integrations.fingerprints
import app.revanced.patches.googlephotos.misc.integrations.fingerprints.HomeActivityInitFingerprint.getApplicationContextIndex
import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal object HomeActivityInitFingerprint : IntegrationsFingerprint(
opcodes = listOf(
Opcode.CONST_STRING,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IF_NEZ,
Opcode.INVOKE_VIRTUAL, // Calls getApplicationContext().
Opcode.MOVE_RESULT_OBJECT,
),
insertIndexResolver = { method ->
getApplicationContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "getApplicationContext"
}
getApplicationContextIndex + 2 // Below the move-result-object instruction.
},
contextRegisterResolver = { method ->
val moveResultInstruction = method.implementation!!.instructions.elementAt(getApplicationContextIndex + 1)
as OneRegisterInstruction
moveResultInstruction.registerA
},
customFingerprint = { methodDef, classDef ->
methodDef.name == "onCreate" && classDef.endsWith("/HomeActivity;")
},
) {
private var getApplicationContextIndex = -1
}

View File

@@ -1,33 +0,0 @@
package app.revanced.patches.googlephotos.preferences
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.googlephotos.preferences.fingerprints.BackupPreferencesFingerprint
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Restore hidden 'Back up while charging' toggle",
description = "Restores a hidden toggle to only run backups when the device is charging.",
compatiblePackages = [CompatiblePackage("com.google.android.apps.photos")],
)
@Suppress("unused")
object RestoreHiddenBackUpWhileChargingTogglePatch : BytecodePatch(
setOf(BackupPreferencesFingerprint),
) {
override fun execute(context: BytecodeContext) {
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
BackupPreferencesFingerprint.result?.let {
val chargingPrefStringIndex = it.scanResult.stringsScanResult!!.matches.first().index
it.mutableMethod.apply {
// Get the register of move-result.
val resultRegister = getInstruction<OneRegisterInstruction>(chargingPrefStringIndex + 2).registerA
// Insert const after move-result to override register as true.
addInstruction(chargingPrefStringIndex + 3, "const/4 v$resultRegister, 0x1")
}
} ?: throw Exception("BackupPreferencesFingerprint result not found")
}
}

View File

@@ -1,10 +0,0 @@
package app.revanced.patches.googlephotos.preferences.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object BackupPreferencesFingerprint : MethodFingerprint(
returnType = "Lcom/google/android/apps/photos/backup/data/BackupPreferences;",
strings = listOf(
"backup_prefs_had_backup_only_when_charging_enabled",
),
)

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.googlerecorder.restrictions
import app.revanced.util.exception
import app.revanced.extensions.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction

Some files were not shown because too many files have changed in this diff Show More