Compare commits

...

55 Commits

Author SHA1 Message Date
semantic-release-bot
97c4682ccc chore(release): 4.3.0-dev.7 [skip ci]
# [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)) ([6742cd9](6742cd9232))
2024-02-26 03:52:58 +00:00
Linus
6742cd9232 feat(OpeningHours): Add Fix crash patch (#2697)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-02-26 04:50:59 +01:00
oSumAtrIX
5b2cc10adb docs: Fix broken links 2024-02-26 04:37:44 +01:00
semantic-release-bot
dbad6252fb chore(release): 4.3.0-dev.6 [skip ci]
# [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 ([0f7ed84](0f7ed841d1))
2024-02-25 18:30:14 +00:00
oSumAtrIX
0f7ed841d1 feat(VSCO - Unlock pro): Constrain to last working version 2024-02-25 19:28:12 +01:00
semantic-release-bot
d33d7d8f35 chore(release): 4.3.0-dev.5 [skip ci]
# [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 ([348e42a](348e42a374))
* **Twitter - Unlock downloads:** Unlock GIF downloads ([3200da8](3200da8657))
2024-02-25 07:02:56 +00:00
oSumAtrIX
3200da8657 feat(Twitter - Unlock downloads): Unlock GIF downloads 2024-02-25 08:00:29 +01:00
oSumAtrIX
348e42a374 feat: Remove unnecessary description from patch 2024-02-25 06:51:51 +01:00
oSumAtrIX
cbbac445b6 chore: Rename issue templates 2024-02-25 03:37:53 +01:00
oSumAtrIX
1593d1352a ci: Fix indentation in workflow 2024-02-24 01:14:45 +01:00
oSumAtrIX
c6fedaa7bc docs: Break long lines 2024-02-23 04:12:28 +01:00
oSumAtrIX
d070aebec4 docs: Fix mistakes and wording 2024-02-23 04:12:28 +01:00
oSumAtrIX
d583256f06 docs: Remove documentation section
The documentation for patches is being moved to the ReVanced Patcher repository. The reason for this is because the documentation about how to create patches using ReVanced Patcher, belongs in the ReVanced Patcher repository, whereas documentation specific to the public API of ReVanced Patches belongs in this repository. Because the public API has no documentation yet, the section is removed until then.
2024-02-23 02:55:45 +01:00
oSumAtrIX
fc67d284f7 ci: Fix PR builds by adding missing GitHub token 2024-02-23 02:23:50 +01:00
semantic-release-bot
d3f3f81b75 chore(release): 4.3.0-dev.4 [skip ci]
# [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 ([afe7f06](afe7f0605e))
* Use deprecated members to ensure backwards compatibility ([a6d8e42](a6d8e4210f))

### Features

* **X:** Add `Open links as query` patch ([#2730](https://github.com/ReVanced/revanced-patches/issues/2730)) ([43359b9](43359b95eb))
2024-02-22 00:13:48 +00:00
oSumAtrIX
8e8600c7c3 chore: Update fingerprint
The compiler of ReVanced Integrations now compiles the target method without `AccessFlags.Public`.
2024-02-22 01:11:17 +01:00
oSumAtrIX
ea33863083 ci: Do not start a Gradle daemon for PR builds
Because there are no subsequent builds, a daemon is not necessary
2024-02-22 01:10:09 +01:00
oSumAtrIX
a6d8e4210f fix: Use deprecated members to ensure backwards compatibility
By migrating to early to new APIs of ReVanced Patcher, if you were to use old versions of ReVanced Patcher, you would get compatibility issues. By using deprecated members until most have updated ReVanced Patcher, we can ensure seamless migration.
2024-02-22 01:10:09 +01:00
oSumAtrIX
1e3356808d ci: Split release into a separate PR build workflow
Because the release workflow already runs on dev and main, it is not necessary to also trigger it for PRs.
2024-02-22 01:07:05 +01:00
dic1911
43359b95eb feat(X): Add Open links as query patch (#2730)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-02-22 01:07:05 +01:00
oSumAtrIX
afe7f0605e fix: Compile DEX without debugging information 2024-02-21 04:15:38 +01:00
oSumAtrIX
8916789f7a build: Publish to GitHub Packages 2024-02-21 04:15:38 +01:00
oSumAtrIX
0d14aa1191 build: Sign release artifacts 2024-02-21 04:15:38 +01:00
oSumAtrIX
376dda6e81 build: Bump dependencies
This commit also migrates away from deprecated to new APIs
2024-02-21 04:07:32 +01:00
semantic-release-bot
ff8b58b645 chore(release): 4.3.0-dev.3 [skip ci]
# [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 ([e775bc2](e775bc2cae))
* **YouTube - Custom branding:** Improve patch option description ([f4b888b](f4b888be56))
2024-02-20 21:43:11 +00:00
oSumAtrIX
f4b888be56 feat(YouTube - Custom branding): Improve patch option description 2024-02-20 22:41:06 +01:00
oSumAtrIX
e775bc2cae feat(YouTube - Change header): Improve patch option description 2024-02-20 22:41:06 +01:00
oSumAtrIX
5c566753a8 build: Bump dependencies 2024-02-14 02:44:25 +01:00
oSumAtrIX
968ebf9f50 build: Bump Gradle 2024-02-14 02:41:21 +01:00
oSumAtrIX
75c510d876 chore: Fix ReplaceWith of Deprecated annotation 2024-02-13 03:27:03 +01:00
oSumAtrIX
72528cb2f1 chore: Remove dummy subproject
It is not necessary anymore
2024-02-13 02:58:24 +01:00
oSumAtrIX
81d79111cf chore: Add .editorconfig 2024-02-13 02:55:07 +01:00
oSumAtrIX
33036092b4 build: Bump dependencies 2024-02-13 02:54:40 +01:00
semantic-release-bot
f5f88092b6 chore(release): 4.3.0-dev.2 [skip ci]
# [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 ([0434d88](0434d8812b))
2024-02-09 02:47:31 +00:00
oSumAtrIX
0434d8812b feat(Sync for Reddit): Add Fix /s/ links patch 2024-02-09 03:45:37 +01:00
semantic-release-bot
ec38b8e51c chore(release): 4.3.0-dev.1 [skip ci]
# [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 ([96f9b73](96f9b73c74))
2024-02-09 00:17:34 +00:00
oSumAtrIX
96f9b73c74 feat(YouTube - Change start page): Add more start pages 2024-02-09 00:50:16 +01:00
semantic-release-bot
1502fe1f7f chore(release): 4.2.0 [skip ci]
# [4.2.0](https://github.com/ReVanced/revanced-patches/compare/v4.1.0...v4.2.0) (2024-02-08)

### Bug Fixes

* **Infinity for Reddit - Unlock subscription:** Do not crash by patching billing client ([53b29ea](53b29ea270))

### Features

* **X:** Add `Hide view count` patch ([1bf9582](1bf9582437))
* **X:** Add `Unlock downloads` patch ([3343b5c](3343b5cb12))
2024-02-08 19:17:40 +00:00
oSumAtrIX
824d094394 chore: Merge branch dev to main (#2693) 2024-02-08 20:15:32 +01:00
semantic-release-bot
010471a745 chore(release): 4.2.0-dev.1 [skip ci]
# [4.2.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.1.1-dev.1...v4.2.0-dev.1) (2024-02-08)

### Features

* **X:** Add `Hide view count` patch ([1bf9582](1bf9582437))
* **X:** Add `Unlock downloads` patch ([3343b5c](3343b5cb12))
2024-02-08 19:13:57 +00:00
oSumAtrIX
3343b5cb12 feat(X): Add Unlock downloads patch 2024-02-08 20:11:18 +01:00
oSumAtrIX
1bf9582437 feat(X): Add Hide view count patch 2024-02-08 20:08:44 +01:00
semantic-release-bot
ea09d4b520 chore(release): 4.1.1-dev.1 [skip ci]
## [4.1.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.1.0...v4.1.1-dev.1) (2024-02-07)

### Bug Fixes

* **Infinity for Reddit - Unlock subscription:** Do not crash by patching billing client ([53b29ea](53b29ea270))
2024-02-07 15:19:04 +00:00
oSumAtrIX
53b29ea270 fix(Infinity for Reddit - Unlock subscription): Do not crash by patching billing client 2024-02-07 16:16:33 +01:00
oSumAtrIX
fd11e2b969 chore: Fix typo in README 2024-02-05 23:24:05 +01:00
semantic-release-bot
65a32ee52e chore(release): 4.1.0 [skip ci]
# [4.1.0](https://github.com/ReVanced/revanced-patches/compare/v4.0.2...v4.1.0) (2024-02-05)

### Bug Fixes

* **Infinity for Reddit - Unlock subscription:** Restore functionality on v7.0.0 ([2c50c25](2c50c25a36))
* **YouTube:** Correctly show channel page on tablet devices ([#2656](https://github.com/ReVanced/revanced-patches/issues/2656)) ([047069c](047069ca8a))

### Features

* **YouTube - Custom filter:** Custom filtering of the protocol buffer ([#2682](https://github.com/ReVanced/revanced-patches/issues/2682)) ([19a9d11](19a9d113d9))
* **YouTube:** Support version `19.04.37` ([#2687](https://github.com/ReVanced/revanced-patches/issues/2687)) ([8b1cdd5](8b1cdd5c6a))
2024-02-05 19:26:27 +00:00
oSumAtrIX
7b2e32d88c chore: Merge branch dev to main (#2657) 2024-02-05 20:24:31 +01:00
semantic-release-bot
f18edb262d chore(release): 4.1.0-dev.2 [skip ci]
# [4.1.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.1.0-dev.1...v4.1.0-dev.2) (2024-02-04)

### Features

* **YouTube:** Support version `19.04.37` ([#2687](https://github.com/ReVanced/revanced-patches/issues/2687)) ([8b1cdd5](8b1cdd5c6a))
2024-02-04 17:40:14 +00:00
LisoUseInAIKyrios
8b1cdd5c6a feat(YouTube): Support version 19.04.37 (#2687) 2024-02-04 21:38:19 +04:00
semantic-release-bot
3bd41fb67f chore(release): 4.1.0-dev.1 [skip ci]
# [4.1.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.0.3-dev.2...v4.1.0-dev.1) (2024-02-02)

### Features

* **YouTube - Custom filter:** Custom filtering of the protocol buffer ([#2682](https://github.com/ReVanced/revanced-patches/issues/2682)) ([19a9d11](19a9d113d9))
2024-02-02 08:17:53 +00:00
LisoUseInAIKyrios
19a9d113d9 feat(YouTube - Custom filter): Custom filtering of the protocol buffer (#2682) 2024-02-02 12:15:34 +04:00
semantic-release-bot
61ee51b856 chore(release): 4.0.3-dev.2 [skip ci]
## [4.0.3-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.0.3-dev.1...v4.0.3-dev.2) (2024-02-01)

### Bug Fixes

* **Infinity for Reddit - Unlock subscription:** Restore functionality on v7.0.0 ([2c50c25](2c50c25a36))
2024-02-01 23:12:20 +00:00
oSumAtrIX
2c50c25a36 fix(Infinity for Reddit - Unlock subscription): Restore functionality on v7.0.0 2024-02-02 00:10:00 +01:00
oSumAtrIX
f5e7bf6e98 docs: Fix feature request template mentioning bug report template 2024-01-31 11:10:07 +01:00
LisoUseInAIKyrios
bba35d5cb5 chore: fix patch typo in WideSearchbarPatch 2024-01-29 17:31:57 +04:00
134 changed files with 1737 additions and 769 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

@@ -71,7 +71,7 @@ body:
Before creating a new feature request, please keep the following in mind: Before creating a new feature request, please keep the following in mind:
- **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-patches/labels/Feature%20request). - **Do not submit a duplicate feature request**: You can review existing feature requests [here](https://github.com/ReVanced/revanced-patches/labels/Feature%20request).
- **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patches/blob/main/CONTRIBUTING.md). - **Review the contribution guidelines**: Make sure your feature request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patches/blob/main/CONTRIBUTING.md).
- **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app). - **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
- type: textarea - type: textarea
attributes: attributes:

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,123 @@
# [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)
### Bug Fixes
* **Infinity for Reddit - Unlock subscription:** Do not crash by patching billing client ([7d76e2e](https://github.com/ReVanced/revanced-patches/commit/7d76e2e43c69b2b75f40a15a9147d041c77cbcd9))
### Features
* **X:** Add `Hide view count` patch ([bf064ec](https://github.com/ReVanced/revanced-patches/commit/bf064ecc1d5de8b592d14d34acfa1a4314c374ba))
* **X:** Add `Unlock downloads` patch ([2c20844](https://github.com/ReVanced/revanced-patches/commit/2c20844eaae698f185a9d321e2c70bde4b485cee))
# [4.2.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.1.1-dev.1...v4.2.0-dev.1) (2024-02-08)
### Features
* **X:** Add `Hide view count` patch ([bf064ec](https://github.com/ReVanced/revanced-patches/commit/bf064ecc1d5de8b592d14d34acfa1a4314c374ba))
* **X:** Add `Unlock downloads` patch ([2c20844](https://github.com/ReVanced/revanced-patches/commit/2c20844eaae698f185a9d321e2c70bde4b485cee))
## [4.1.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.1.0...v4.1.1-dev.1) (2024-02-07)
### Bug Fixes
* **Infinity for Reddit - Unlock subscription:** Do not crash by patching billing client ([7d76e2e](https://github.com/ReVanced/revanced-patches/commit/7d76e2e43c69b2b75f40a15a9147d041c77cbcd9))
# [4.1.0](https://github.com/ReVanced/revanced-patches/compare/v4.0.2...v4.1.0) (2024-02-05)
### Bug Fixes
* **Infinity for Reddit - Unlock subscription:** Restore functionality on v7.0.0 ([bf19af9](https://github.com/ReVanced/revanced-patches/commit/bf19af99cb522f9027a4b3ae42d6258ac71758e5))
* **YouTube:** Correctly show channel page on tablet devices ([#2656](https://github.com/ReVanced/revanced-patches/issues/2656)) ([c7c9700](https://github.com/ReVanced/revanced-patches/commit/c7c9700d93caeae105916d33376670f525276fac))
### Features
* **YouTube - Custom filter:** Custom filtering of the protocol buffer ([#2682](https://github.com/ReVanced/revanced-patches/issues/2682)) ([872a5b6](https://github.com/ReVanced/revanced-patches/commit/872a5b6d8969ab1569cd57ece3c400c3741049be))
* **YouTube:** Support version `19.04.37` ([#2687](https://github.com/ReVanced/revanced-patches/issues/2687)) ([c23e023](https://github.com/ReVanced/revanced-patches/commit/c23e0233cf5c28d354132443d227b42ddc4a3dad))
# [4.1.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.1.0-dev.1...v4.1.0-dev.2) (2024-02-04)
### Features
* **YouTube:** Support version `19.04.37` ([#2687](https://github.com/ReVanced/revanced-patches/issues/2687)) ([c23e023](https://github.com/ReVanced/revanced-patches/commit/c23e0233cf5c28d354132443d227b42ddc4a3dad))
# [4.1.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.0.3-dev.2...v4.1.0-dev.1) (2024-02-02)
### Features
* **YouTube - Custom filter:** Custom filtering of the protocol buffer ([#2682](https://github.com/ReVanced/revanced-patches/issues/2682)) ([872a5b6](https://github.com/ReVanced/revanced-patches/commit/872a5b6d8969ab1569cd57ece3c400c3741049be))
## [4.0.3-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.0.3-dev.1...v4.0.3-dev.2) (2024-02-01)
### Bug Fixes
* **Infinity for Reddit - Unlock subscription:** Restore functionality on v7.0.0 ([bf19af9](https://github.com/ReVanced/revanced-patches/commit/bf19af99cb522f9027a4b3ae42d6258ac71758e5))
## [4.0.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.0.2...v4.0.3-dev.1) (2024-01-28) ## [4.0.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.0.2...v4.0.3-dev.1) (2024-01-28)

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
@@ -406,6 +410,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
@@ -550,6 +560,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
@@ -1065,6 +1081,18 @@ public final class app/revanced/patches/twitch/misc/settings/SettingsResourcePat
public static final field INSTANCE Lapp/revanced/patches/twitch/misc/settings/SettingsResourcePatch; public static final field INSTANCE Lapp/revanced/patches/twitch/misc/settings/SettingsResourcePatch;
} }
public final class app/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/twitter/interaction/downloads/UnlockDownloadsPatch;
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/twitter/layout/viewcount/HideViewCountPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/twitter/layout/viewcount/HideViewCountPatch;
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/twitter/misc/dynamiccolor/DynamicColorPatch : app/revanced/patcher/patch/ResourcePatch { public final class app/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch; public static final field INSTANCE Lapp/revanced/patches/twitter/misc/dynamiccolor/DynamicColorPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
@@ -1092,6 +1120,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
@@ -1420,6 +1454,10 @@ public final class app/revanced/patches/youtube/layout/startupshortsreset/Disabl
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/youtube/layout/startupshortsreset/fingerprints/UserWasInShortsFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/startupshortsreset/fingerprints/UserWasInShortsFingerprint;
}
public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch : app/revanced/patcher/patch/BytecodePatch { public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch; public static final field INSTANCE Lapp/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V

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.0.3-dev.1 version = 4.3.0-dev.7

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.4"
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

@@ -11,16 +11,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")) {
@@ -54,7 +55,7 @@ object OverrideCertificatePinningPatch : ResourcePatch() {
</trust-anchors> </trust-anchors>
</debug-overrides> </debug-overrides>
</network-security-config> </network-security-config>
""" """,
) )
} else { } else {
// If the file already exists. // If the file already exists.
@@ -63,12 +64,11 @@ object OverrideCertificatePinningPatch : ResourcePatch() {
writeText( writeText(
text.replace( text.replace(
"<trust-anchors>", "<trust-anchors>",
"<trust-anchors>\n<certificates src=\"user\" overridePins=\"true\" />\n<certificates src=\"system\" />" "<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

@@ -5,6 +5,7 @@ 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.reddit.customclients.infinityforreddit.api.SpoofClientPatch import app.revanced.patches.reddit.customclients.infinityforreddit.api.SpoofClientPatch
import app.revanced.patches.reddit.customclients.infinityforreddit.subscription.fingerprints.BillingClientOnServiceConnected
import app.revanced.patches.reddit.customclients.infinityforreddit.subscription.fingerprints.StartSubscriptionActivityFingerprint import app.revanced.patches.reddit.customclients.infinityforreddit.subscription.fingerprints.StartSubscriptionActivityFingerprint
import app.revanced.util.returnEarly import app.revanced.util.returnEarly
@@ -12,13 +13,14 @@ import app.revanced.util.returnEarly
name = "Unlock subscription", name = "Unlock subscription",
description = "Unlocks the subscription feature but requires a custom client ID.", description = "Unlocks the subscription feature but requires a custom client ID.",
compatiblePackages = [ compatiblePackages = [
CompatiblePackage("ml.docilealligator.infinityforreddit") CompatiblePackage("ml.docilealligator.infinityforreddit"),
], ],
dependencies = [SpoofClientPatch::class] dependencies = [SpoofClientPatch::class],
) )
@Suppress("unused") @Suppress("unused")
object UnlockSubscriptionPatch : BytecodePatch( object UnlockSubscriptionPatch : BytecodePatch(
setOf(StartSubscriptionActivityFingerprint) setOf(StartSubscriptionActivityFingerprint, BillingClientOnServiceConnected),
) { ) {
override fun execute(context: BytecodeContext) = listOf(StartSubscriptionActivityFingerprint).returnEarly() override fun execute(context: BytecodeContext) =
listOf(StartSubscriptionActivityFingerprint, BillingClientOnServiceConnected).returnEarly()
} }

View File

@@ -0,0 +1,7 @@
package app.revanced.patches.reddit.customclients.infinityforreddit.subscription.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object BillingClientOnServiceConnected : MethodFingerprint(
strings = listOf("Billing service connected"),
)

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

@@ -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
@@ -49,14 +51,18 @@ abstract class BaseSettingsResourcePatch(
// 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)
} }
} }
// 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

@@ -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

@@ -0,0 +1,83 @@
package app.revanced.patches.twitter.interaction.downloads
import app.revanced.patcher.data.BytecodeContext
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.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.fingerprint.MethodFingerprintResult
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.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.ShowDownloadVideoUpsellBottomSheetFingerprint
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.TwoRegisterInstruction
@Patch(
name = "Unlock downloads",
description = "Unlocks the ability to download any video. GIFs can be downloaded via the menu on long press.",
compatiblePackages = [CompatiblePackage("com.twitter.android")],
)
@Suppress("unused")
object UnlockDownloadsPatch : BytecodePatch(
setOf(
ConstructMediaOptionsSheetFingerprint,
ShowDownloadVideoUpsellBottomSheetFingerprint,
BuildMediaOptionsSheetFingerprint,
),
) {
override fun execute(context: BytecodeContext) {
fun MethodFingerprint.patch(getRegisterAndIndex: MethodFingerprintResult.() -> Pair<Int, Int>) = result?.let {
getRegisterAndIndex(it).let { (index, register) ->
it.mutableMethod.addInstruction(index, "const/4 v$register, 0x1")
}
} ?: throw exception
// Allow downloads for non-premium users.
ShowDownloadVideoUpsellBottomSheetFingerprint.patch {
val checkIndex = scanResult.patternScanResult!!.startIndex
val register = mutableMethod.getInstruction<OneRegisterInstruction>(checkIndex).registerA
checkIndex to register
}
// Force show the download menu item.
ConstructMediaOptionsSheetFingerprint.patch {
val showDownloadButtonIndex = mutableMethod.getInstructions().lastIndex - 1
val register = mutableMethod.getInstruction<TwoRegisterInstruction>(showDownloadButtonIndex).registerA
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

@@ -0,0 +1,12 @@
package app.revanced.patches.twitter.interaction.downloads.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object ConstructMediaOptionsSheetFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
strings = listOf("captionsState")
)

View File

@@ -0,0 +1,10 @@
package app.revanced.patches.twitter.interaction.downloads.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object ShowDownloadVideoUpsellBottomSheetFingerprint : MethodFingerprint(
returnType = "Z",
strings = listOf("variantToDownload.url"),
opcodes = listOf(Opcode.IF_EQZ)
)

View File

@@ -0,0 +1,30 @@
package app.revanced.patches.twitter.layout.viewcount
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.layout.viewcount.fingerprints.ViewCountsEnabledFingerprint
import app.revanced.util.exception
@Patch(
name = "Hide view count",
description = "Hides the view count of Posts.",
compatiblePackages = [CompatiblePackage("com.twitter.android")],
use = false
)
@Suppress("unused")
object HideViewCountPatch : BytecodePatch(
setOf(ViewCountsEnabledFingerprint)
) {
override fun execute(context: BytecodeContext) =
ViewCountsEnabledFingerprint.result?.mutableMethod?.addInstructions(
0,
"""
const/4 v0, 0x0
return v0
"""
) ?: throw ViewCountsEnabledFingerprint.exception
}

View File

@@ -0,0 +1,8 @@
package app.revanced.patches.twitter.layout.viewcount.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object ViewCountsEnabledFingerprint : MethodFingerprint(
returnType = "Z",
strings = listOf("view_counts_public_visibility_enabled")
)

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",
@@ -35,30 +36,35 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
"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.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
@@ -69,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

@@ -30,7 +30,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -35,7 +35,9 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"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.04.37"
] ]
) )
] ]

View File

@@ -13,26 +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.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) {
@@ -42,4 +45,4 @@ object CopyVideoUrlBytecodePatch : BytecodePatch() {
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$descriptor->changeVisibility(Z)V") PlayerControlsBytecodePatch.injectVisibilityCheckCall("$descriptor->changeVisibility(Z)V")
} }
} }
} }

View File

@@ -32,7 +32,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -13,22 +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.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) {
@@ -37,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

@@ -32,7 +32,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -34,7 +34,9 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
"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.04.37"
] ]
) )
] ]

View File

@@ -30,7 +30,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"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.04.37"
] ]
) )
], ],

View File

@@ -36,7 +36,9 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
"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.04.37"
] ]
) )
] ]

View File

@@ -34,7 +34,9 @@ import app.revanced.util.exception
"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.04.37"
] ]
) )
], ],

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,62 +15,69 @@ 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(),
) )
} }
@@ -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

@@ -33,7 +33,9 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"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.04.37"
] ]
) )
] ]

