Merge branch 'dev' into feat/music-settings

# Conflicts:
#	src/main/kotlin/app/revanced/patches/youtube/misc/debugging/DebuggingPatch.kt
#	src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsResourcePatch.kt
#	src/main/resources/addresources/values/strings.xml
This commit is contained in:
oSumAtrIX 2024-03-04 16:25:06 +01:00
commit 02c6b7bdf6
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
151 changed files with 2216 additions and 1642 deletions

3
.editorconfig Normal file
View File

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

View File

@ -0,0 +1,25 @@
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

@ -6,10 +6,6 @@ on:
branches: branches:
- main - main
- dev - dev
pull_request:
branches:
- main
- dev
jobs: jobs:
release: release:
@ -41,6 +37,13 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: npm install 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: ${{ env.GPG_FINGERPRINT }}
- name: Release - name: Release
env: env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }} GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}

View File

@ -33,7 +33,7 @@
{ {
"assets": [ "assets": [
{ {
"path": "build/libs/*.jar" "path": "build/libs/revanced-patches*"
}, },
{ {
"path": "patches.json" "path": "patches.json"

View File

@ -1,3 +1,136 @@
# [4.4.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.3.0...v4.4.0-dev.1) (2024-03-04)
### Features
* **YouTube Vanced:** Remove `Hide ads` patch ([87887e4](https://github.com/ReVanced/revanced-patches/commit/87887e4163dd9e242209f4d0fefb415f9bc7ca75))
# [4.3.0](https://github.com/ReVanced/revanced-patches/compare/v4.2.0...v4.3.0) (2024-03-02)
### Bug Fixes
* Compile DEX without debugging information ([f5df957](https://github.com/ReVanced/revanced-patches/commit/f5df9578669f71a67411bc93a25a7e8da43610d0))
* **Override certificate pinning:** Always overwrite with a generic network security configuration ([2a842a1](https://github.com/ReVanced/revanced-patches/commit/2a842a1e14e1993eb028ae0bd1a93e227bb929a6))
* Remove extra space from patch description ([#2780](https://github.com/ReVanced/revanced-patches/issues/2780)) ([96a3f35](https://github.com/ReVanced/revanced-patches/commit/96a3f359266ff8d16ae9ee3c6ce2f16ce67a3b93))
* Use deprecated members to ensure backwards compatibility ([083bd40](https://github.com/ReVanced/revanced-patches/commit/083bd4009231b9612394b4496ca1d329947d6577))
* **YouTube - Spoof app version:** Remove broken versions ([#2776](https://github.com/ReVanced/revanced-patches/issues/2776)) ([9466d97](https://github.com/ReVanced/revanced-patches/commit/9466d973c6d7a2891e3fa9f283107b64399152ea))
* **YouTube - Spoof signature:** Fix tracking such as history or watch time ([bcd8b48](https://github.com/ReVanced/revanced-patches/commit/bcd8b48e70693dac1bfcc0bf4971d6b526065b59))
### Features
* **OpeningHours:** Add `Fix crash` patch ([#2697](https://github.com/ReVanced/revanced-patches/issues/2697)) ([0d011b8](https://github.com/ReVanced/revanced-patches/commit/0d011b876ecf05031a7daa54ab7e6d3506728a47))
* Remove unnecessary description from patch ([1a89dd9](https://github.com/ReVanced/revanced-patches/commit/1a89dd9f8cd0c614055a9da97338839b77a25ed1))
* **Sync for Reddit:** Add `Fix /s/ links` patch ([f15ef3f](https://github.com/ReVanced/revanced-patches/commit/f15ef3f63460254236185f8e22c9395db4db9465))
* **Twitter - Unlock downloads:** Unlock GIF downloads ([d0f91c8](https://github.com/ReVanced/revanced-patches/commit/d0f91c8550592723e1252e1af2971b508591dd59))
* **VSCO - Unlock pro:** Constrain to last working version ([6dd4a7c](https://github.com/ReVanced/revanced-patches/commit/6dd4a7c29e48c3bc517bbdd7ed160624c36c2333))
* **X:** Add `Open links as query` patch ([#2730](https://github.com/ReVanced/revanced-patches/issues/2730)) ([ba75a51](https://github.com/ReVanced/revanced-patches/commit/ba75a51b71dbb9157db230b3e97a90361019fe30))
* **YouTube - Change header:** Improve patch option description ([3b8bc08](https://github.com/ReVanced/revanced-patches/commit/3b8bc08d4ed3a3a0f96d2f476e5059840b9f9d9b))
* **YouTube - Change start page:** Add more start pages ([cc1d9b7](https://github.com/ReVanced/revanced-patches/commit/cc1d9b743633c619fb6acc428e884c1c9b53e10b))
* **YouTube - Custom branding:** Improve patch option description ([e27f56c](https://github.com/ReVanced/revanced-patches/commit/e27f56c8a34d41167b290f47280276c1c6003876))
* **YouTube - Spoof app version:** Add target versions ([#2787](https://github.com/ReVanced/revanced-patches/issues/2787)) ([83a7bd8](https://github.com/ReVanced/revanced-patches/commit/83a7bd8d69e62623fc4d2ba73d9fb49e92751d89))
* **YouTube:** Reorganize settings menu ([#2737](https://github.com/ReVanced/revanced-patches/issues/2737)) ([36132df](https://github.com/ReVanced/revanced-patches/commit/36132df4be6a04c08b6f3dd79de1bcea93a80fb8))
# [4.3.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.12...v4.3.0-dev.13) (2024-03-02)
### Features
* **YouTube:** Reorganize settings menu ([#2737](https://github.com/ReVanced/revanced-patches/issues/2737)) ([36132df](https://github.com/ReVanced/revanced-patches/commit/36132df4be6a04c08b6f3dd79de1bcea93a80fb8))
# [4.3.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.11...v4.3.0-dev.12) (2024-03-02)
### Features
* **YouTube - Spoof app version:** Add target versions ([#2787](https://github.com/ReVanced/revanced-patches/issues/2787)) ([83a7bd8](https://github.com/ReVanced/revanced-patches/commit/83a7bd8d69e62623fc4d2ba73d9fb49e92751d89))
# [4.3.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.10...v4.3.0-dev.11) (2024-03-02)
### Bug Fixes
* **YouTube - Spoof signature:** Fix tracking such as history or watch time ([bcd8b48](https://github.com/ReVanced/revanced-patches/commit/bcd8b48e70693dac1bfcc0bf4971d6b526065b59))
# [4.3.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.9...v4.3.0-dev.10) (2024-02-29)
### Bug Fixes
* **YouTube - Spoof app version:** Remove broken versions ([#2776](https://github.com/ReVanced/revanced-patches/issues/2776)) ([9466d97](https://github.com/ReVanced/revanced-patches/commit/9466d973c6d7a2891e3fa9f283107b64399152ea))
# [4.3.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.8...v4.3.0-dev.9) (2024-02-28)
### Bug Fixes
* **Override certificate pinning:** Always overwrite with a generic network security configuration ([2a842a1](https://github.com/ReVanced/revanced-patches/commit/2a842a1e14e1993eb028ae0bd1a93e227bb929a6))
# [4.3.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.7...v4.3.0-dev.8) (2024-02-28)
### Bug Fixes
* Remove extra space from patch description ([#2780](https://github.com/ReVanced/revanced-patches/issues/2780)) ([96a3f35](https://github.com/ReVanced/revanced-patches/commit/96a3f359266ff8d16ae9ee3c6ce2f16ce67a3b93))
# [4.3.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.6...v4.3.0-dev.7) (2024-02-26)
### Features
* **OpeningHours:** Add `Fix crash` patch ([#2697](https://github.com/ReVanced/revanced-patches/issues/2697)) ([0d011b8](https://github.com/ReVanced/revanced-patches/commit/0d011b876ecf05031a7daa54ab7e6d3506728a47))
# [4.3.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.5...v4.3.0-dev.6) (2024-02-25)
### Features
* **VSCO - Unlock pro:** Constrain to last working version ([6dd4a7c](https://github.com/ReVanced/revanced-patches/commit/6dd4a7c29e48c3bc517bbdd7ed160624c36c2333))
# [4.3.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.4...v4.3.0-dev.5) (2024-02-25)
### Features
* Remove unnecessary description from patch ([1a89dd9](https://github.com/ReVanced/revanced-patches/commit/1a89dd9f8cd0c614055a9da97338839b77a25ed1))
* **Twitter - Unlock downloads:** Unlock GIF downloads ([d0f91c8](https://github.com/ReVanced/revanced-patches/commit/d0f91c8550592723e1252e1af2971b508591dd59))
# [4.3.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.3...v4.3.0-dev.4) (2024-02-22)
### Bug Fixes
* Compile DEX without debugging information ([f5df957](https://github.com/ReVanced/revanced-patches/commit/f5df9578669f71a67411bc93a25a7e8da43610d0))
* Use deprecated members to ensure backwards compatibility ([083bd40](https://github.com/ReVanced/revanced-patches/commit/083bd4009231b9612394b4496ca1d329947d6577))
### Features
* **X:** Add `Open links as query` patch ([#2730](https://github.com/ReVanced/revanced-patches/issues/2730)) ([ba75a51](https://github.com/ReVanced/revanced-patches/commit/ba75a51b71dbb9157db230b3e97a90361019fe30))
# [4.3.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.2...v4.3.0-dev.3) (2024-02-20)
### Features
* **YouTube - Change header:** Improve patch option description ([3b8bc08](https://github.com/ReVanced/revanced-patches/commit/3b8bc08d4ed3a3a0f96d2f476e5059840b9f9d9b))
* **YouTube - Custom branding:** Improve patch option description ([e27f56c](https://github.com/ReVanced/revanced-patches/commit/e27f56c8a34d41167b290f47280276c1c6003876))
# [4.3.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.3.0-dev.1...v4.3.0-dev.2) (2024-02-09)
### Features
* **Sync for Reddit:** Add `Fix /s/ links` patch ([f15ef3f](https://github.com/ReVanced/revanced-patches/commit/f15ef3f63460254236185f8e22c9395db4db9465))
# [4.3.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.2.0...v4.3.0-dev.1) (2024-02-09)
### Features
* **YouTube - Change start page:** Add more start pages ([cc1d9b7](https://github.com/ReVanced/revanced-patches/commit/cc1d9b743633c619fb6acc428e884c1c9b53e10b))
# [4.2.0](https://github.com/ReVanced/revanced-patches/compare/v4.1.0...v4.2.0) (2024-02-08) # [4.2.0](https://github.com/ReVanced/revanced-patches/compare/v4.1.0...v4.2.0) (2024-02-08)

View File

@ -64,15 +64,15 @@ This document describes how to contribute to ReVanced Patches.
## 📖 Resources to help you get started ## 📖 Resources to help you get started
* The [documentation](https://github.com/ReVanced/revanced-patches/tree/docs/docs) provides the fundamentals of patches * The [documentation](https://github.com/ReVanced/revanced-patcher/tree/docs/docs) contains the fundamentals
and everything necessary to create your own patch from scratch of ReVanced Patcher and how to use ReVanced Patcher to create patches
* [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on * [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 * [Issues](https://github.com/ReVanced/revanced-patches/issues) are where we keep track of bugs and feature requests
## 🙏 Submitting a feature request ## 🙏 Submitting a feature request
Features can be requested by opening an issue using the 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** > **Note**
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Patches. > Requests can be accepted or rejected at the discretion of maintainers of ReVanced Patches.
@ -81,7 +81,7 @@ Features can be requested by opening an issue using the
## 🐞 Submitting a bug report ## 🐞 Submitting a bug report
If you encounter a bug while using ReVanced Patches, open an issue using the 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+). [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 ## 🧑‍⚖️ Guidelines for requesting or contributing patches
@ -110,7 +110,7 @@ are unaffected by this change.
## 📝 How to contribute ## 📝 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 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 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` 2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`

View File

@ -67,7 +67,7 @@ This repository contains a collection of ReVanced Patches.
## ❓ About ## ❓ About
Patches are small modifications to Android apps that allow you to change the behaviour of or add new features, Patches are small modifications to Android apps that allow you to change the behavior of or add new features,
block ads, customize the appearance, and much more. block ads, customize the appearance, and much more.
## 💪 Features ## 💪 Features
@ -77,11 +77,11 @@ Some of the features the patches provide are:
* 🚫 **Block ads**: Say goodbye to ads * 🚫 **Block ads**: Say goodbye to ads
* ⭐ **Customize your app**: Personalize the appearance of apps with various layouts and themes * ⭐ **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 * 🪄 **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, * ⚙️ **Miscellaneous and general purpose**: Rename packages, enable debugging, disable screen capture restrictions,
export activities, etc. export activities, etc.
* ✨ **And much more!** * ✨ **And much more!**
For a full list of all available patches, visit [revanced.app/patches](https://revanced.app/patches). For a complete list of all available patches, visit [revanced.app/patches](https://revanced.app/patches).
## 🚀 How to get started ## 🚀 How to get started
@ -93,17 +93,13 @@ 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). 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 ### 🛠️ Building
In order to build ReVanced Patches, you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation). To build ReVanced Patches, you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
## 📜 Licence ## 📜 Licence
ReVanced Patches is licensed under the GPLv3 licence. Please see the [licence file](LICENSE) for more information. 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. [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. Any modifications to ReVanced Patches must also be made available under the GPL,
along with build & install instructions.

View File

@ -1,3 +1,7 @@
public final class app/revanced/generator/MainKt {
public static synthetic fun main ([Ljava/lang/String;)V
}
public final class app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch : app/revanced/patcher/patch/ResourcePatch { public final class app/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch; public static final field INSTANCE Lapp/revanced/patches/all/activity/exportall/ExportAllActivitiesPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
@ -430,6 +434,12 @@ public final class app/revanced/patches/nyx/misc/pro/UnlockProPatch : app/revanc
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
} }
public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch; public static final field INSTANCE Lapp/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -574,6 +584,12 @@ public final class app/revanced/patches/reddit/customclients/syncforreddit/detec
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
} }
public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch; public static final field INSTANCE Lapp/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -738,10 +754,8 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP
} }
public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;)V public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V
public final fun getCategories ()Ljava/util/Set; public final fun getCategories ()Ljava/util/Set;
public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
@ -813,12 +827,22 @@ public class app/revanced/patches/shared/misc/settings/preference/PreferenceCate
} }
public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreference { public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreen : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getPreferences ()Ljava/util/Set; public final fun getPreferences ()Ljava/util/Set;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
} }
public final class app/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting : java/lang/Enum {
public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public final fun getKeySuffix ()Ljava/lang/String;
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
public static fun values ()[Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen$Sorting;
}
public final class app/revanced/patches/shared/misc/settings/preference/SummaryType : java/lang/Enum { public final class app/revanced/patches/shared/misc/settings/preference/SummaryType : java/lang/Enum {
public static final field DEFAULT Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; public static final field DEFAULT Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
public static final field OFF Lapp/revanced/patches/shared/misc/settings/preference/SummaryType; public static final field OFF Lapp/revanced/patches/shared/misc/settings/preference/SummaryType;
@ -1135,6 +1159,12 @@ public final class app/revanced/patches/twitter/misc/hook/patch/recommendation/H
public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/patch/recommendation/HideRecommendedUsersPatch; public static final field INSTANCE Lapp/revanced/patches/twitter/misc/hook/patch/recommendation/HideRecommendedUsersPatch;
} }
public final class app/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/twitter/misc/links/OpenLinksWithAppChooserPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/vsco/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/vsco/misc/pro/UnlockProPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/vsco/misc/pro/UnlockProPatch; public static final field INSTANCE Lapp/revanced/patches/vsco/misc/pro/UnlockProPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
@ -1477,7 +1507,6 @@ public final class app/revanced/patches/youtube/layout/tabletminiplayer/TabletMi
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch; public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public final fun unwrap (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lkotlin/Triple;
} }
public final class app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
@ -1626,9 +1655,14 @@ public final class app/revanced/patches/youtube/misc/settings/SettingsPatch$Pref
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/SettingsPatch$PreferenceScreen; public static final field INSTANCE Lapp/revanced/patches/youtube/misc/settings/SettingsPatch$PreferenceScreen;
public fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen;)V public fun commit (Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreen;)V
public final fun getADS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; public final fun getADS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getINTERACTIONS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; public final fun getALTERNATIVE_THUMBNAILS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getLAYOUT ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; public final fun getFEED ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getGENERAL_LAYOUT ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getMISC ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; public final fun getMISC ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getPLAYER ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getSEEKBAR ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getSHORTS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getSWIPE_CONTROLS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getVIDEO ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen; public final fun getVIDEO ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
} }

View File

@ -1,9 +1,10 @@
import org.gradle.kotlin.dsl.support.listFilesOrdered import org.gradle.kotlin.dsl.support.listFilesOrdered
plugins { plugins {
kotlin("jvm") version "1.9.22" alias(libs.plugins.kotlin)
alias(libs.plugins.binary.compatibility.validator) alias(libs.plugins.binary.compatibility.validator)
`maven-publish` `maven-publish`
signing
} }
group = "app.revanced" group = "app.revanced"
@ -12,7 +13,14 @@ repositories {
mavenCentral() mavenCentral()
mavenLocal() mavenLocal()
google() google()
maven { url = uri("https://jitpack.io") } maven {
// A repository must be speficied 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")
}
}
} }
dependencies { dependencies {
@ -22,34 +30,32 @@ dependencies {
implementation(libs.guava) implementation(libs.guava)
// Used in JsonGenerator. // Used in JsonGenerator.
implementation(libs.gson) implementation(libs.gson)
// A dependency to the Android library unfortunately fails the build, which is why this is required.
compileOnly(project("dummy"))
} }
kotlin { kotlin {
jvmToolchain(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"
}
}
tasks { tasks {
register<DefaultTask>("generateBundle") { withType(Jar::class) {
description = "Generate DEX files and add them in the JAR file" 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"
dependsOn(build) dependsOn(build)
@ -57,39 +63,50 @@ tasks {
val d8 = File(System.getenv("ANDROID_HOME")).resolve("build-tools") val d8 = File(System.getenv("ANDROID_HOME")).resolve("build-tools")
.listFilesOrdered().last().resolve("d8").absolutePath .listFilesOrdered().last().resolve("d8").absolutePath
val artifacts = configurations.archives.get().allArtifacts.files.files.first().absolutePath val patchesJar = configurations.archives.get().allArtifacts.files.files.first().absolutePath
val workingDirectory = layout.buildDirectory.dir("libs").get().asFile val workingDirectory = layout.buildDirectory.dir("libs").get().asFile
exec { exec {
workingDir = workingDirectory workingDir = workingDirectory
commandLine = listOf(d8, artifacts) commandLine = listOf(d8, "--release", patchesJar)
} }
exec { exec {
workingDir = workingDirectory workingDir = workingDirectory
commandLine = listOf("zip", "-u", artifacts, "classes.dex") commandLine = listOf("zip", "-u", patchesJar, "classes.dex")
} }
} }
} }
register<JavaExec>("generateMeta") { register<JavaExec>("generatePatchesFiles") {
description = "Generate metadata for this bundle" description = "Generate patches files"
dependsOn(build) dependsOn(build)
classpath = sourceSets["main"].runtimeClasspath classpath = sourceSets["main"].runtimeClasspath
mainClass.set("app.revanced.meta.IPatchesFileGenerator") mainClass.set("app.revanced.generator.MainKt")
} }
// Required to run tasks because Gradle semantic-release plugin runs the publish task. // Needed by gradle-semantic-release-plugin.
// Tracking: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435 // Tracking: https://github.com/KengoTODA/gradle-semantic-release-plugin/issues/435
named("publish") { publish {
dependsOn("generateBundle") dependsOn("buildDexJar")
dependsOn("generateMeta") dependsOn("generatePatchesFiles")
} }
} }
publishing { 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 { publications {
create<MavenPublication>("revanced-patches-publication") { create<MavenPublication>("revanced-patches-publication") {
from(components["java"]) from(components["java"])
@ -120,4 +137,10 @@ publishing {
} }
} }
} }
} }
signing {
useGpgCmd()
sign(publishing.publications["revanced-patches-publication"])
}

View File

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

View File

@ -1,9 +0,0 @@
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.parallel = true
org.gradle.caching = true org.gradle.caching = true
kotlin.code.style = official kotlin.code.style = official
version = 4.2.0 version = 4.4.0-dev.1

View File

@ -1,9 +1,10 @@
[versions] [versions]
revanced-patcher = "19.2.0" revanced-patcher = "19.3.1"
smali = "3.0.3" smali = "3.0.5"
guava = "33.0.0-jre" guava = "33.0.0-jre"
gson = "2.10.1" gson = "2.10.1"
binary-compatibility-validator = "0.13.2" binary-compatibility-validator = "0.14.0"
kotlin = "1.9.22"
[libraries] [libraries]
revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" } revanced-patcher = { module = "app.revanced:revanced-patcher", version.ref = "revanced-patcher" }
@ -13,3 +14,4 @@ gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
[plugins] [plugins]
binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" } binary-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "binary-compatibility-validator" }
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

View File

@ -1,8 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026 distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dist

280
package-lock.json generated
View File

@ -9,7 +9,7 @@
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.9.1", "gradle-semantic-release-plugin": "^1.9.1",
"semantic-release": "^23.0.0" "semantic-release": "^23.0.2"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@ -326,9 +326,9 @@
} }
}, },
"node_modules/@octokit/request": { "node_modules/@octokit/request": {
"version": "8.1.6", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.6.tgz", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz",
"integrity": "sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==", "integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@octokit/endpoint": "^9.0.0", "@octokit/endpoint": "^9.0.0",
@ -564,6 +564,26 @@
"node": ">= 16" "node": ">= 16"
} }
}, },
"node_modules/@saithodev/semantic-release-backmerge/node_modules/marked-terminal": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz",
"integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==",
"dev": true,
"dependencies": {
"ansi-escapes": "^6.2.0",
"cardinal": "^2.1.1",
"chalk": "^5.3.0",
"cli-table3": "^0.6.3",
"node-emoji": "^2.1.3",
"supports-hyperlinks": "^3.0.0"
},
"engines": {
"node": ">=16.0.0"
},
"peerDependencies": {
"marked": ">=1 <12"
}
},
"node_modules/@saithodev/semantic-release-backmerge/node_modules/mimic-fn": { "node_modules/@saithodev/semantic-release-backmerge/node_modules/mimic-fn": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
@ -1195,9 +1215,9 @@
} }
}, },
"node_modules/@sindresorhus/merge-streams": { "node_modules/@sindresorhus/merge-streams": {
"version": "1.0.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.2.0.tgz",
"integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", "integrity": "sha512-UTce8mUwUW0RikMb/eseJ7ys0BRkZVFB86orHzrfW12ZmFtym5zua8joZ4L7okH2dDFHkcFjqnZ5GocWBXOFtA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
@ -1282,6 +1302,12 @@
"integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==",
"dev": true "dev": true
}, },
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
"dev": true
},
"node_modules/argparse": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@ -1376,6 +1402,81 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/cli-highlight": {
"version": "2.1.11",
"resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz",
"integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==",
"dev": true,
"dependencies": {
"chalk": "^4.0.0",
"highlight.js": "^10.7.1",
"mz": "^2.4.0",
"parse5": "^5.1.1",
"parse5-htmlparser2-tree-adapter": "^6.0.0",
"yargs": "^16.0.0"
},
"bin": {
"highlight": "bin/highlight"
},
"engines": {
"node": ">=8.0.0",
"npm": ">=5.0.0"
}
},
"node_modules/cli-highlight/node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/cli-highlight/node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"node_modules/cli-highlight/node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/cli-highlight/node_modules/yargs-parser": {
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"dev": true,
"engines": {
"node": ">=10"
}
},
"node_modules/cli-table3": { "node_modules/cli-table3": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz",
@ -1818,9 +1919,9 @@
} }
}, },
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.1.1", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@ -1888,9 +1989,9 @@
} }
}, },
"node_modules/fastq": { "node_modules/fastq": {
"version": "1.16.0", "version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
"integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"reusify": "^1.0.4" "reusify": "^1.0.4"
@ -2052,12 +2153,12 @@
} }
}, },
"node_modules/globby": { "node_modules/globby": {
"version": "14.0.0", "version": "14.0.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-14.0.0.tgz", "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.1.tgz",
"integrity": "sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==", "integrity": "sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@sindresorhus/merge-streams": "^1.0.0", "@sindresorhus/merge-streams": "^2.1.0",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"ignore": "^5.2.4", "ignore": "^5.2.4",
"path-type": "^5.0.0", "path-type": "^5.0.0",
@ -2142,9 +2243,9 @@
} }
}, },
"node_modules/hasown": { "node_modules/hasown": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"function-bind": "^1.1.2" "function-bind": "^1.1.2"
@ -2153,6 +2254,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/highlight.js": {
"version": "10.7.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/hook-std": { "node_modules/hook-std": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz",
@ -2178,9 +2288,9 @@
} }
}, },
"node_modules/http-proxy-agent": { "node_modules/http-proxy-agent": {
"version": "7.0.0", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.1.tgz",
"integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "integrity": "sha512-My1KCEPs6A0hb4qCVzYp8iEvA8j8YqcvXLZZH8C9OFuTYpYjHE7N2dtG3mRl1HMD4+VGXpF3XcDVcxGBT7yDZQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"agent-base": "^7.1.0", "agent-base": "^7.1.0",
@ -2191,9 +2301,9 @@
} }
}, },
"node_modules/https-proxy-agent": { "node_modules/https-proxy-agent": {
"version": "7.0.2", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.3.tgz",
"integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "integrity": "sha512-kCnwztfX0KZJSLOBrcL0emLeFako55NWMovvyPP2AjsghNk9RB1yjSI+jVumPHYZsNXegNoqupSW9IY3afSH8w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"agent-base": "^7.0.2", "agent-base": "^7.0.2",
@ -2213,9 +2323,9 @@
} }
}, },
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.0", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
"integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">= 4" "node": ">= 4"
@ -2629,9 +2739,9 @@
} }
}, },
"node_modules/marked": { "node_modules/marked": {
"version": "11.1.1", "version": "12.0.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-11.1.1.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.0.tgz",
"integrity": "sha512-EgxRjgK9axsQuUa/oKMx5DEY8oXpKJfk61rT5iY3aRlgU6QJtUcxU5OAymdhCvWvhYcd9FKmO5eQoX8m9VGJXg==", "integrity": "sha512-Vkwtq9rLqXryZnWaQc86+FHLC6tr/fycMfYAhiOIXkrNmeGAyhSxjqu0Rs1i0bBqw5u0S7+lV9fdH2ZSVaoa0w==",
"dev": true, "dev": true,
"bin": { "bin": {
"marked": "bin/marked.js" "marked": "bin/marked.js"
@ -2641,14 +2751,14 @@
} }
}, },
"node_modules/marked-terminal": { "node_modules/marked-terminal": {
"version": "6.2.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-6.2.0.tgz", "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.0.0.tgz",
"integrity": "sha512-ubWhwcBFHnXsjYNsu+Wndpg0zhY4CahSpPlA70PlO0rR9r2sZpkyU+rkCsOWH+KMEkx847UpALON+HWgxowFtw==", "integrity": "sha512-sNEx8nn9Ktcm6pL0TnRz8tnXq/mSS0Q1FRSwJOAqw4lAB4l49UeDf85Gm1n9RPFm5qurCPjwi1StAQT2XExhZw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"ansi-escapes": "^6.2.0", "ansi-escapes": "^6.2.0",
"cardinal": "^2.1.1",
"chalk": "^5.3.0", "chalk": "^5.3.0",
"cli-highlight": "^2.1.11",
"cli-table3": "^0.6.3", "cli-table3": "^0.6.3",
"node-emoji": "^2.1.3", "node-emoji": "^2.1.3",
"supports-hyperlinks": "^3.0.0" "supports-hyperlinks": "^3.0.0"
@ -2657,7 +2767,7 @@
"node": ">=16.0.0" "node": ">=16.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"marked": ">=1 <12" "marked": ">=1 <13"
} }
}, },
"node_modules/meow": { "node_modules/meow": {
@ -2739,6 +2849,17 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true "dev": true
}, },
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dev": true,
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"node_modules/neo-async": { "node_modules/neo-async": {
"version": "2.6.2", "version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
@ -5551,6 +5672,15 @@
"inBundle": true, "inBundle": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -5695,6 +5825,27 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/parse5": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
"dev": true
},
"node_modules/parse5-htmlparser2-tree-adapter": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
"integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
"dev": true,
"dependencies": {
"parse5": "^6.0.1"
}
},
"node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
"dev": true
},
"node_modules/parsimmon": { "node_modules/parsimmon": {
"version": "1.18.1", "version": "1.18.1",
"resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz", "resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz",
@ -5860,9 +6011,9 @@
} }
}, },
"node_modules/read-pkg-up/node_modules/type-fest": { "node_modules/read-pkg-up/node_modules/type-fest": {
"version": "4.10.1", "version": "4.10.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.1.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz",
"integrity": "sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==", "integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -5889,9 +6040,9 @@
} }
}, },
"node_modules/read-pkg/node_modules/type-fest": { "node_modules/read-pkg/node_modules/type-fest": {
"version": "4.10.1", "version": "4.10.2",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.1.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.10.2.tgz",
"integrity": "sha512-7ZnJYTp6uc04uYRISWtiX3DSKB/fxNQT0B5o1OUeCqiQiwF+JC9+rJiZIDrPrNCLLuTqyQmh4VdQqh/ZOkv9MQ==", "integrity": "sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -5994,9 +6145,9 @@
"dev": true "dev": true
}, },
"node_modules/semantic-release": { "node_modules/semantic-release": {
"version": "23.0.0", "version": "23.0.2",
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-23.0.0.tgz", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-23.0.2.tgz",
"integrity": "sha512-Jz7jEWO2igTtske112gC4PPE2whCMVrsgxUPG3/SZI7VE357suIUZFlJd1Yu0g2I6RPc2HxNEfUg7KhmDTjwqg==", "integrity": "sha512-OnVYJ6Xgzwe1x8MKswba7RU9+5djS1MWRTrTn5qsq3xZYpslroZkV9Pt0dA2YcIuieeuSZWJhn+yUWoBUHO5Fw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@semantic-release/commit-analyzer": "^11.0.0", "@semantic-release/commit-analyzer": "^11.0.0",
@ -6017,8 +6168,8 @@
"hosted-git-info": "^7.0.0", "hosted-git-info": "^7.0.0",
"import-from-esm": "^1.3.1", "import-from-esm": "^1.3.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"marked": "^11.0.0", "marked": "^12.0.0",
"marked-terminal": "^6.0.0", "marked-terminal": "^7.0.0",
"micromatch": "^4.0.2", "micromatch": "^4.0.2",
"p-each-series": "^3.0.0", "p-each-series": "^3.0.0",
"p-reduce": "^3.0.0", "p-reduce": "^3.0.0",
@ -6247,9 +6398,9 @@
} }
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.5.4", "version": "7.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"lru-cache": "^6.0.0" "lru-cache": "^6.0.0"
@ -6481,9 +6632,9 @@
} }
}, },
"node_modules/spdx-license-ids": { "node_modules/spdx-license-ids": {
"version": "3.0.16", "version": "3.0.17",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
"integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
"dev": true "dev": true
}, },
"node_modules/split2": { "node_modules/split2": {
@ -6655,6 +6806,27 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dev": true,
"dependencies": {
"any-promise": "^1.0.0"
}
},
"node_modules/thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dev": true,
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/through": { "node_modules/through": {
"version": "2.3.8", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",

View File

@ -4,6 +4,6 @@
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.9.1", "gradle-semantic-release-plugin": "^1.9.1",
"semantic-release": "^23.0.0" "semantic-release": "^23.0.2"
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,3 @@
include("dummy")
rootProject.name = "revanced-patches" rootProject.name = "revanced-patches"
buildCache { buildCache {

View File

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

View File

@ -0,0 +1,12 @@
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

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

View File

@ -1,20 +0,0 @@
package app.revanced.meta
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.PatchSet
import java.io.File
internal interface IPatchesFileGenerator {
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(JsonPatchesFileGenerator()).forEach { generator -> generator.generate(bundle) }
}
}
}

View File

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

View File

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

View File

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

View File

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

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

View File

@ -19,6 +19,7 @@ import java.util.*
* An identifier of an app. For example, `youtube`. * An identifier of an app. For example, `youtube`.
*/ */
private typealias AppId = String private typealias AppId = String
/** /**
* An identifier of a patch. For example, `ad.general.HideAdsPatch`. * An identifier of a patch. For example, `ad.general.HideAdsPatch`.
*/ */
@ -28,10 +29,12 @@ private typealias PatchId = String
* A set of resources of a patch. * A set of resources of a patch.
*/ */
private typealias PatchResources = MutableSet<BaseResource> private typealias PatchResources = MutableSet<BaseResource>
/** /**
* A map of resources belonging to a patch. * A map of resources belonging to a patch.
*/ */
private typealias AppResources = MutableMap<PatchId, PatchResources> private typealias AppResources = MutableMap<PatchId, PatchResources>
/** /**
* A map of resources belonging to an app. * A map of resources belonging to an app.
*/ */
@ -67,40 +70,44 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
this.context = context this.context = context
resources = buildMap { resources =
/** buildMap {
* Puts resources under `/resources/addresources/<value>/<resourceKind>.xml` into the map. /**
* * Puts resources under `/resources/addresources/<value>/<resourceKind>.xml` into the map.
* @param value The value of the resource. For example, `values` or `values-de`. *
* @param resourceKind The kind of the resource. For example, `strings` or `arrays`. * @param value The value of the resource. For example, `values` or `values-de`.
* @param transform A function that transforms the [Node]s from the XML files to a [BaseResource]. * @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( */
value: Value, fun addResources(
resourceKind: String, value: Value,
transform: (Node) -> BaseResource, resourceKind: String,
) { transform: (Node) -> BaseResource,
inputStreamFromBundledResource( ) {
"addresources", inputStreamFromBundledResource(
"$value/$resourceKind.xml" "addresources",
)?.let { stream -> "$value/$resourceKind.xml",
// Add the resources associated with the given value to the map, )?.let { stream ->
// instead of overwriting it. // Add the resources associated with the given value to the map,
// This covers the example case such as adding strings and arrays of the same value. // instead of overwriting it.
getOrPut(value, ::mutableMapOf).apply { // This covers the example case such as adding strings and arrays of the same value.
context.xmlEditor[stream].use { getOrPut(value, ::mutableMapOf).apply {
it.file.getElementsByTagName("app").asSequence().forEach { app -> context.xmlEditor[stream].use { editor ->
val appId = app.attributes.getNamedItem("id").textContent val document = editor.file
getOrPut(appId, ::mutableMapOf).apply { document.getElementsByTagName("app").asSequence().forEach { app ->
app.forEachChildElement { patch -> val appId = app.attributes.getNamedItem("id").textContent
val patchId = patch.attributes.getNamedItem("id").textContent
getOrPut(patchId, ::mutableSetOf).apply { getOrPut(appId, ::mutableMapOf).apply {
patch.forEachChildElement { resourceNode -> app.forEachChildElement { patch ->
val resource = transform(resourceNode) val patchId = patch.attributes.getNamedItem("id").textContent
add(resource) getOrPut(patchId, ::mutableSetOf).apply {
patch.forEachChildElement { resourceNode ->
val resource = transform(resourceNode)
add(resource)
}
} }
} }
} }
@ -109,23 +116,22 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
} }
} }
} }
}
// Stage all resources to a temporary map. // Stage all resources to a temporary map.
// Staged resources consumed by AddResourcesPatch#invoke(PatchClass) // Staged resources consumed by AddResourcesPatch#invoke(PatchClass)
// are later used in AddResourcesPatch#close. // are later used in AddResourcesPatch#close.
try { try {
val addStringResources = { value: Value -> val addStringResources = { value: Value ->
addResources(value, "strings", StringResource::fromNode) addResources(value, "strings", StringResource::fromNode)
}
Locale.getISOLanguages().asSequence().map { "values-$it" }.forEach { addStringResources(it) }
addStringResources("values")
addResources("values", "arrays", ArrayResource::fromNode)
} catch (e: Exception) {
throw PatchException("Failed to read resources", e)
} }
Locale.getISOLanguages().asSequence().map { "values-$it" }.forEach { addStringResources(it) }
addStringResources("values")
addResources("values", "arrays", ArrayResource::fromNode)
} catch (e: Exception) {
throw PatchException("Failed to read resources", e)
} }
}
} }
/** /**
@ -136,8 +142,10 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
* *
* @return True if the resource was added, false if it already existed. * @return True if the resource was added, false if it already existed.
*/ */
operator fun invoke(value: Value, resource: BaseResource) = operator fun invoke(
getOrPut(value, ::mutableSetOf).add(resource) value: Value,
resource: BaseResource,
) = getOrPut(value, ::mutableSetOf).add(resource)
/** /**
* Adds a list of [BaseResource]s to the map using [MutableMap.getOrPut]. * Adds a list of [BaseResource]s to the map using [MutableMap.getOrPut].
@ -147,8 +155,10 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
* *
* @return True if the resources were added, false if they already existed. * @return True if the resources were added, false if they already existed.
*/ */
operator fun invoke(value: Value, resources: Iterable<BaseResource>) = operator fun invoke(
getOrPut(value, ::mutableSetOf).addAll(resources) value: Value,
resources: Iterable<BaseResource>,
) = getOrPut(value, ::mutableSetOf).addAll(resources)
/** /**
* Adds a [StringResource]. * Adds a [StringResource].
@ -177,10 +187,9 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
*/ */
operator fun invoke( operator fun invoke(
name: String, name: String,
items: List<String> items: List<String>,
) = invoke("values", ArrayResource(name, items)) ) = invoke("values", ArrayResource(name, items))
/** /**
* Puts all resources of any [Value] staged in [resources] for the given [PatchClass] to [AddResourcesPatch]. * Puts all resources of any [Value] staged in [resources] for the given [PatchClass] to [AddResourcesPatch].
* *
@ -209,7 +218,7 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
appId to patchId appId to patchId
} }
} },
): Boolean { ): Boolean {
val (appId, patchId) = patch.parseIds() val (appId, patchId) = patch.parseIds()
@ -218,7 +227,7 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
// Stage resources for the given patch to AddResourcesPatch associated with their value. // Stage resources for the given patch to AddResourcesPatch associated with their value.
resources.forEach { (value, resources) -> resources.forEach { (value, resources) ->
resources[appId]?.get(patchId)?.let { patchResources -> resources[appId]?.get(patchId)?.let { patchResources ->
if (invoke(value, patchResources)) result = true if (invoke(value, patchResources)) result = true
} }
} }
@ -232,28 +241,32 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
override fun close() { override fun close() {
operator fun MutableMap<String, Pair<DomFileEditor, Node>>.invoke( operator fun MutableMap<String, Pair<DomFileEditor, Node>>.invoke(
value: Value, value: Value,
resource: BaseResource resource: BaseResource,
) { ) {
// TODO: Fix open-closed principle violation by modifying BaseResource#serialize so that it accepts // TODO: Fix open-closed principle violation by modifying BaseResource#serialize so that it accepts
// a Value and the map of editors. It will then get or put the editor suitable for its resource type // 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. // to serialize itself to it.
val resourceFileName = when (resource) { val resourceFileName =
is StringResource -> "strings" when (resource) {
is ArrayResource -> "arrays" is StringResource -> "strings"
else -> throw NotImplementedError("Unsupported resource type") is ArrayResource -> "arrays"
} else -> throw NotImplementedError("Unsupported resource type")
getOrPut(resourceFileName) {
val targetFile = context["res/$value/$resourceFileName.xml"].also {
it.parentFile?.mkdirs()
it.createNewFile()
} }
getOrPut(resourceFileName) {
val targetFile =
context.get("res/$value/$resourceFileName.xml").also {
it.parentFile?.mkdirs()
it.createNewFile()
}
context.xmlEditor[targetFile.path].let { editor -> context.xmlEditor[targetFile.path].let { editor ->
val document = editor.file
// Save the target node here as well // Save the target node here as well
// in order to avoid having to call editor.getNode("resources") // in order to avoid having to call document.getNode("resources")
// every time addUsingEditors is called but also save the editor so that it can be closed later. // but also save the document so that it can be closed later.
editor to editor.getNode("resources") editor to document.getNode("resources")
} }
}.let { (_, targetNode) -> }.let { (_, targetNode) ->
targetNode.addResource(resource) { invoke(value, it) } targetNode.addResource(resource) { invoke(value, it) }
@ -261,17 +274,17 @@ object AddResourcesPatch : ResourcePatch(), MutableMap<Value, MutableSet<BaseRes
} }
forEach { (value, resources) -> forEach { (value, resources) ->
// A map of editors associated by their kind (e.g. strings, arrays). // A map of document associated by their kind (e.g. strings, arrays).
// Each editor is accompanied by the target node to which resources are added. // Each document is accompanied by the target node to which resources are added.
// A map is used because Map#getOrPut allows opening a new editor for the duration of a resource value. // 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. // 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. // Instead, it is cached once and reused for resources of the same value.
// This map is later accessed to close all editors for the current resource value. // This map is later accessed to close all documents for the current resource value.
val resourceFileEditors = mutableMapOf<String, Pair<DomFileEditor, Node>>() val documents = mutableMapOf<String, Pair<DomFileEditor, Node>>()
resources.forEach { resource -> resourceFileEditors(value, resource) } resources.forEach { resource -> documents(value, resource) }
resourceFileEditors.values.forEach { (editor, _) -> editor.close() } documents.values.forEach { (document, _) -> document.close() }
} }
} }
} }

View File

@ -9,12 +9,12 @@ 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.Instruction
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
abstract class BaseTransformInstructionsPatch<T> : BytecodePatch() { abstract class BaseTransformInstructionsPatch<T> : BytecodePatch(emptySet()) {
abstract fun filterMap( abstract fun filterMap(
classDef: ClassDef, classDef: ClassDef,
method: Method, method: Method,
instruction: Instruction, instruction: Instruction,
instructionIndex: Int instructionIndex: Int,
): T? ): T?
abstract fun transform(mutableMethod: MutableMethod, entry: T) abstract fun transform(mutableMethod: MutableMethod, entry: T)

View File

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

View File

@ -1,51 +0,0 @@
package app.revanced.patches.music.audio.exclusiveaudio.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
internal object ExclusiveAudioFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,
listOf("L", "Z"),
listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQ,
Opcode.CONST_4,
Opcode.GOTO,
Opcode.NOP,
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IF_EQZ,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
Opcode.IF_EQZ,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.INVOKE_INTERFACE,
Opcode.GOTO,
Opcode.RETURN_VOID
)
)

View File

@ -1,14 +1,12 @@
package app.revanced.patches.music.misc.gms.fingerprints package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
internal object ServiceCheckFingerprint : MethodFingerprint( internal object ServiceCheckFingerprint : MethodFingerprint(
"V", "V",
AccessFlags.PUBLIC or AccessFlags.STATIC, AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"), listOf("L", "I"),
strings = listOf("Google Play Services not available") strings = listOf("Google Play Services not available"),
) )

View File

@ -6,8 +6,5 @@ import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch
@Patch(requiresIntegrations = true) @Patch(requiresIntegrations = true)
object IntegrationsPatch : BaseIntegrationsPatch( object IntegrationsPatch : BaseIntegrationsPatch(
"Lapp/revanced/integrations/utils/ReVancedUtils;", setOf(ApplicationInitFingerprint),
setOf(
ApplicationInitFingerprint,
),
) )

View File

@ -6,23 +6,24 @@ import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import org.w3c.dom.Element import org.w3c.dom.Element
@Patch( @Patch(
name = "Remove broadcasts restriction", name = "Remove broadcasts restriction",
description = "Enables starting/stopping NetGuard via broadcasts.", description = "Enables starting/stopping NetGuard via broadcasts.",
compatiblePackages = [CompatiblePackage("eu.faircode.netguard")], compatiblePackages = [CompatiblePackage("eu.faircode.netguard")],
use = false use = false,
) )
@Suppress("unused") @Suppress("unused")
object RemoveBroadcastsRestrictionPatch : ResourcePatch() { object RemoveBroadcastsRestrictionPatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
context.xmlEditor["AndroidManifest.xml"].use { dom -> context.xmlEditor["AndroidManifest.xml"].use { editor ->
val applicationNode = dom val document = editor.file
.file
.getElementsByTagName("application")
.item(0) as Element
applicationNode.getElementsByTagName("receiver").also { list -> val applicationNode =
document
.getElementsByTagName("application")
.item(0) as Element
applicationNode.getElementsByTagName("receiver").also { list ->
for (i in 0 until list.length) { for (i in 0 until list.length) {
val element = list.item(i) as? Element ?: continue val element = list.item(i) as? Element ?: continue
if (element.getAttribute("android:name") == "eu.faircode.netguard.WidgetAdmin") { if (element.getAttribute("android:name") == "eu.faircode.netguard.WidgetAdmin") {

View File

@ -0,0 +1,115 @@
package app.revanced.patches.openinghours.misc.fix.crash
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.newLabel
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.openinghours.misc.fix.crash.fingerprints.SetPlaceFingerprint
import app.revanced.util.exception
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21t
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 = "Fix crash",
compatiblePackages = [CompatiblePackage("de.simon.openinghours", ["1.0"])],
)
@Suppress("unused")
object FixCrashPatch : BytecodePatch(
setOf(SetPlaceFingerprint),
) {
override fun execute(context: BytecodeContext) {
SetPlaceFingerprint.result?.let {
val indexedInstructions = it.mutableMethod.getInstructions().withIndex().toList()
/**
* This function replaces all `checkNotNull` instructions in the integer interval
* from [startIndex] to [endIndex], both inclusive. In place of the `checkNotNull`
* instruction an if-null check is inserted. If the if-null check yields that
* the value is indeed null, we jump to a newly created label at `endIndex + 1`.
*/
fun avoidNullPointerException(startIndex: Int, endIndex: Int) {
val continueLabel = it.mutableMethod.newLabel(endIndex + 1)
for (index in startIndex..endIndex) {
val instruction = indexedInstructions[index].value
if (!instruction.isCheckNotNullInstruction) {
continue
}
val checkNotNullInstruction = instruction as FiveRegisterInstruction
val originalRegister = checkNotNullInstruction.registerC
it.mutableMethod.replaceInstruction(
index,
BuilderInstruction21t(
Opcode.IF_EQZ,
originalRegister,
continueLabel,
),
)
}
}
val getOpeningHoursIndex = getIndicesOfInvoke(
indexedInstructions,
"Lde/simon/openinghours/models/Place;",
"getOpeningHours",
)
val setWeekDayTextIndex = getIndexOfInvoke(
indexedInstructions,
"Lde/simon/openinghours/views/custom/PlaceCard;",
"setWeekDayText",
)
val startCalculateStatusIndex = getIndexOfInvoke(
indexedInstructions,
"Lde/simon/openinghours/views/custom/PlaceCard;",
"startCalculateStatus",
)
// Replace the Intrinsics;->checkNotNull instructions with a null check
// and jump to our newly created label if it returns true.
// This avoids the NullPointerExceptions.
avoidNullPointerException(getOpeningHoursIndex[1], startCalculateStatusIndex)
avoidNullPointerException(getOpeningHoursIndex[0], setWeekDayTextIndex)
} ?: throw SetPlaceFingerprint.exception
}
private fun isInvokeInstruction(instruction: Instruction, className: String, methodName: String): Boolean {
val methodRef = instruction.getReference<MethodReference>() ?: return false
return methodRef.definingClass == className && methodRef.name == methodName
}
private fun getIndicesOfInvoke(
instructions: List<IndexedValue<Instruction>>,
className: String,
methodName: String,
): List<Int> = instructions.mapNotNull { (index, instruction) ->
if (isInvokeInstruction(instruction, className, methodName)) {
index
} else {
null
}
}
private fun getIndexOfInvoke(
instructions: List<IndexedValue<Instruction>>,
className: String,
methodName: String,
): Int = instructions.first { (_, instruction) ->
isInvokeInstruction(instruction, className, methodName)
}.index
private val Instruction.isCheckNotNullInstruction
get() = isInvokeInstruction(this, "Lkotlin/jvm/internal/Intrinsics;", "checkNotNull")
}

View File

@ -0,0 +1,12 @@
package app.revanced.patches.openinghours.misc.fix.crash.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object SetPlaceFingerprint : MethodFingerprint(
"V",
parameters = listOf("Lde/simon/openinghours/models/Place;"),
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lde/simon/openinghours/views/custom/PlaceCard;" &&
methodDef.name == "setPlace"
},
)

View File

@ -9,8 +9,10 @@ object HideBannerPatch : ResourcePatch() {
private const val RESOURCE_FILE_PATH = "res/layout/merge_listheader_link_detail.xml" private const val RESOURCE_FILE_PATH = "res/layout/merge_listheader_link_detail.xml"
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
context.xmlEditor[RESOURCE_FILE_PATH].use { context.xmlEditor[RESOURCE_FILE_PATH].use { editor ->
it.file.getElementsByTagName("merge").item(0).childNodes.apply { val document = editor.file
document.getElementsByTagName("merge").item(0).childNodes.apply {
val attributes = arrayOf("height", "width") val attributes = arrayOf("height", "width")
for (i in 1 until length) { for (i in 1 until length) {
@ -30,4 +32,3 @@ object HideBannerPatch : ResourcePatch() {
} }
} }
} }

View File

@ -7,7 +7,7 @@ import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.reddit.ad.comments.fingerprints.HideCommentAdsFingerprint import app.revanced.patches.reddit.ad.comments.fingerprints.HideCommentAdsFingerprint
@Patch(description = "Removes ads in the comments.",) @Patch(description = "Removes ads in the comments.")
object HideCommentAdsPatch : BytecodePatch( object HideCommentAdsPatch : BytecodePatch(
setOf(HideCommentAdsFingerprint) setOf(HideCommentAdsFingerprint)
) { ) {

View File

@ -6,18 +6,13 @@ import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.reddit.customclients.syncforreddit.detection.piracy.fingerprints.PiracyDetectionFingerprint import app.revanced.patches.reddit.customclients.syncforreddit.detection.piracy.fingerprints.PiracyDetectionFingerprint
@Patch(description = "Disables detection of modified versions.",) @Patch(description = "Disables detection of modified versions.")
object DisablePiracyDetectionPatch : BytecodePatch(setOf(PiracyDetectionFingerprint)) { object DisablePiracyDetectionPatch : BytecodePatch(setOf(PiracyDetectionFingerprint)) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
// Do not throw an error if the fingerprint is not resolved. // Do not throw an error if the fingerprint is not resolved.
// This is fine because new versions of the target app do not need this patch. // This is fine because new versions of the target app do not need this patch.
PiracyDetectionFingerprint.result?.mutableMethod?.apply { PiracyDetectionFingerprint.result?.mutableMethod?.apply {
addInstruction( addInstruction(0, "return-void")
0,
"""
return-void
"""
)
} }
} }
} }

View File

@ -0,0 +1,32 @@
package app.revanced.patches.reddit.customclients.syncforreddit.fix.slink
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.reddit.customclients.syncforreddit.fix.slink.fingerprints.LinkHelperOpenLinkFingerprint
import app.revanced.util.exception
@Patch(
name = "Fix /s/ links",
description = "Fixes the issue where /s/ links do not work.",
compatiblePackages = [
CompatiblePackage("com.laurencedawson.reddit_sync"),
CompatiblePackage("com.laurencedawson.reddit_sync.pro"),
CompatiblePackage("com.laurencedawson.reddit_sync.dev")
],
requiresIntegrations = true
)
object FixSLinksPatch : BytecodePatch(
setOf(LinkHelperOpenLinkFingerprint)
) {
override fun execute(context: BytecodeContext) =
LinkHelperOpenLinkFingerprint.result?.mutableMethod?.addInstructions(
1,
"""
invoke-static { p3 }, Lapp/revanced/integrations/syncforreddit/FixSLinksPatch;->resolveSLink(Ljava/lang/String;)Ljava/lang/String;
move-result-object p3
"""
) ?: throw LinkHelperOpenLinkFingerprint.exception
}

View File

@ -0,0 +1,7 @@
package app.revanced.patches.reddit.customclients.syncforreddit.fix.slink.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object LinkHelperOpenLinkFingerprint: MethodFingerprint(
strings = listOf("Link title: ")
)

View File

@ -17,25 +17,26 @@ abstract class BaseDebuggingPatch(
// so we do not have to pass it in as a dependency AND it's preference screen at the same time. // so we do not have to pass it in as a dependency AND it's preference screen at the same time.
private val miscPreferenceScreen: BasePreferenceScreen.Screen, private val miscPreferenceScreen: BasePreferenceScreen.Screen,
private val additionalDebugPreferences: Set<BasePreference> = emptySet(), private val additionalDebugPreferences: Set<BasePreference> = emptySet(),
additionalDependencies: Set<PatchClass> = emptySet() additionalDependencies: Set<PatchClass> = emptySet(),
) : ResourcePatch( ) : ResourcePatch(
name = "Enable debugging", name = "Enable debugging",
description = "Adds options for debugging.", description = "Adds options for debugging.",
dependencies = setOf(integrationsPatch, settingsPatch) + AddResourcesPatch::class + additionalDependencies, dependencies = setOf(integrationsPatch, settingsPatch) + AddResourcesPatch::class + additionalDependencies,
compatiblePackages = compatiblePackages compatiblePackages = compatiblePackages,
) { ) {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(BaseDebuggingPatch::class) AddResourcesPatch(BaseDebuggingPatch::class)
miscPreferenceScreen.addPreferences( miscPreferenceScreen.addPreferences(
PreferenceScreen( PreferenceScreen(
"revanced_debug_preference_screen", "revanced_debug_screen",
sorting = PreferenceScreen.Sorting.UNSORTED,
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_debug"), SwitchPreference("revanced_debug"),
SwitchPreference("revanced_debug_stacktrace"), SwitchPreference("revanced_debug_stacktrace"),
SwitchPreference("revanced_debug_toast_on_error") SwitchPreference("revanced_debug_toast_on_error"),
) + additionalDebugPreferences ) + additionalDebugPreferences,
) ),
) )
} }
} }

View File

@ -22,19 +22,21 @@ abstract class BaseGmsCoreSupportResourcePatch(
private val fromPackageName: String, private val fromPackageName: String,
private val toPackageName: String, private val toPackageName: String,
private val spoofedPackageSignature: String, private val spoofedPackageSignature: String,
dependencies: Set<PatchClass> = setOf() dependencies: Set<PatchClass> = setOf(),
) : ResourcePatch(dependencies = setOf(ChangePackageNamePatch::class, AddResourcesPatch::class) + dependencies) { ) : ResourcePatch(dependencies = setOf(ChangePackageNamePatch::class, AddResourcesPatch::class) + dependencies) {
internal val gmsCoreVendorOption = stringPatchOption( internal val gmsCoreVendorOption =
key = "gmsCoreVendor", stringPatchOption(
default = "com.mgoogle", key = "gmsCoreVendor",
values = mapOf( default = "com.mgoogle",
"Vanced" to "com.mgoogle", values =
"ReVanced" to "app.revanced" mapOf(
), "Vanced" to "com.mgoogle",
title = "GmsCore Vendor", "ReVanced" to "app.revanced",
description = "The group id of the GmsCore vendor.", ),
required = true title = "GmsCore Vendor",
) { it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) } description = "The group id of the GmsCore vendor.",
required = true,
) { it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) }
protected val gmsCoreVendor by gmsCoreVendorOption protected val gmsCoreVendor by gmsCoreVendorOption
@ -49,17 +51,22 @@ abstract class BaseGmsCoreSupportResourcePatch(
* Add metadata to manifest to support spoofing the package name and signature of GmsCore. * Add metadata to manifest to support spoofing the package name and signature of GmsCore.
*/ */
private fun ResourceContext.addSpoofingMetadata() { private fun ResourceContext.addSpoofingMetadata() {
fun Node.adoptChild(tagName: String, block: Element.() -> Unit) { fun Node.adoptChild(
tagName: String,
block: Element.() -> Unit,
) {
val child = ownerDocument.createElement(tagName) val child = ownerDocument.createElement(tagName)
child.block() child.block()
appendChild(child) appendChild(child)
} }
xmlEditor["AndroidManifest.xml"].use { xmlEditor["AndroidManifest.xml"].use { editor ->
val applicationNode = it val document = editor.file
.file
.getElementsByTagName("application") val applicationNode =
.item(0) document
.getElementsByTagName("application")
.item(0)
// Spoof package name and signature. // Spoof package name and signature.
applicationNode.adoptChild("meta-data") { applicationNode.adoptChild("meta-data") {
@ -87,27 +94,27 @@ abstract class BaseGmsCoreSupportResourcePatch(
private fun ResourceContext.patchManifest() { private fun ResourceContext.patchManifest() {
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName) val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName)
val manifest = this["AndroidManifest.xml"].readText() val manifest = this.get("AndroidManifest.xml").readText()
this["AndroidManifest.xml"].writeText( this.get("AndroidManifest.xml").writeText(
manifest.replace( manifest.replace(
"package=\"$fromPackageName", "package=\"$fromPackageName",
"package=\"$packageName" "package=\"$packageName",
).replace( ).replace(
"android:authorities=\"$fromPackageName", "android:authorities=\"$fromPackageName",
"android:authorities=\"$packageName" "android:authorities=\"$packageName",
).replace( ).replace(
"$fromPackageName.permission.C2D_MESSAGE", "$fromPackageName.permission.C2D_MESSAGE",
"$packageName.permission.C2D_MESSAGE" "$packageName.permission.C2D_MESSAGE",
).replace( ).replace(
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION", "$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
"$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" "$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
).replace( ).replace(
"com.google.android.c2dm", "com.google.android.c2dm",
"$gmsCoreVendor.android.c2dm" "$gmsCoreVendor.android.c2dm",
).replace( ).replace(
"</queries>", "</queries>",
"<package android:name=\"$gmsCoreVendor.android.gms\"/></queries>" "<package android:name=\"$gmsCoreVendor.android.gms\"/></queries>",
) ),
) )
} }
} }

View File

@ -11,23 +11,25 @@ import com.android.tools.smali.dexlib2.iface.ClassDef
import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.Method
abstract class BaseIntegrationsPatch( abstract class BaseIntegrationsPatch(
private val hooks: Set<IntegrationsFingerprint> private val hooks: Set<IntegrationsFingerprint>,
) : BytecodePatch(hooks) { ) : BytecodePatch(hooks) {
@Deprecated( @Deprecated(
"Use the constructor without the integrationsDescriptor parameter", "Use the constructor without the integrationsDescriptor parameter",
ReplaceWith("AbstractIntegrationsPatch(hooks)") ReplaceWith("BaseIntegrationsPatch(hooks)"),
) )
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
constructor( constructor(
integrationsDescriptor: String, integrationsDescriptor: String,
hooks: Set<IntegrationsFingerprint> hooks: Set<IntegrationsFingerprint>,
) : this(hooks) ) : this(hooks)
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
if (context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR) == null) throw PatchException( if (context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR) == null) {
"Integrations have not been merged yet. This patch can not succeed without merging the integrations." throw PatchException(
) "Integrations have not been merged yet. This patch can not succeed without merging the integrations.",
)
}
hooks.forEach { hook -> hooks.forEach { hook ->
hook.invoke(INTEGRATIONS_CLASS_DESCRIPTOR) hook.invoke(INTEGRATIONS_CLASS_DESCRIPTOR)
@ -47,14 +49,14 @@ abstract class BaseIntegrationsPatch(
opcodes: Iterable<Opcode?>? = null, opcodes: Iterable<Opcode?>? = null,
strings: Iterable<String>? = null, strings: Iterable<String>? = null,
customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null, customFingerprint: ((methodDef: Method, classDef: ClassDef) -> Boolean)? = null,
private val contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {} private val contextRegisterResolver: (Method) -> Int = object : IRegisterResolver {},
) : MethodFingerprint( ) : MethodFingerprint(
returnType, returnType,
accessFlags, accessFlags,
parameters, parameters,
opcodes, opcodes,
strings, strings,
customFingerprint customFingerprint,
) { ) {
fun invoke(integrationsDescriptor: String) { fun invoke(integrationsDescriptor: String) {
result?.mutableMethod?.let { method -> result?.mutableMethod?.let { method ->
@ -63,7 +65,7 @@ abstract class BaseIntegrationsPatch(
method.addInstruction( method.addInstruction(
0, 0,
"sput-object v$contextRegister, " + "sput-object v$contextRegister, " +
"$integrationsDescriptor->context:Landroid/content/Context;" "$integrationsDescriptor->context:Landroid/content/Context;",
) )
} ?: throw PatchException("Could not find hook target fingerprint.") } ?: throw PatchException("Could not find hook target fingerprint.")
} }
@ -76,4 +78,4 @@ abstract class BaseIntegrationsPatch(
private companion object { private companion object {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/shared/Utils;" private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/shared/Utils;"
} }
} }

View File

@ -7,7 +7,6 @@ import java.util.*
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
object ResourceMappingPatch : ResourcePatch() { object ResourceMappingPatch : ResourcePatch() {
internal lateinit var resourceMappings: List<ResourceElement> internal lateinit var resourceMappings: List<ResourceElement>
private set private set
@ -17,7 +16,7 @@ object ResourceMappingPatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
// save the file in memory to concurrently read from // save the file in memory to concurrently read from
val resourceXmlFile = context["res/values/public.xml"].readBytes() val resourceXmlFile = context.get("res/values/public.xml").readBytes()
// create a synchronized list to store the resource mappings // create a synchronized list to store the resource mappings
val mappings = Collections.synchronizedList(mutableListOf<ResourceElement>()) val mappings = Collections.synchronizedList(mutableListOf<ResourceElement>())
@ -25,7 +24,9 @@ object ResourceMappingPatch : ResourcePatch() {
for (threadIndex in 0 until THREAD_COUNT) { for (threadIndex in 0 until THREAD_COUNT) {
threadPoolExecutor.execute thread@{ threadPoolExecutor.execute thread@{
context.xmlEditor[resourceXmlFile.inputStream()].use { editor -> context.xmlEditor[resourceXmlFile.inputStream()].use { editor ->
val resources = editor.file.documentElement.childNodes val document = editor.file
val resources = document.documentElement.childNodes
val resourcesLength = resources.length val resourcesLength = resources.length
val jobSize = resourcesLength / THREAD_COUNT val jobSize = resourcesLength / THREAD_COUNT
@ -59,4 +60,4 @@ object ResourceMappingPatch : ResourcePatch() {
} }
data class ResourceElement(val type: String, val name: String, val id: Long) data class ResourceElement(val type: String, val name: String, val id: Long)
} }

View File

@ -21,16 +21,18 @@ import java.io.Closeable
*/ */
abstract class BaseSettingsResourcePatch( abstract class BaseSettingsResourcePatch(
private val rootPreference: Pair<IntentPreference, String>? = null, private val rootPreference: Pair<IntentPreference, String>? = null,
dependencies: Set<PatchClass> = emptySet() dependencies: Set<PatchClass> = emptySet(),
) : ResourcePatch( ) : ResourcePatch(
dependencies = setOf(AddResourcesPatch::class) + dependencies dependencies = setOf(AddResourcesPatch::class) + dependencies,
), MutableSet<BasePreference> by mutableSetOf(), Closeable { ),
MutableSet<BasePreference> by mutableSetOf(),
Closeable {
private lateinit var context: ResourceContext private lateinit var context: ResourceContext
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
context.copyResources( context.copyResources(
"settings", "settings",
ResourceGroup("xml", "revanced_prefs.xml") ResourceGroup("xml", "revanced_prefs.xml"),
) )
this.context = context this.context = context
@ -39,24 +41,34 @@ abstract class BaseSettingsResourcePatch(
} }
override fun close() { override fun close() {
fun Node.addPreference(preference: BasePreference) { fun Node.addPreference(preference: BasePreference, prepend: Boolean = false) {
preference.serialize(ownerDocument) { resource -> preference.serialize(ownerDocument) { resource ->
// TODO: Currently, resources can only be added to "values", which may not be the correct place. // TODO: Currently, resources can only be added to "values", which may not be the correct place.
// It may be necessary to ask for the desired resourceValue in the future. // It may be necessary to ask for the desired resourceValue in the future.
AddResourcesPatch("values", resource) AddResourcesPatch("values", resource)
}.let(this::appendChild) }.let { preferenceNode ->
if (prepend && firstChild != null) {
insertBefore(preferenceNode, firstChild)
} else {
appendChild(preferenceNode)
}
}
} }
// Add the root preference to an existing fragment if needed. // Add the root preference to an existing fragment if needed.
rootPreference?.let { (intentPreference, fragment) -> rootPreference?.let { (intentPreference, fragment) ->
context.xmlEditor["res/xml/$fragment.xml"].use { context.xmlEditor["res/xml/$fragment.xml"].use { editor ->
it.getNode("PreferenceScreen").addPreference(intentPreference) val document = editor.file
document.getNode("PreferenceScreen").addPreference(intentPreference, true)
} }
} }
// Add all preferences to the ReVanced fragment. // Add all preferences to the ReVanced fragment.
context.xmlEditor["res/xml/revanced_prefs.xml"].use { editor -> context.xmlEditor["res/xml/revanced_prefs.xml"].use { editor ->
val revancedPreferenceScreenNode = editor.getNode("PreferenceScreen") val document = editor.file
val revancedPreferenceScreenNode = document.getNode("PreferenceScreen")
forEach { revancedPreferenceScreenNode.addPreference(it) } forEach { revancedPreferenceScreenNode.addPreference(it) }
} }
} }

View File

@ -1,9 +1,10 @@
package app.revanced.patches.shared.misc.settings.preference package app.revanced.patches.shared.misc.settings.preference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import java.io.Closeable import java.io.Closeable
abstract class BasePreferenceScreen( abstract class BasePreferenceScreen(
private val root: MutableSet<Screen> = mutableSetOf() private val root: MutableSet<Screen> = mutableSetOf(),
) : Closeable { ) : Closeable {
override fun close() { override fun close() {
@ -24,33 +25,27 @@ abstract class BasePreferenceScreen(
titleKey: String = "${key}_title", titleKey: String = "${key}_title",
private val summaryKey: String? = "${key}_summary", private val summaryKey: String? = "${key}_summary",
preferences: MutableSet<BasePreference> = mutableSetOf(), preferences: MutableSet<BasePreference> = mutableSetOf(),
val categories: MutableSet<Category> = mutableSetOf() val categories: MutableSet<Category> = mutableSetOf(),
private val sorting: Sorting = Sorting.BY_TITLE,
) : BasePreferenceCollection(key, titleKey, preferences) { ) : BasePreferenceCollection(key, titleKey, preferences) {
/**
* Initialize using title and summary keys with suffix "_title" and "_summary".
*/
constructor(
key: String? = null,
preferences: MutableSet<BasePreference> = mutableSetOf(),
categories: MutableSet<Category> = mutableSetOf()
) : this(key, key + "_title", key + "_summary", preferences, categories)
override fun transform(): PreferenceScreen { override fun transform(): PreferenceScreen {
return PreferenceScreen( return PreferenceScreen(
key, key,
titleKey, titleKey,
summaryKey, summaryKey,
sorting,
// Screens and preferences are sorted at runtime by integrations code, // Screens and preferences are sorted at runtime by integrations code,
// so they appear in alphabetical order for the localized language in use. // so title sorting uses the localized language in use.
preferences = preferences + categories.map { it.transform() } preferences = preferences + categories.map { it.transform() },
) )
} }
private fun ensureScreenInserted() { private fun ensureScreenInserted() {
// Add to screens if not yet done // Add to screens if not yet done
if (!root.contains(this)) if (!root.contains(this)) {
root.add(this) root.add(this)
}
} }
fun addPreferences(vararg preferences: BasePreference) { fun addPreferences(vararg preferences: BasePreference) {
@ -61,13 +56,13 @@ abstract class BasePreferenceScreen(
open inner class Category( open inner class Category(
key: String? = null, key: String? = null,
titleKey: String = "${key}_title", titleKey: String = "${key}_title",
preferences: MutableSet<BasePreference> = mutableSetOf() preferences: MutableSet<BasePreference> = mutableSetOf(),
) : BasePreferenceCollection(key, titleKey, preferences) { ) : BasePreferenceCollection(key, titleKey, preferences) {
override fun transform(): PreferenceCategory { override fun transform(): PreferenceCategory {
return PreferenceCategory( return PreferenceCategory(
key, key,
titleKey, titleKey,
preferences = preferences preferences = preferences,
) )
} }
@ -75,8 +70,9 @@ abstract class BasePreferenceScreen(
ensureScreenInserted() ensureScreenInserted()
// Add to the categories if not done yet. // Add to the categories if not done yet.
if (!categories.contains(this)) if (!categories.contains(this)) {
categories.add(this) categories.add(this)
}
this.preferences.addAll(preferences) this.preferences.addAll(preferences)
} }
@ -86,8 +82,8 @@ abstract class BasePreferenceScreen(
abstract class BasePreferenceCollection( abstract class BasePreferenceCollection(
val key: String? = null, val key: String? = null,
val titleKey: String = "${key}_title", val titleKey: String = "${key}_title",
val preferences: MutableSet<BasePreference> = mutableSetOf() val preferences: MutableSet<BasePreference> = mutableSetOf(),
) { ) {
abstract fun transform(): BasePreference abstract fun transform(): BasePreference
} }
} }

View File

@ -1,19 +1,16 @@
package app.revanced.patches.shared.misc.settings.preference package app.revanced.patches.shared.misc.settings.preference
import app.revanced.patches.shared.misc.settings.preference.IntentPreference.Intent
import app.revanced.util.resource.BaseResource import app.revanced.util.resource.BaseResource
import org.w3c.dom.Document import org.w3c.dom.Document
/** /**
* A preference that opens an intent. * A preference that opens an intent.
* *
* @param key The preference key. If null, other parameters must be specified. * @param key Optional preference key.
* @param titleKey The preference title key. * @param titleKey The preference title key.
* @param summaryKey The preference summary key. * @param summaryKey The preference summary key.
* @param tag The preference tag. * @param tag The preference tag.
* @param intent The intent to open. * @param intent The intent to open.
*
* @see Intent
*/ */
class IntentPreference( class IntentPreference(
key: String? = null, key: String? = null,
@ -21,7 +18,7 @@ class IntentPreference(
summaryKey: String? = "${key}_summary", summaryKey: String? = "${key}_summary",
tag: String = "Preference", tag: String = "Preference",
val intent: Intent, val intent: Intent,
) : BasePreference(null, titleKey, summaryKey, tag) { ) : BasePreference(key, titleKey, summaryKey, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply { super.serialize(ownerDocument, resourceCallback).apply {

View File

@ -6,10 +6,12 @@ import org.w3c.dom.Document
/** /**
* A non-interactive preference. * A non-interactive preference.
* *
* Typically used to present static text, but also used for custom integration code that responds to taps.
*
* @param key The preference key. * @param key The preference key.
* @param summaryKey The preference summary key. * @param summaryKey The preference summary key.
* @param tag The preference tag. * @param tag The tag or full class name of the preference.
* @param selectable Whether the preference is selectable. * @param selectable If the preference is selectable and responds to tap events.
*/ */
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
class NonInteractivePreference( class NonInteractivePreference(

View File

@ -9,6 +9,8 @@ import org.w3c.dom.Document
* @param key The key of the preference. If null, other parameters must be specified. * @param key The key of the preference. If null, other parameters must be specified.
* @param titleKey The key of the preference title. * @param titleKey The key of the preference title.
* @param summaryKey The key of the preference summary. * @param summaryKey The key of the preference summary.
* @param sorting Sorting to use. If the sorting is not [Sorting.UNSORTED],
* then the key parameter will be modified to include the sort type.
* @param tag The tag or full class name of the preference. * @param tag The tag or full class name of the preference.
* @param preferences The preferences in this screen. * @param preferences The preferences in this screen.
*/ */
@ -17,14 +19,40 @@ open class PreferenceScreen(
key: String? = null, key: String? = null,
titleKey: String = "${key}_title", titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary", summaryKey: String? = "${key}_summary",
sorting: Sorting = Sorting.BY_TITLE,
tag: String = "PreferenceScreen", tag: String = "PreferenceScreen",
val preferences: Set<BasePreference> val preferences: Set<BasePreference>,
) : BasePreference(key, titleKey, summaryKey, tag) { // Alternatively, instead of repurposing the key for sorting,
// an extra bundle parameter can be added to the preferences XML declaration.
// This would require bundling and referencing an additional XML file
// or adding new attributes to the attrs.xml file.
// Since the key value is not currently used by integrations,
// for now it's much simpler to modify the key to include the sort parameter.
) : BasePreference(if (sorting == Sorting.UNSORTED) key else (key + sorting.keySuffix), titleKey, summaryKey, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply { super.serialize(ownerDocument, resourceCallback).apply {
preferences.forEach { preferences.forEach {
appendChild(it.serialize(ownerDocument, resourceCallback)) appendChild(it.serialize(ownerDocument, resourceCallback))
} }
} }
/**
* How a PreferenceScreen should be sorted.
*/
enum class Sorting(val keySuffix: String) {
/**
* Sort by the localized preference title.
*/
BY_TITLE("_sort_by_title"),
/**
* Sort by the preference keys.
*/
BY_KEY("_sort_by_key"),
/**
* Unspecified sorting.
*/
UNSORTED("_sort_by_unsorted"),
}
} }

View File

@ -10,7 +10,7 @@ import org.w3c.dom.Element
@Patch( @Patch(
name = "Custom theme", name = "Custom theme",
description = "Applies a custom theme.", description = "Applies a custom theme.",
compatiblePackages = [CompatiblePackage("com.spotify.music")] compatiblePackages = [CompatiblePackage("com.spotify.music")],
) )
@Suppress("unused") @Suppress("unused")
object CustomThemePatch : ResourcePatch() { object CustomThemePatch : ResourcePatch() {
@ -19,7 +19,7 @@ object CustomThemePatch : ResourcePatch() {
default = "@android:color/black", default = "@android:color/black",
title = "Primary background color", title = "Primary background color",
description = "The background color. Can be a hex color or a resource reference.", description = "The background color. Can be a hex color or a resource reference.",
required = true required = true,
) )
private var backgroundColorSecondary by stringPatchOption( private var backgroundColorSecondary by stringPatchOption(
@ -27,7 +27,7 @@ object CustomThemePatch : ResourcePatch() {
default = "#ff282828", default = "#ff282828",
title = "Secondary background color", title = "Secondary background color",
description = "The secondary background color. (e.g. search box, artist & podcast). Can be a hex color or a resource reference.", description = "The secondary background color. (e.g. search box, artist & podcast). Can be a hex color or a resource reference.",
required = true required = true,
) )
private var accentColor by stringPatchOption( private var accentColor by stringPatchOption(
@ -35,16 +35,17 @@ object CustomThemePatch : ResourcePatch() {
default = "#ff1ed760", default = "#ff1ed760",
title = "Accent color", title = "Accent color",
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.", description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
required = true required = true,
) )
private var accentColorPressed by stringPatchOption( private var accentColorPressed by stringPatchOption(
key = "accentColorPressed", key = "accentColorPressed",
default = "#ff169c46", default = "#ff169c46",
title = "Pressed dark theme accent color", title = "Pressed dark theme accent color",
description = "The color when accented buttons are pressed, by default slightly darker than accent. " description =
+ "Can be a hex color or a resource reference.", "The color when accented buttons are pressed, by default slightly darker than accent. " +
required = true "Can be a hex color or a resource reference.",
required = true,
) )
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
@ -54,23 +55,27 @@ object CustomThemePatch : ResourcePatch() {
val accentColorPressed = accentColorPressed!! val accentColorPressed = accentColorPressed!!
context.xmlEditor["res/values/colors.xml"].use { editor -> context.xmlEditor["res/values/colors.xml"].use { editor ->
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element val document = editor.file
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
for (i in 0 until resourcesNode.childNodes.length) { for (i in 0 until resourcesNode.childNodes.length) {
val node = resourcesNode.childNodes.item(i) as? Element ?: continue val node = resourcesNode.childNodes.item(i) as? Element ?: continue
node.textContent = when (node.getAttribute("name")) { node.textContent =
"dark_base_background_elevated_base", "design_dark_default_color_background", when (node.getAttribute("name")) {
"design_dark_default_color_surface", "gray_7", "gray_background", "gray_layer", "dark_base_background_elevated_base", "design_dark_default_color_background",
"sthlm_blk" -> backgroundColor "design_dark_default_color_surface", "gray_7", "gray_background", "gray_layer",
"sthlm_blk",
-> backgroundColor
"gray_15" -> backgroundColorSecondary "gray_15" -> backgroundColorSecondary
"dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light" -> accentColor "dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light" -> accentColor
"dark_brightaccent_background_press" -> accentColorPressed "dark_brightaccent_background_press" -> accentColorPressed
else -> continue else -> continue
} }
} }
} }
} }

View File

@ -23,19 +23,19 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
dependencies = [IntegrationsPatch::class, SettingsPatch::class], dependencies = [IntegrationsPatch::class, SettingsPatch::class],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage("com.ss.android.ugc.trill"), CompatiblePackage("com.ss.android.ugc.trill"),
CompatiblePackage("com.zhiliaoapp.musically") CompatiblePackage("com.zhiliaoapp.musically"),
], ],
use = false use = false,
) )
@Suppress("unused") @Suppress("unused")
object SpoofSimPatch : BytecodePatch() { object SpoofSimPatch : BytecodePatch(emptySet()) {
private val replacements = hashMapOf( private val replacements = hashMapOf(
"getSimCountryIso" to "getCountryIso", "getSimCountryIso" to "getCountryIso",
"getNetworkCountryIso" to "getCountryIso", "getNetworkCountryIso" to "getCountryIso",
"getSimOperator" to "getOperator", "getSimOperator" to "getOperator",
"getNetworkOperator" to "getOperator", "getNetworkOperator" to "getOperator",
"getSimOperatorName" to "getOperatorName", "getSimOperatorName" to "getOperatorName",
"getNetworkOperatorName" to "getOperatorName" "getNetworkOperatorName" to "getOperatorName",
) )
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
@ -85,7 +85,7 @@ object SpoofSimPatch : BytecodePatch() {
with(SettingsStatusLoadFingerprint.result!!.mutableMethod) { with(SettingsStatusLoadFingerprint.result!!.mutableMethod) {
addInstruction( addInstruction(
0, 0,
"invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableSimSpoof()V" "invoke-static {}, Lapp/revanced/integrations/tiktok/settings/SettingsStatus;->enableSimSpoof()V",
) )
} }
} }
@ -99,7 +99,7 @@ object SpoofSimPatch : BytecodePatch() {
""" """
invoke-static {v$resultReg}, Lapp/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String; invoke-static {v$resultReg}, Lapp/revanced/integrations/tiktok/spoof/sim/SpoofSimPatch;->$replacement(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$resultReg move-result-object v$resultReg
""" """,
) )
} }
} }

View File

@ -10,11 +10,11 @@ import app.revanced.patches.tumblr.timelinefilter.TimelineFilterPatch
name = "Disable dashboard ads", name = "Disable dashboard ads",
description = "Disables ads in the dashboard.", description = "Disables ads in the dashboard.",
compatiblePackages = [CompatiblePackage("com.tumblr")], compatiblePackages = [CompatiblePackage("com.tumblr")],
dependencies = [TimelineFilterPatch::class] dependencies = [TimelineFilterPatch::class],
) )
@Suppress("unused") @Suppress("unused")
object DisableDashboardAds : BytecodePatch() { object DisableDashboardAds : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
// The timeline object types are filtered by their name in the TimelineObjectType enum. // The timeline object types are filtered by their name in the TimelineObjectType enum.
// This is often different from the "object_type" returned in the api (noted in comments here) // This is often different from the "object_type" returned in the api (noted in comments here)
arrayOf( arrayOf(
@ -29,9 +29,9 @@ object DisableDashboardAds : BytecodePatch() {
"DISPLAY_IO_INTERSCROLLER_AD", // "display_io_interscroller" "DISPLAY_IO_INTERSCROLLER_AD", // "display_io_interscroller"
"DISPLAY_IO_HEADLINE_VIDEO_AD", // "display_io_headline_video" "DISPLAY_IO_HEADLINE_VIDEO_AD", // "display_io_headline_video"
"FACEBOOK_BIDDAABLE", // "facebook_biddable_sdk_ad" "FACEBOOK_BIDDAABLE", // "facebook_biddable_sdk_ad"
"GOOGLE_NATIVE" // "google_native_ad" "GOOGLE_NATIVE", // "google_native_ad"
).forEach { ).forEach {
TimelineFilterPatch.addObjectTypeFilter(it) TimelineFilterPatch.addObjectTypeFilter(it)
} }
} }
} }

View File

@ -10,13 +10,13 @@ import app.revanced.patches.tumblr.featureflags.OverrideFeatureFlagsPatch
name = "Disable in-app update", name = "Disable in-app update",
description = "Disables the in-app update check and update prompt.", description = "Disables the in-app update check and update prompt.",
dependencies = [OverrideFeatureFlagsPatch::class], dependencies = [OverrideFeatureFlagsPatch::class],
compatiblePackages = [CompatiblePackage("com.tumblr")] compatiblePackages = [CompatiblePackage("com.tumblr")],
) )
@Suppress("unused") @Suppress("unused")
object DisableInAppUpdatePatch : BytecodePatch() { object DisableInAppUpdatePatch : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
// Before checking for updates using Google Play core AppUpdateManager, the value of this feature flag is checked. // Before checking for updates using Google Play core AppUpdateManager, the value of this feature flag is checked.
// If this flag is false or the last update check was today and no update check is performed. // If this flag is false or the last update check was today and no update check is performed.
OverrideFeatureFlagsPatch.addOverride("inAppUpdate", "false") OverrideFeatureFlagsPatch.addOverride("inAppUpdate", "false")
} }
} }

View File

@ -11,10 +11,10 @@ import app.revanced.patches.tumblr.timelinefilter.TimelineFilterPatch
name = "Disable Tumblr Live", name = "Disable Tumblr Live",
description = "Disable the Tumblr Live tab button and dashboard carousel.", description = "Disable the Tumblr Live tab button and dashboard carousel.",
dependencies = [OverrideFeatureFlagsPatch::class, TimelineFilterPatch::class], dependencies = [OverrideFeatureFlagsPatch::class, TimelineFilterPatch::class],
compatiblePackages = [CompatiblePackage("com.tumblr")] compatiblePackages = [CompatiblePackage("com.tumblr")],
) )
@Suppress("unused") @Suppress("unused")
object DisableTumblrLivePatch : BytecodePatch() { object DisableTumblrLivePatch : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
// Hide the LIVE_MARQUEE timeline element that appears in the feed // Hide the LIVE_MARQUEE timeline element that appears in the feed
// Called "live_marquee" in api response // Called "live_marquee" in api response
@ -23,4 +23,4 @@ object DisableTumblrLivePatch : BytecodePatch() {
// Hide the Tab button for Tumblr Live by forcing the feature flag to false // Hide the Tab button for Tumblr Live by forcing the feature flag to false
OverrideFeatureFlagsPatch.addOverride("liveStreaming", "false") OverrideFeatureFlagsPatch.addOverride("liveStreaming", "false")
} }
} }

View File

@ -26,7 +26,9 @@ object AudioAdsPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.ADS.CLIENT_SIDE.addPreferences(SwitchPreference("revanced_block_audio_ads")) SettingsPatch.PreferenceScreen.ADS.CLIENT_SIDE.addPreferences(
SwitchPreference("revanced_block_audio_ads")
)
// Block playAds call // Block playAds call
with(AudioAdsPresenterPlayFingerprint.result!!) { with(AudioAdsPresenterPlayFingerprint.result!!) {

View File

@ -35,7 +35,9 @@ object VideoAdsPatch : BaseAdPatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.ADS.CLIENT_SIDE.addPreferences(SwitchPreference("revanced_block_video_ads")) SettingsPatch.PreferenceScreen.ADS.CLIENT_SIDE.addPreferences(
SwitchPreference("revanced_block_video_ads")
)
/* Amazon ads SDK */ /* Amazon ads SDK */
context.blockMethods( context.blockMethods(

View File

@ -32,7 +32,9 @@ object DebugModePatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.MISC.OTHER.addPreferences(SwitchPreference("revanced_twitch_debug_mode")) SettingsPatch.PreferenceScreen.MISC.OTHER.addPreferences(
SwitchPreference("revanced_twitch_debug_mode")
)
listOf( listOf(
IsDebugConfigEnabledFingerprint, IsDebugConfigEnabledFingerprint,

View File

@ -25,7 +25,6 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.immutable.ImmutableField import com.android.tools.smali.dexlib2.immutable.ImmutableField
import java.io.Closeable import java.io.Closeable
@Patch( @Patch(
name = "Settings", name = "Settings",
description = "Adds settings menu to Twitch.", description = "Adds settings menu to Twitch.",
@ -62,7 +61,9 @@ object SettingsPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
PreferenceScreen.MISC.OTHER.addPreferences(SwitchPreference("revanced_debug")) PreferenceScreen.MISC.OTHER.addPreferences(
SwitchPreference("revanced_debug")
)
// Hook onCreate to handle fragment creation // Hook onCreate to handle fragment creation
SettingsActivityOnCreateFingerprint.result?.apply { SettingsActivityOnCreateFingerprint.result?.apply {

View File

@ -2,27 +2,36 @@ package app.revanced.patches.twitter.interaction.downloads
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.fingerprint.MethodFingerprintResult import app.revanced.patcher.fingerprint.MethodFingerprintResult
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.twitter.interaction.downloads.fingerprints.BuildMediaOptionsSheetFingerprint
import app.revanced.patches.twitter.interaction.downloads.fingerprints.ConstructMediaOptionsSheetFingerprint import app.revanced.patches.twitter.interaction.downloads.fingerprints.ConstructMediaOptionsSheetFingerprint
import app.revanced.patches.twitter.interaction.downloads.fingerprints.ShowDownloadVideoUpsellBottomSheetFingerprint import app.revanced.patches.twitter.interaction.downloads.fingerprints.ShowDownloadVideoUpsellBottomSheetFingerprint
import app.revanced.util.exception import app.revanced.util.exception
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@Patch( @Patch(
name = "Unlock downloads", name = "Unlock downloads",
description = "Unlocks the ability to download any video.", description = "Unlocks the ability to download any video. GIFs can be downloaded via the menu on long press.",
compatiblePackages = [CompatiblePackage("com.twitter.android")] compatiblePackages = [CompatiblePackage("com.twitter.android")],
) )
@Suppress("unused") @Suppress("unused")
object UnlockDownloadsPatch : BytecodePatch( object UnlockDownloadsPatch : BytecodePatch(
setOf(ConstructMediaOptionsSheetFingerprint, ShowDownloadVideoUpsellBottomSheetFingerprint) setOf(
ConstructMediaOptionsSheetFingerprint,
ShowDownloadVideoUpsellBottomSheetFingerprint,
BuildMediaOptionsSheetFingerprint,
),
) { ) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
fun MethodFingerprint.patch(getRegisterAndIndex: MethodFingerprintResult.() -> Pair<Int, Int>) = result?.let { fun MethodFingerprint.patch(getRegisterAndIndex: MethodFingerprintResult.() -> Pair<Int, Int>) = result?.let {
@ -46,5 +55,29 @@ object UnlockDownloadsPatch : BytecodePatch(
showDownloadButtonIndex to register showDownloadButtonIndex to register
} }
// Make GIFs downloadable.
BuildMediaOptionsSheetFingerprint.result?.let {
val scanResult = it.scanResult.patternScanResult!!
it.mutableMethod.apply {
val checkMediaTypeIndex = scanResult.startIndex
val checkMediaTypeInstruction = getInstruction<TwoRegisterInstruction>(checkMediaTypeIndex)
// Treat GIFs as videos.
addInstructionsWithLabels(
checkMediaTypeIndex + 1,
"""
const/4 v${checkMediaTypeInstruction.registerB}, 0x2 # GIF
if-eq v${checkMediaTypeInstruction.registerA}, v${checkMediaTypeInstruction.registerB}, :video
""",
ExternalLabel("video", getInstruction(scanResult.endIndex)),
)
// Remove media.isDownloadable check.
removeInstruction(
getInstructions().first { insn -> insn.opcode == Opcode.IGET_BOOLEAN }.location.index + 1,
)
}
} ?: throw BuildMediaOptionsSheetFingerprint.exception
} }
} }

View File

@ -0,0 +1,14 @@
package app.revanced.patches.twitter.interaction.downloads.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object BuildMediaOptionsSheetFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.IF_EQ,
Opcode.SGET_OBJECT,
Opcode.GOTO_16,
Opcode.NEW_INSTANCE,
),
strings = listOf("resources.getString(R.string.post_video)"),
)

View File

@ -11,12 +11,12 @@ import java.nio.file.Files
@Patch( @Patch(
name = "Dynamic color", name = "Dynamic color",
description = "Replaces the default X (Formerly Twitter) Blue with the user's Material You palette.", description = "Replaces the default X (Formerly Twitter) Blue with the user's Material You palette.",
compatiblePackages = [CompatiblePackage("com.twitter.android")] compatiblePackages = [CompatiblePackage("com.twitter.android")],
) )
@Suppress("unused") @Suppress("unused")
object DynamicColorPatch : ResourcePatch() { object DynamicColorPatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
val resDirectory = context["res"] val resDirectory = context.get("res")
if (!resDirectory.isDirectory) throw PatchException("The res folder can not be found.") if (!resDirectory.isDirectory) throw PatchException("The res folder can not be found.")
val valuesV31Directory = resDirectory.resolve("values-v31") val valuesV31Directory = resDirectory.resolve("values-v31")
@ -28,7 +28,7 @@ object DynamicColorPatch : ResourcePatch() {
listOf(valuesV31Directory, valuesNightV31Directory).forEach { it -> listOf(valuesV31Directory, valuesNightV31Directory).forEach { it ->
val colorsXml = it.resolve("colors.xml") val colorsXml = it.resolve("colors.xml")
if(!colorsXml.exists()) { if (!colorsXml.exists()) {
FileWriter(colorsXml).use { FileWriter(colorsXml).use {
it.write("<?xml version=\"1.0\" encoding=\"utf-8\"?><resources></resources>") it.write("<?xml version=\"1.0\" encoding=\"utf-8\"?><resources></resources>")
} }
@ -46,7 +46,7 @@ object DynamicColorPatch : ResourcePatch() {
"twitter_blue_opacity_30" to "@android:color/system_accent1_100", "twitter_blue_opacity_30" to "@android:color/system_accent1_100",
"twitter_blue_opacity_50" to "@android:color/system_accent1_200", "twitter_blue_opacity_50" to "@android:color/system_accent1_200",
"twitter_blue_opacity_58" to "@android:color/system_accent1_300", "twitter_blue_opacity_58" to "@android:color/system_accent1_300",
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200" "deep_transparent_twitter_blue" to "@android:color/system_accent1_200",
).forEach { (k, v) -> ).forEach { (k, v) ->
val colorElement = document.createElement("color") val colorElement = document.createElement("color")
@ -66,7 +66,7 @@ object DynamicColorPatch : ResourcePatch() {
"twitter_blue_opacity_30" to "@android:color/system_accent1_50", "twitter_blue_opacity_30" to "@android:color/system_accent1_50",
"twitter_blue_opacity_50" to "@android:color/system_accent1_100", "twitter_blue_opacity_50" to "@android:color/system_accent1_100",
"twitter_blue_opacity_58" to "@android:color/system_accent1_200", "twitter_blue_opacity_58" to "@android:color/system_accent1_200",
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200" "deep_transparent_twitter_blue" to "@android:color/system_accent1_200",
).forEach { (k, v) -> ).forEach { (k, v) ->
val colorElement = document.createElement("color") val colorElement = document.createElement("color")

View File

@ -4,7 +4,7 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patches.twitter.misc.hook.json.JsonHookPatch import app.revanced.patches.twitter.misc.hook.json.JsonHookPatch
abstract class BaseHookPatch(private val hookClassDescriptor: String) : BytecodePatch() { abstract class BaseHookPatch(private val hookClassDescriptor: String) : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) = override fun execute(context: BytecodeContext) =
JsonHookPatch.hooks.addHook(JsonHookPatch.Hook(context, hookClassDescriptor)) JsonHookPatch.hooks.addHook(JsonHookPatch.Hook(context, hookClassDescriptor))
} }

View File

@ -7,9 +7,8 @@ import app.revanced.patches.twitter.misc.hook.patch.BaseHookPatch
@Patch( @Patch(
name = "Hide ads", name = "Hide ads",
description = "Hides ads.",
dependencies = [JsonHookPatch::class], dependencies = [JsonHookPatch::class],
compatiblePackages = [CompatiblePackage("com.twitter.android")] compatiblePackages = [CompatiblePackage("com.twitter.android")],
) )
@Suppress("unused") @Suppress("unused")
object HideAdsHookPatch : BaseHookPatch("Lapp/revanced/integrations/twitter/patches/hook/patch/ads/AdsHook;") object HideAdsHookPatch : BaseHookPatch("Lapp/revanced/integrations/twitter/patches/hook/patch/ads/AdsHook;")

View File

@ -0,0 +1,35 @@
package app.revanced.patches.twitter.misc.links
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.twitter.misc.links.fingerprints.OpenLinkFingerprint
import app.revanced.util.exception
@Patch(
name = "Open links with app chooser",
description = "Instead of opening links directly, open them with an app chooser. " +
"As a result you can select a browser to open the link with.",
compatiblePackages = [CompatiblePackage("com.twitter.android")],
use = false,
)
@Suppress("unused")
object OpenLinksWithAppChooserPatch : BytecodePatch(
setOf(OpenLinkFingerprint),
) {
private const val METHOD_REFERENCE =
"Lapp/revanced/integrations/twitter/patches/links/OpenLinksWithAppChooserPatch;->" +
"openWithChooser(Landroid/content/Context;Landroid/content/Intent;)V"
override fun execute(context: BytecodeContext) {
OpenLinkFingerprint.result?.mutableMethod?.addInstructions(
0,
"""
invoke-static { p0, p1 }, $METHOD_REFERENCE
return-void
""",
) ?: throw OpenLinkFingerprint.exception
}
}

View File

@ -0,0 +1,8 @@
package app.revanced.patches.twitter.misc.links.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object OpenLinkFingerprint : MethodFingerprint(
returnType = "V",
parameters = listOf("Landroid/content/Context;", "Landroid/content/Intent;", "Landroid/os/Bundle;"),
)

View File

@ -1,20 +1,20 @@
package app.revanced.patches.vsco.misc.pro package app.revanced.patches.vsco.misc.pro
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.vsco.misc.pro.fingerprints.RevCatSubscriptionFingerprint import app.revanced.patches.vsco.misc.pro.fingerprints.RevCatSubscriptionFingerprint
import app.revanced.util.exception
@Patch( @Patch(
name = "Unlock pro", name = "Unlock pro",
description = "Unlocks pro features.", description = "Unlocks pro features.",
compatiblePackages = [CompatiblePackage("com.vsco.cam")] compatiblePackages = [CompatiblePackage("com.vsco.cam", ["345"])],
) )
object UnlockProPatch : BytecodePatch( object UnlockProPatch : BytecodePatch(
setOf(RevCatSubscriptionFingerprint) setOf(RevCatSubscriptionFingerprint),
) { ) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
RevCatSubscriptionFingerprint.result?.mutableMethod?.apply { RevCatSubscriptionFingerprint.result?.mutableMethod?.apply {
@ -23,7 +23,7 @@ object UnlockProPatch : BytecodePatch(
0, 0,
""" """
const p1, 0x1 const p1, 0x1
""" """,
) )
} ?: throw RevCatSubscriptionFingerprint.exception } ?: throw RevCatSubscriptionFingerprint.exception
} }

View File

@ -1,7 +1,5 @@
package app.revanced.patches.youtube.ad.general package app.revanced.patches.youtube.ad.general
import app.revanced.util.findMutableMethodOf
import app.revanced.util.injectHideViewCall
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.CompatiblePackage
@ -9,6 +7,8 @@ import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.shared.misc.fix.verticalscroll.VerticalScrollPatch import app.revanced.patches.shared.misc.fix.verticalscroll.VerticalScrollPatch
import app.revanced.patches.youtube.ad.getpremium.HideGetPremiumPatch import app.revanced.patches.youtube.ad.getpremium.HideGetPremiumPatch
import app.revanced.patches.youtube.misc.fix.backtoexitgesture.FixBackToExitGesturePatch import app.revanced.patches.youtube.misc.fix.backtoexitgesture.FixBackToExitGesturePatch
import app.revanced.util.findMutableMethodOf
import app.revanced.util.injectHideViewCall
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
@ -20,11 +20,12 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
HideGetPremiumPatch::class, HideGetPremiumPatch::class,
HideAdsResourcePatch::class, HideAdsResourcePatch::class,
VerticalScrollPatch::class, VerticalScrollPatch::class,
FixBackToExitGesturePatch::class FixBackToExitGesturePatch::class,
], ],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage( CompatiblePackage(
"com.google.android.youtube", [ "com.google.android.youtube",
[
"18.32.39", "18.32.39",
"18.37.36", "18.37.36",
"18.38.44", "18.38.44",
@ -37,30 +38,33 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
"19.02.39", "19.02.39",
"19.03.35", "19.03.35",
"19.03.36", "19.03.36",
"19.04.37" "19.04.37",
] ],
) ),
] ],
) )
@Suppress("unused") @Suppress("unused")
object HideAdsPatch : BytecodePatch() { object HideAdsPatch : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
context.classes.forEach { classDef -> context.classes.forEach { classDef ->
classDef.methods.forEach { method -> classDef.methods.forEach { method ->
with(method.implementation) { with(method.implementation) {
this?.instructions?.forEachIndexed { index, instruction -> this?.instructions?.forEachIndexed { index, instruction ->
if (instruction.opcode != Opcode.CONST) if (instruction.opcode != Opcode.CONST) {
return@forEachIndexed return@forEachIndexed
}
// Instruction to store the id adAttribution into a register // Instruction to store the id adAttribution into a register
if ((instruction as Instruction31i).wideLiteral != HideAdsResourcePatch.adAttributionId) if ((instruction as Instruction31i).wideLiteral != HideAdsResourcePatch.adAttributionId) {
return@forEachIndexed return@forEachIndexed
}
val insertIndex = index + 1 val insertIndex = index + 1
// Call to get the view with the id adAttribution // Call to get the view with the id adAttribution
with(instructions.elementAt(insertIndex)) { with(instructions.elementAt(insertIndex)) {
if (opcode != Opcode.INVOKE_VIRTUAL) if (opcode != Opcode.INVOKE_VIRTUAL) {
return@forEachIndexed return@forEachIndexed
}
// Hide the view // Hide the view
val viewRegister = (this as Instruction35c).registerC val viewRegister = (this as Instruction35c).registerC
@ -71,7 +75,7 @@ object HideAdsPatch : BytecodePatch() {
insertIndex, insertIndex,
viewRegister, viewRegister,
"Lapp/revanced/integrations/youtube/patches/components/AdsFilter;", "Lapp/revanced/integrations/youtube/patches/components/AdsFilter;",
"hideAdAttributionView" "hideAdAttributionView",
) )
} }
} }

View File

@ -8,7 +8,6 @@ import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch.PreferenceScreen
@Patch( @Patch(
dependencies = [ dependencies = [
@ -27,7 +26,7 @@ object HideAdsResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
PreferenceScreen.ADS.addPreferences( SettingsPatch.PreferenceScreen.ADS.addPreferences(
SwitchPreference("revanced_hide_general_ads"), SwitchPreference("revanced_hide_general_ads"),
SwitchPreference("revanced_hide_fullscreen_ads"), SwitchPreference("revanced_hide_fullscreen_ads"),
SwitchPreference("revanced_hide_buttoned_ads"), SwitchPreference("revanced_hide_buttoned_ads"),

View File

@ -44,7 +44,9 @@ object HideGetPremiumPatch : BytecodePatch(setOf(GetPremiumViewFingerprint)) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.ADS.addPreferences(SwitchPreference("revanced_hide_get_premium")) SettingsPatch.PreferenceScreen.ADS.addPreferences(
SwitchPreference("revanced_hide_get_premium")
)
GetPremiumViewFingerprint.result?.let { GetPremiumViewFingerprint.result?.let {
it.mutableMethod.apply { it.mutableMethod.apply {

View File

@ -49,7 +49,9 @@ object VideoAdsPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.ADS.addPreferences(SwitchPreference("revanced_hide_video_ads")) SettingsPatch.PreferenceScreen.ADS.addPreferences(
SwitchPreference("revanced_hide_video_ads")
)
val loadVideoAdsFingerprintMethod = LoadVideoAdsFingerprint.result!!.mutableMethod val loadVideoAdsFingerprintMethod = LoadVideoAdsFingerprint.result!!.mutableMethod

View File

@ -13,28 +13,29 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
dependencies = [ dependencies = [
CopyVideoUrlResourcePatch::class, CopyVideoUrlResourcePatch::class,
PlayerControlsBytecodePatch::class, PlayerControlsBytecodePatch::class,
VideoInformationPatch::class VideoInformationPatch::class,
], ],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage( CompatiblePackage(
"com.google.android.youtube", [ "com.google.android.youtube",
[
"18.48.39", "18.48.39",
"18.49.37", "18.49.37",
"19.01.34", "19.01.34",
"19.02.39", "19.02.39",
"19.03.35", "19.03.35",
"19.03.36", "19.03.36",
"19.04.37" "19.04.37",
] ],
) ),
] ],
) )
@Suppress("unused") @Suppress("unused")
object CopyVideoUrlBytecodePatch : BytecodePatch() { object CopyVideoUrlBytecodePatch : BytecodePatch(emptySet()) {
private const val INTEGRATIONS_PLAYER_PACKAGE = "Lapp/revanced/integrations/youtube/videoplayer" private const val INTEGRATIONS_PLAYER_PACKAGE = "Lapp/revanced/integrations/youtube/videoplayer"
private val BUTTONS_DESCRIPTORS = listOf( private val BUTTONS_DESCRIPTORS = listOf(
"$INTEGRATIONS_PLAYER_PACKAGE/CopyVideoUrlButton;", "$INTEGRATIONS_PLAYER_PACKAGE/CopyVideoUrlButton;",
"$INTEGRATIONS_PLAYER_PACKAGE/CopyVideoUrlTimestampButton;" "$INTEGRATIONS_PLAYER_PACKAGE/CopyVideoUrlTimestampButton;",
) )
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
@ -44,4 +45,4 @@ object CopyVideoUrlBytecodePatch : BytecodePatch() {
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$descriptor->changeVisibility(Z)V") PlayerControlsBytecodePatch.injectVisibilityCheckCall("$descriptor->changeVisibility(Z)V")
} }
} }
} }

View File

@ -4,7 +4,6 @@ import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
@ -22,14 +21,9 @@ internal object CopyVideoUrlResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.INTERACTIONS.addPreferences( SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen( SwitchPreference("revanced_copy_video_url"),
"revanced_copy_video_url_preference_screen", SwitchPreference("revanced_copy_video_url_timestamp")
preferences = setOf(
SwitchPreference("revanced_copy_video_url"),
SwitchPreference("revanced_copy_video_url_timestamp")
)
)
) )
context.copyResources( context.copyResources(
@ -40,8 +34,6 @@ internal object CopyVideoUrlResourcePatch : ResourcePatch() {
) )
) )
AddResourcesPatch(this::class)
BottomControlsResourcePatch.addControls("copyvideourl") BottomControlsResourcePatch.addControls("copyvideourl")
} }
} }

View File

@ -50,7 +50,9 @@ object RemoveViewerDiscretionDialogPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.INTERACTIONS.addPreferences(SwitchPreference("revanced_remove_viewer_discretion_dialog")) SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_remove_viewer_discretion_dialog")
)
CreateDialogFingerprint.result?.mutableMethod?.apply { CreateDialogFingerprint.result?.mutableMethod?.apply {
val showDialogIndex = implementation!!.instructions.lastIndex - 2 val showDialogIndex = implementation!!.instructions.lastIndex - 2

View File

@ -13,24 +13,25 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
dependencies = [ dependencies = [
ExternalDownloadsResourcePatch::class, ExternalDownloadsResourcePatch::class,
PlayerControlsBytecodePatch::class, PlayerControlsBytecodePatch::class,
VideoInformationPatch::class VideoInformationPatch::class,
], ],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage( CompatiblePackage(
"com.google.android.youtube", [ "com.google.android.youtube",
[
"18.48.39", "18.48.39",
"18.49.37", "18.49.37",
"19.01.34", "19.01.34",
"19.02.39", "19.02.39",
"19.03.35", "19.03.35",
"19.03.36", "19.03.36",
"19.04.37" "19.04.37",
] ],
), ),
] ],
) )
@Suppress("unused") @Suppress("unused")
object ExternalDownloadsBytecodePatch : BytecodePatch() { object ExternalDownloadsBytecodePatch : BytecodePatch(emptySet()) {
private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;" private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;"
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
@ -39,13 +40,15 @@ object ExternalDownloadsBytecodePatch : BytecodePatch() {
*/ */
PlayerControlsBytecodePatch.initializeControl( PlayerControlsBytecodePatch.initializeControl(
"$BUTTON_DESCRIPTOR->initializeButton(Landroid/view/View;)V") "$BUTTON_DESCRIPTOR->initializeButton(Landroid/view/View;)V",
)
/* /*
add code to change the visibility of the control add code to change the visibility of the control
*/ */
PlayerControlsBytecodePatch.injectVisibilityCheckCall( PlayerControlsBytecodePatch.injectVisibilityCheckCall(
"$BUTTON_DESCRIPTOR->changeVisibility(Z)V") "$BUTTON_DESCRIPTOR->changeVisibility(Z)V",
)
} }
} }

View File

@ -6,6 +6,7 @@ import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.InputType import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
@ -18,25 +19,26 @@ import app.revanced.util.copyResources
BottomControlsResourcePatch::class, BottomControlsResourcePatch::class,
SettingsPatch::class, SettingsPatch::class,
AddResourcesPatch::class, AddResourcesPatch::class,
] ],
) )
internal object ExternalDownloadsResourcePatch : ResourcePatch() { internal object ExternalDownloadsResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.INTERACTIONS.addPreferences( SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen( PreferenceScreen(
"revanced_external_downloader_preference_screen", key = "revanced_external_downloader_screen",
sorting = Sorting.UNSORTED,
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_external_downloader"), SwitchPreference("revanced_external_downloader"),
TextPreference("revanced_external_downloader_name", inputType = InputType.TEXT) TextPreference("revanced_external_downloader_name", inputType = InputType.TEXT),
), ),
) ),
) )
context.copyResources( context.copyResources(
"downloads", "downloads",
ResourceGroup("drawable", "revanced_yt_download_button.xml") ResourceGroup("drawable", "revanced_yt_download_button.xml"),
) )
BottomControlsResourcePatch.addControls("downloads") BottomControlsResourcePatch.addControls("downloads")

View File

@ -50,7 +50,7 @@ object DisablePreciseSeekingGesturePatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.INTERACTIONS.addPreferences( SettingsPatch.PreferenceScreen.SEEKBAR.addPreferences(
SwitchPreference("revanced_disable_precise_seeking_gesture") SwitchPreference("revanced_disable_precise_seeking_gesture")
) )

View File

@ -51,7 +51,9 @@ object EnableSeekbarTappingPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.INTERACTIONS.addPreferences(SwitchPreference("revanced_seekbar_tapping")) SettingsPatch.PreferenceScreen.SEEKBAR.addPreferences(
SwitchPreference("revanced_seekbar_tapping")
)
// Find the required methods to tap the seekbar. // Find the required methods to tap the seekbar.
val seekbarTappingMethods = OnTouchEventHandlerFingerprint.result?.let { val seekbarTappingMethods = OnTouchEventHandlerFingerprint.result?.let {

View File

@ -17,7 +17,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch( @Patch(
name = "Enable slide to seek", name = "Enable slide to seek",
description = "Adds an option to enable slide to seek instead of playing at 2x speed when pressing and holding in the video player. Including this patch may cause issues with tapping or double tapping the video player overlay.", description = "Adds an option to enable slide to seek instead of playing at 2x speed when pressing and holding in the video player. Including this patch may cause issues with tapping or double tapping the video player overlay.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class], dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage( CompatiblePackage(
@ -50,7 +50,9 @@ object EnableSlideToSeekPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.INTERACTIONS.addPreferences(SwitchPreference("revanced_slide_to_seek")) SettingsPatch.PreferenceScreen.SEEKBAR.addPreferences(
SwitchPreference("revanced_slide_to_seek")
)
arrayOf( arrayOf(
// Restore the behaviour to slide to seek. // Restore the behaviour to slide to seek.

View File

@ -5,7 +5,6 @@ import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.InputType import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch
@ -13,27 +12,22 @@ import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources import app.revanced.util.copyResources
@Patch( @Patch(
dependencies = [SettingsPatch::class, AddResourcesPatch::class] dependencies = [SettingsPatch::class, AddResourcesPatch::class],
) )
internal object SwipeControlsResourcePatch : ResourcePatch() { internal object SwipeControlsResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.INTERACTIONS.addPreferences( SettingsPatch.PreferenceScreen.SWIPE_CONTROLS.addPreferences(
PreferenceScreen( SwitchPreference("revanced_swipe_brightness"),
key = "revanced_swipe_controls_preference_screen", SwitchPreference("revanced_swipe_volume"),
preferences = setOf( SwitchPreference("revanced_swipe_press_to_engage"),
SwitchPreference("revanced_swipe_brightness"), SwitchPreference("revanced_swipe_haptic_feedback"),
SwitchPreference("revanced_swipe_volume"), SwitchPreference("revanced_swipe_save_and_restore_brightness"),
SwitchPreference("revanced_swipe_press_to_engage"), TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
SwitchPreference("revanced_swipe_haptic_feedback"), TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
SwitchPreference("revanced_swipe_save_and_restore_brightness"), TextPreference("revanced_swipe_overlay_background_alpha", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER), TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_background_alpha", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER)
),
)
) )
context.copyResources( context.copyResources(
@ -43,8 +37,8 @@ internal object SwipeControlsResourcePatch : ResourcePatch() {
"revanced_ic_sc_brightness_auto.xml", "revanced_ic_sc_brightness_auto.xml",
"revanced_ic_sc_brightness_manual.xml", "revanced_ic_sc_brightness_manual.xml",
"revanced_ic_sc_volume_mute.xml", "revanced_ic_sc_volume_mute.xml",
"revanced_ic_sc_volume_normal.xml" "revanced_ic_sc_volume_normal.xml",
) ),
) )
} }
} }

View File

@ -48,7 +48,9 @@ object AutoCaptionsPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_auto_captions")) SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_auto_captions")
)
mapOf( mapOf(
StartVideoInformerFingerprint to 0, StartVideoInformerFingerprint to 0,

View File

@ -15,9 +15,9 @@ import java.nio.file.Files
name = "Custom branding", name = "Custom branding",
description = "Applies a custom app name and icon. Defaults to \"YouTube ReVanced\" and the ReVanced logo.", description = "Applies a custom app name and icon. Defaults to \"YouTube ReVanced\" and the ReVanced logo.",
compatiblePackages = [ compatiblePackages = [
CompatiblePackage("com.google.android.youtube") CompatiblePackage("com.google.android.youtube"),
], ],
use = false use = false,
) )
@Suppress("unused") @Suppress("unused")
object CustomBrandingPatch : ResourcePatch() { object CustomBrandingPatch : ResourcePatch() {
@ -28,7 +28,7 @@ object CustomBrandingPatch : ResourcePatch() {
"adaptiveproduct_youtube_background_color_108", "adaptiveproduct_youtube_background_color_108",
"adaptiveproduct_youtube_foreground_color_108", "adaptiveproduct_youtube_foreground_color_108",
"ic_launcher", "ic_launcher",
"ic_launcher_round" "ic_launcher_round",
).map { "$it.png" }.toTypedArray() ).map { "$it.png" }.toTypedArray()
private val mipmapDirectories = arrayOf( private val mipmapDirectories = arrayOf(
@ -36,7 +36,7 @@ object CustomBrandingPatch : ResourcePatch() {
"xxhdpi", "xxhdpi",
"xhdpi", "xhdpi",
"hdpi", "hdpi",
"mdpi" "mdpi",
).map { "mipmap-$it" } ).map { "mipmap-$it" }
private var appName by stringPatchOption( private var appName by stringPatchOption(
@ -49,7 +49,7 @@ object CustomBrandingPatch : ResourcePatch() {
"YouTube" to "YouTube", "YouTube" to "YouTube",
), ),
title = "App name", title = "App name",
description = "The name of the app." description = "The name of the app.",
) )
private var icon by stringPatchOption( private var icon by stringPatchOption(
@ -58,14 +58,16 @@ object CustomBrandingPatch : ResourcePatch() {
values = mapOf("ReVanced Logo" to REVANCED_ICON), values = mapOf("ReVanced Logo" to REVANCED_ICON),
title = "App icon", title = "App icon",
description = """ description = """
The path to a folder containing the following folders: The icon to apply to the app.
If a path to a folder is provided, the folder must contain the following folders:
${mipmapDirectories.joinToString("\n") { "- $it" }} ${mipmapDirectories.joinToString("\n") { "- $it" }}
Each of these folders has to have the following files: Each of these folders must contain the following files:
${iconResourceFileNames.joinToString("\n") { "- $it" }} ${iconResourceFileNames.joinToString("\n") { "- $it" }}
""".trimIndentMultiline() """.trimIndentMultiline(),
) )
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
@ -73,12 +75,13 @@ object CustomBrandingPatch : ResourcePatch() {
// Change the app icon. // Change the app icon.
mipmapDirectories.map { directory -> mipmapDirectories.map { directory ->
ResourceGroup( ResourceGroup(
directory, *iconResourceFileNames directory,
*iconResourceFileNames,
) )
}.let { resourceGroups -> }.let { resourceGroups ->
if (icon != REVANCED_ICON) { if (icon != REVANCED_ICON) {
val path = File(icon) val path = File(icon)
val resourceDirectory = context["res"] val resourceDirectory = context.get("res")
resourceGroups.forEach { group -> resourceGroups.forEach { group ->
val fromDirectory = path.resolve(group.resourceDirectoryName) val fromDirectory = path.resolve(group.resourceDirectoryName)
@ -87,23 +90,25 @@ object CustomBrandingPatch : ResourcePatch() {
group.resources.forEach { iconFileName -> group.resources.forEach { iconFileName ->
Files.write( Files.write(
toDirectory.resolve(iconFileName).toPath(), toDirectory.resolve(iconFileName).toPath(),
fromDirectory.resolve(iconFileName).readBytes() fromDirectory.resolve(iconFileName).readBytes(),
) )
} }
} }
} else resourceGroups.forEach { context.copyResources("custom-branding", it) } } else {
resourceGroups.forEach { context.copyResources("custom-branding", it) }
}
} }
} }
appName?.let { name -> appName?.let { name ->
// Change the app name. // Change the app name.
val manifest = context["AndroidManifest.xml"] val manifest = context.get("AndroidManifest.xml")
manifest.writeText( manifest.writeText(
manifest.readText() manifest.readText()
.replace( .replace(
"android:label=\"@string/application_name", "android:label=\"@string/application_name",
"android:label=\"$name" "android:label=\"$name",
) ),
) )
} }
} }

View File

@ -15,67 +15,74 @@ import java.io.File
name = "Change header", name = "Change header",
description = "Applies a custom header in the top left corner within the app. Defaults to the ReVanced header.", description = "Applies a custom header in the top left corner within the app. Defaults to the ReVanced header.",
compatiblePackages = [ compatiblePackages = [
CompatiblePackage("com.google.android.youtube") CompatiblePackage("com.google.android.youtube"),
], ],
use = false use = false,
) )
@Suppress("unused") @Suppress("unused")
object ChangeHeaderPatch : ResourcePatch() { object ChangeHeaderPatch : ResourcePatch() {
private const val HEADER_NAME = "yt_wordmark_header" private const val HEADER_FILE_NAME = "yt_wordmark_header"
private const val PREMIUM_HEADER_NAME = "yt_premium_wordmark_header" private const val PREMIUM_HEADER_FILE_NAME = "yt_premium_wordmark_header"
private const val REVANCED_HEADER_NAME = "ReVanced"
private const val REVANCED_BORDERLESS_HEADER_NAME = "ReVanced (borderless logo)"
private val targetResourceDirectoryNames = arrayOf( private const val HEADER_OPTION = "header*"
"xxxhdpi", private const val PREMIUM_HEADER_OPTION = "premium*header"
"xxhdpi", private const val REVANCED_HEADER_OPTION = "revanced*"
"xhdpi", private const val REVANCED_BORDERLESS_HEADER_OPTION = "revanced*borderless"
"mdpi",
"hdpi", private val targetResourceDirectoryNames = mapOf(
).map { dpi -> "xxxhdpi" to "512px x 192px",
"drawable-$dpi" "xxhdpi" to "387px x 144px",
} "xhdpi" to "258px x 96px",
"hdpi" to "194px x 72px",
"mdpi" to "129px x 48px",
).map { (dpi, dim) ->
"drawable-$dpi" to dim
}.toMap()
private val variants = arrayOf("light", "dark") private val variants = arrayOf("light", "dark")
private val header by stringPatchOption( private val header by stringPatchOption(
key = "header", key = "header",
default = REVANCED_BORDERLESS_HEADER_NAME, default = REVANCED_BORDERLESS_HEADER_OPTION,
values = mapOf( values = mapOf(
"YouTube" to HEADER_NAME, "YouTube" to HEADER_OPTION,
"YouTube Premium" to PREMIUM_HEADER_NAME, "YouTube Premium" to PREMIUM_HEADER_OPTION,
"ReVanced" to REVANCED_HEADER_NAME, "ReVanced" to REVANCED_HEADER_OPTION,
"ReVanced (borderless logo)" to REVANCED_BORDERLESS_HEADER_NAME, "ReVanced (borderless logo)" to REVANCED_BORDERLESS_HEADER_OPTION,
), ),
title = "Header", title = "Header",
description = """ description = """
Either a header name or a path to a custom header folder to use in the top bar. The header to apply to the app.
The path to a folder must contain one or more of the following folders matching the DPI of your device:
If a path to a folder is provided, the folder must contain one or more of the following folders, depending on the DPI of the device:
${targetResourceDirectoryNames.keys.joinToString("\n") { "- $it" }}
Each of the folders must contain all of the following files:
${variants.joinToString("\n") { variant -> "- ${HEADER_FILE_NAME}_$variant.png" }}
${targetResourceDirectoryNames.joinToString("\n") { "- $it" }} The image dimensions must be as follows:
${targetResourceDirectoryNames.map { (dpi, dim) -> "- $dpi: $dim" }.joinToString("\n")}
These folders must contain the following files:
${variants.joinToString("\n") { variant -> "- ${HEADER_NAME}_$variant.png" }}
""".trimIndentMultiline(), """.trimIndentMultiline(),
required = true, required = true,
) )
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
// The directories to copy the header to. // The directories to copy the header to.
val targetResourceDirectories = targetResourceDirectoryNames.mapNotNull { val targetResourceDirectories = targetResourceDirectoryNames.keys.mapNotNull {
context["res"].resolve(it).takeIf(File::exists) context.get("res").resolve(it).takeIf(File::exists)
} }
// The files to replace in the target directories. // The files to replace in the target directories.
val targetResourceFiles = targetResourceDirectoryNames.map { directoryName -> val targetResourceFiles = targetResourceDirectoryNames.keys.map { directoryName ->
ResourceGroup( ResourceGroup(
directoryName, directoryName,
*variants.map { variant -> "${HEADER_NAME}_$variant.png" }.toTypedArray() *variants.map { variant -> "${HEADER_FILE_NAME}_$variant.png" }.toTypedArray(),
) )
} }
/** /**
* A function that overwrites both header variants from [from] to [to] in the target resource directories. * A function that overwrites both header variants in the target resource directories.
*/ */
val overwriteFromTo: (String, String) -> Unit = { from: String, to: String -> val overwriteFromTo: (String, String) -> Unit = { from: String, to: String ->
targetResourceDirectories.forEach { directory -> targetResourceDirectories.forEach { directory ->
@ -89,8 +96,8 @@ object ChangeHeaderPatch : ResourcePatch() {
} }
// Functions to overwrite the header to the different variants. // Functions to overwrite the header to the different variants.
val toPremium = { overwriteFromTo(PREMIUM_HEADER_NAME, HEADER_NAME) } val toPremium = { overwriteFromTo(PREMIUM_HEADER_FILE_NAME, HEADER_FILE_NAME) }
val toHeader = { overwriteFromTo(HEADER_NAME, PREMIUM_HEADER_NAME) } val toHeader = { overwriteFromTo(HEADER_FILE_NAME, PREMIUM_HEADER_FILE_NAME) }
val toReVanced = { val toReVanced = {
// Copy the ReVanced header to the resource directories. // Copy the ReVanced header to the resource directories.
targetResourceFiles.forEach { context.copyResources("change-header/revanced", it) } targetResourceFiles.forEach { context.copyResources("change-header/revanced", it) }
@ -106,32 +113,38 @@ object ChangeHeaderPatch : ResourcePatch() {
toHeader() toHeader()
} }
val toCustom = { val toCustom = {
var copiedReplacementImages = false val sourceFolders = File(header!!).listFiles { file -> file.isDirectory }
// For all the resource groups in the custom header folder, copy them to the resource directories. ?: throw PatchException("The provided path is not a directory: $header")
File(header!!).listFiles { file -> file.isDirectory }?.forEach { folder ->
val targetDirectory = context["res"].resolve(folder.name)
// Skip if the target directory (DPI) doesn't exist.
if (!targetDirectory.exists()) return@forEach
folder.listFiles { file -> file.isFile }?.forEach { var copiedFiles = false
val targetResourceFile = targetDirectory.resolve(it.name)
it.copyTo(targetResourceFile, true) // For each source folder, copy the files to the target resource directories.
copiedReplacementImages = true sourceFolders.forEach { dpiSourceFolder ->
val targetDpiFolder = context.get("res").resolve(dpiSourceFolder.name)
if (!targetDpiFolder.exists()) return@forEach
val imgSourceFiles = dpiSourceFolder.listFiles { file -> file.isFile }!!
imgSourceFiles.forEach { imgSourceFile ->
val imgTargetFile = targetDpiFolder.resolve(imgSourceFile.name)
imgSourceFile.copyTo(imgTargetFile, true)
copiedFiles = true
} }
} }
if (!copiedReplacementImages) throw PatchException("Could not find any custom images resources in directory: $header") if (!copiedFiles) {
throw PatchException("No header files were copied from the provided path: $header.")
}
// Overwrite the premium with the custom header as well. // Overwrite the premium with the custom header as well.
toHeader() toHeader()
} }
when (header) { when (header) {
HEADER_NAME -> toHeader HEADER_OPTION -> toHeader
PREMIUM_HEADER_NAME -> toPremium PREMIUM_HEADER_OPTION -> toPremium
REVANCED_HEADER_NAME -> toReVanced REVANCED_HEADER_OPTION -> toReVanced
REVANCED_BORDERLESS_HEADER_NAME -> toReVancedBorderless REVANCED_BORDERLESS_HEADER_OPTION -> toReVancedBorderless
else -> toCustom else -> toCustom
}() }()
} }

View File

@ -48,12 +48,11 @@ object HideButtonsPatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen( PreferenceScreen(
"revanced_hide_buttons_preference_screen", "revanced_hide_buttons_screen",
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_hide_like_dislike_button"), SwitchPreference("revanced_hide_like_dislike_button"),
SwitchPreference("revanced_hide_live_chat_button"),
SwitchPreference("revanced_hide_share_button"), SwitchPreference("revanced_hide_share_button"),
SwitchPreference("revanced_hide_report_button"), SwitchPreference("revanced_hide_report_button"),
SwitchPreference("revanced_hide_remix_button"), SwitchPreference("revanced_hide_remix_button"),

View File

@ -57,7 +57,9 @@ object HideAutoplayButtonPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_autoplay_button")) SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_hide_autoplay_button")
)
LayoutConstructorFingerprint.result?.mutableMethod?.apply { LayoutConstructorFingerprint.result?.mutableMethod?.apply {
val layoutGenMethodInstructions = implementation!!.instructions val layoutGenMethodInstructions = implementation!!.instructions

View File

@ -48,7 +48,9 @@ object HideCaptionsButtonPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_captions_button")) SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_hide_captions_button")
)
val subtitleButtonControllerMethod = SubtitleButtonControllerFingerprint.result!!.mutableMethod val subtitleButtonControllerMethod = SubtitleButtonControllerFingerprint.result!!.mutableMethod

View File

@ -17,17 +17,19 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
dependencies = [ dependencies = [
IntegrationsPatch::class, IntegrationsPatch::class,
SettingsPatch::class, SettingsPatch::class,
AddResourcesPatch::class AddResourcesPatch::class,
], ],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage("com.google.android.youtube") CompatiblePackage("com.google.android.youtube"),
] ],
) )
object HideCastButtonPatch : BytecodePatch() { object HideCastButtonPatch : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_cast_button")) SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_hide_cast_button")
)
val buttonClass = context.findClass("MediaRouteButton") val buttonClass = context.findClass("MediaRouteButton")
?: throw PatchException("MediaRouteButton class not found.") ?: throw PatchException("MediaRouteButton class not found.")
@ -38,7 +40,7 @@ object HideCastButtonPatch : BytecodePatch() {
""" """
invoke-static {p1}, Lapp/revanced/integrations/youtube/patches/HideCastButtonPatch;->getCastButtonOverrideV2(I)I invoke-static {p1}, Lapp/revanced/integrations/youtube/patches/HideCastButtonPatch;->getCastButtonOverrideV2(I)I
move-result p1 move-result p1
""" """,
) )
} ?: throw PatchException("setVisibility method not found.") } ?: throw PatchException("setVisibility method not found.")
} }

View File

@ -8,6 +8,7 @@ import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.* import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.*
import app.revanced.patches.youtube.layout.buttons.navigation.utils.InjectionUtils.REGISTER_TEMPLATE_REPLACEMENT import app.revanced.patches.youtube.layout.buttons.navigation.utils.InjectionUtils.REGISTER_TEMPLATE_REPLACEMENT
@ -24,7 +25,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
IntegrationsPatch::class, IntegrationsPatch::class,
SettingsPatch::class, SettingsPatch::class,
ResolvePivotBarFingerprintsPatch::class, ResolvePivotBarFingerprintsPatch::class,
AddResourcesPatch::class AddResourcesPatch::class,
], ],
compatiblePackages = [ compatiblePackages = [
CompatiblePackage( CompatiblePackage(
@ -42,14 +43,14 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"19.02.39", "19.02.39",
"19.03.35", "19.03.35",
"19.03.36", "19.03.36",
"19.04.37" "19.04.37",
] ],
) ),
] ],
) )
@Suppress("unused") @Suppress("unused")
object NavigationButtonsPatch : BytecodePatch( object NavigationButtonsPatch : BytecodePatch(
setOf(AddCreateButtonViewFingerprint) setOf(AddCreateButtonViewFingerprint),
) { ) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/NavigationButtonsPatch;" "Lapp/revanced/integrations/youtube/patches/NavigationButtonsPatch;"
@ -57,17 +58,18 @@ object NavigationButtonsPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
PreferenceScreen( PreferenceScreen(
key = "revanced_navigation_buttons_preference_screen", key = "revanced_navigation_buttons_screen",
sorting = Sorting.UNSORTED,
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_hide_home_button"), SwitchPreference("revanced_hide_home_button"),
SwitchPreference("revanced_hide_shorts_button"), SwitchPreference("revanced_hide_shorts_button"),
SwitchPreference("revanced_hide_subscriptions_button"),
SwitchPreference("revanced_hide_create_button"), SwitchPreference("revanced_hide_create_button"),
SwitchPreference("revanced_hide_subscriptions_button"),
SwitchPreference("revanced_switch_create_with_notifications_button"), SwitchPreference("revanced_switch_create_with_notifications_button"),
), ),
) ),
) )
/* /*
@ -82,14 +84,14 @@ object NavigationButtonsPatch : BytecodePatch(
if (!it.resolve( if (!it.resolve(
context, context,
initializeButtonsResult.mutableMethod, initializeButtonsResult.mutableMethod,
initializeButtonsResult.mutableClass initializeButtonsResult.mutableClass,
) )
) ) {
throw it.exception throw it.exception
}
} }
.map { it.result!!.scanResult.patternScanResult!! } .map { it.result!!.scanResult.patternScanResult!! }
val enumScanResult = fingerprintResults[0] val enumScanResult = fingerprintResults[0]
val buttonViewResult = fingerprintResults[1] val buttonViewResult = fingerprintResults[1]
@ -101,14 +103,14 @@ object NavigationButtonsPatch : BytecodePatch(
*/ */
val enumHook = "sput-object v$REGISTER_TEMPLATE_REPLACEMENT, " + val enumHook = "sput-object v$REGISTER_TEMPLATE_REPLACEMENT, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->lastNavigationButton:Ljava/lang/Enum;" "$INTEGRATIONS_CLASS_DESCRIPTOR->lastNavigationButton:Ljava/lang/Enum;"
val buttonHook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " + val buttonHook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->hideButton(Landroid/view/View;)V" "$INTEGRATIONS_CLASS_DESCRIPTOR->hideButton(Landroid/view/View;)V"
// Inject bottom to top to not mess up the indices // Inject bottom to top to not mess up the indices
mapOf( mapOf(
buttonHook to buttonHookInsertIndex, buttonHook to buttonHookInsertIndex,
enumHook to enumHookInsertIndex enumHook to enumHookInsertIndex,
).forEach { (hook, insertIndex) -> ).forEach { (hook, insertIndex) ->
initializeButtonsResult.mutableMethod.injectHook(hook, insertIndex) initializeButtonsResult.mutableMethod.injectHook(hook, insertIndex)
} }
@ -131,7 +133,7 @@ object NavigationButtonsPatch : BytecodePatch(
""" """
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->switchCreateWithNotificationButton()Z invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->switchCreateWithNotificationButton()Z
move-result v$conditionRegister move-result v$conditionRegister
""" """,
) )
} }
} ?: throw AddCreateButtonViewFingerprint.exception } ?: throw AddCreateButtonViewFingerprint.exception
@ -141,8 +143,9 @@ object NavigationButtonsPatch : BytecodePatch(
*/ */
InitializeButtonsFingerprint.result!!.let { InitializeButtonsFingerprint.result!!.let {
if (!PivotBarCreateButtonViewFingerprint.resolve(context, it.mutableMethod, it.mutableClass)) if (!PivotBarCreateButtonViewFingerprint.resolve(context, it.mutableMethod, it.mutableClass)) {
throw PivotBarCreateButtonViewFingerprint.exception throw PivotBarCreateButtonViewFingerprint.exception
}
} }
PivotBarCreateButtonViewFingerprint.result!!.apply { PivotBarCreateButtonViewFingerprint.result!!.apply {
@ -152,9 +155,9 @@ object NavigationButtonsPatch : BytecodePatch(
* Inject hooks * Inject hooks
*/ */
val hook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " + val hook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->hideCreateButton(Landroid/view/View;)V" "$INTEGRATIONS_CLASS_DESCRIPTOR->hideCreateButton(Landroid/view/View;)V"
mutableMethod.injectHook(hook, insertIndex) mutableMethod.injectHook(hook, insertIndex)
} }
} }
} }

View File

@ -52,7 +52,9 @@ object HidePlayerButtonsPatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_player_buttons")) SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_hide_player_buttons")
)
PlayerControlsVisibilityModelFingerprint.result?.apply { PlayerControlsVisibilityModelFingerprint.result?.apply {
val callIndex = scanResult.patternScanResult!!.endIndex val callIndex = scanResult.patternScanResult!!.endIndex

View File

@ -21,7 +21,9 @@ internal object AlbumCardsResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_album_cards")) SettingsPatch.PreferenceScreen.FEED.addPreferences(
SwitchPreference("revanced_hide_album_cards")
)
albumCardId = ResourceMappingPatch.resourceMappings.single { albumCardId = ResourceMappingPatch.resourceMappings.single {
it.type == "layout" && it.name == "album_card" it.type == "layout" && it.name == "album_card"

View File

@ -21,7 +21,9 @@ internal object BreakingNewsResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_breaking_news")) SettingsPatch.PreferenceScreen.FEED.addPreferences(
SwitchPreference("revanced_hide_breaking_news")
)
horizontalCardListId = ResourceMappingPatch.resourceMappings.single { horizontalCardListId = ResourceMappingPatch.resourceMappings.single {
it.type == "layout" && it.name == "horizontal_card_list" it.type == "layout" && it.name == "horizontal_card_list"

View File

@ -47,9 +47,9 @@ object CommentsPatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen( PreferenceScreen(
"revanced_comments_preference_screen", "revanced_comments_screen",
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_hide_comments_section"), SwitchPreference("revanced_hide_comments_section"),
SwitchPreference("revanced_hide_preview_comment") SwitchPreference("revanced_hide_preview_comment")

View File

@ -21,7 +21,9 @@ internal object CrowdfundingBoxResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_crowdfunding_box")) SettingsPatch.PreferenceScreen.FEED.addPreferences(
SwitchPreference("revanced_hide_crowdfunding_box")
)
crowdfundingBoxId = ResourceMappingPatch.resourceMappings.single { crowdfundingBoxId = ResourceMappingPatch.resourceMappings.single {
it.type == "layout" && it.name == "donation_companion" it.type == "layout" && it.name == "donation_companion"

View File

@ -23,7 +23,9 @@ internal object HideEndscreenCardsResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_endscreen_cards")) SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_hide_endscreen_cards")
)
fun findEndscreenResourceId(name: String) = ResourceMappingPatch.resourceMappings.single { fun findEndscreenResourceId(name: String) = ResourceMappingPatch.resourceMappings.single {
it.type == "layout" && it.name == "endscreen_element_layout_$name" it.type == "layout" && it.name == "endscreen_element_layout_$name"

View File

@ -18,9 +18,9 @@ internal object HideFilterBarResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( SettingsPatch.PreferenceScreen.FEED.addPreferences(
PreferenceScreen( PreferenceScreen(
key = "revanced_hide_filter_bar_preference", key = "revanced_hide_filter_bar_screen",
preferences = setOf( preferences = setOf(
SwitchPreference("revanced_hide_filter_bar_feed_in_feed"), SwitchPreference("revanced_hide_filter_bar_feed_in_feed"),
SwitchPreference("revanced_hide_filter_bar_feed_in_search"), SwitchPreference("revanced_hide_filter_bar_feed_in_search"),

View File

@ -22,7 +22,9 @@ internal object HideFloatingMicrophoneButtonResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) { override fun execute(context: ResourceContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(SwitchPreference("revanced_hide_floating_microphone_button")) SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_hide_floating_microphone_button")
)
fabButtonId = ResourceMappingPatch.resourceMappings.find { it.type == "id" && it.name == "fab" }?.id fabButtonId = ResourceMappingPatch.resourceMappings.find { it.type == "id" && it.name == "fab" }?.id
?: throw PatchException("Can not find required fab button resource id") ?: throw PatchException("Can not find required fab button resource id")

View File

@ -45,7 +45,7 @@ object DisableFullscreenAmbientModePatch : BytecodePatch(
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_disable_fullscreen_ambient_mode") SwitchPreference("revanced_disable_fullscreen_ambient_mode")
) )

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