View File

@@ -43,7 +43,9 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
"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.04.37"
] ]
) )
] ]

View File

@@ -34,7 +34,9 @@ import com.android.tools.smali.dexlib2.Opcode
"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.04.37"
] ]
) )
] ]

View File

@@ -17,13 +17,13 @@ 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)
@@ -38,7 +38,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

@@ -40,7 +40,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -38,7 +38,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
"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.04.37"
] ]
) )
] ]

View File

@@ -32,7 +32,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -32,7 +32,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -32,7 +32,9 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"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.04.37"
] ]
) )
] ]

View File

@@ -32,7 +32,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -35,7 +35,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
"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.04.37"
] ]
) )
] ]

View File

@@ -32,7 +32,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -28,7 +28,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -28,7 +28,9 @@ import app.revanced.util.exception
"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.04.37"
] ]
) )
] ]

View File

@@ -46,7 +46,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"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.04.37"
] ]
) )
] ]
@@ -59,6 +61,9 @@ object HideLayoutComponentsPatch : BytecodePatch(
"Lapp/revanced/integrations/youtube/patches/components/LayoutComponentsFilter;" "Lapp/revanced/integrations/youtube/patches/components/LayoutComponentsFilter;"
private const val DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME = private const val DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME =
"Lapp/revanced/integrations/youtube/patches/components/DescriptionComponentsFilter;" "Lapp/revanced/integrations/youtube/patches/components/DescriptionComponentsFilter;"
private const val CUSTOM_FILTER_CLASS_NAME =
"Lapp/revanced/integrations/youtube/patches/components/CustomFilter;"
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
AddResourcesPatch(this::class) AddResourcesPatch(this::class)
@@ -116,6 +121,7 @@ object HideLayoutComponentsPatch : BytecodePatch(
LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR) LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR)
LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME) LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME)
LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_NAME)
// region Mix playlists // region Mix playlists

View File

@@ -39,7 +39,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -28,7 +28,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -33,7 +33,9 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
"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.04.37"
] ]
) )
] ]

View File

@@ -30,7 +30,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -37,7 +37,9 @@ import app.revanced.patches.youtube.shared.fingerprints.SeekbarOnDrawFingerprint
"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.04.37"
] ]
) )
] ]

View File

@@ -40,7 +40,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
"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.04.37"
] ]
) )
] ]

View File

@@ -27,7 +27,9 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
"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.04.37"
] ]
) )
] ]

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