Compare commits
54 commits
fix/remove
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b632d0f042 | ||
|
|
fca2470990 | ||
|
|
66b6c8c8ed | ||
|
|
4b699da220 | ||
|
|
e19275fb7d | ||
|
|
4bc8c7c0f6 | ||
|
|
e51c5292c1 | ||
|
|
43688d0622 | ||
|
|
4b6c3e3123 | ||
|
|
14ea61355d | ||
|
|
90eebe082c | ||
|
|
6564c7642f | ||
|
|
a05386e8bc | ||
|
|
64cae2bfa5 | ||
|
|
df1c3a4a70 | ||
|
|
558f683bf2 | ||
|
|
252617b8dd | ||
|
|
c35b8b8e96 | ||
|
|
33aa830a1c | ||
|
|
8f10eb62a8 | ||
|
|
8f6f128d71 | ||
|
|
5fe8b58284 | ||
|
|
3e32c38732 | ||
|
|
ce85fbfad7 | ||
|
|
02a48e7a5f | ||
|
|
5e1f899a3b | ||
|
|
938c6fae60 | ||
|
|
ca903248a5 | ||
|
|
a2a11d7362 | ||
|
|
7517f57ac7 | ||
|
|
dc39dfdab4 | ||
|
|
806d6c799f | ||
|
|
b2b1e4f993 | ||
|
|
882cb4a0d4 | ||
|
|
24caae98b7 | ||
|
|
a02350bbc8 | ||
|
|
e0dc009780 | ||
|
|
d2659637ac | ||
|
|
ddb6396b3f | ||
|
|
49dffe37c5 | ||
|
|
0637d4596a | ||
|
|
55f510dbed | ||
|
|
bb7448bc9d | ||
|
|
ed9f191a2e | ||
|
|
f934022f37 | ||
|
|
11bd06d374 | ||
|
|
6a547a97e5 | ||
|
|
f7298ffd52 | ||
|
|
63161e9fb3 | ||
|
|
a031891566 | ||
|
|
fdfed3c9dd | ||
|
|
f045923cef | ||
|
|
e9bfb7ca9b | ||
|
|
46fb3669ee |
185 changed files with 30973 additions and 17113 deletions
4
.github/workflows/build_pull_request.yml
vendored
4
.github/workflows/build_pull_request.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.pr && format('refs/pull/{0}/merge', inputs.pr) || github.ref }}
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ jobs:
|
|||
run: ./gradlew :patches:buildAndroid --no-daemon
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: revanced-patches
|
||||
path: patches/build/libs
|
||||
|
|
|
|||
2
.github/workflows/open_pull_request.yml
vendored
2
.github/workflows/open_pull_request.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Open pull request
|
||||
uses: repo-sync/pull-request@v2
|
||||
|
|
|
|||
3
.github/workflows/pull_strings.yml
vendored
3
.github/workflows/pull_strings.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: dev
|
||||
persist-credentials: true
|
||||
|
|
@ -32,6 +32,7 @@ jobs:
|
|||
|
||||
- name: Process strings
|
||||
run: |
|
||||
chmod -R 777 patches/src/main/resources
|
||||
./gradlew processStringsFromCrowdin
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }}
|
||||
|
|
|
|||
2
.github/workflows/push_strings.yml
vendored
2
.github/workflows/push_strings.yml
vendored
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Process strings
|
||||
env:
|
||||
|
|
|
|||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
|
|
@ -15,10 +15,11 @@ jobs:
|
|||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
artifact-metadata: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v5
|
||||
|
|
@ -61,7 +62,7 @@ jobs:
|
|||
|
||||
- name: Attest
|
||||
if: steps.release.outputs.new_release_published == 'true'
|
||||
uses: actions/attest-build-provenance@v3
|
||||
uses: actions/attest@v4
|
||||
with:
|
||||
subject-name: 'ReVanced Patches ${{ steps.release.outputs.new_release_git_tag }}'
|
||||
subject-path: patches/build/libs/patches-*.rvp
|
||||
|
|
|
|||
4
.github/workflows/update-gradle-wrapper.yml
vendored
4
.github/workflows/update-gradle-wrapper.yml
vendored
|
|
@ -10,9 +10,9 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Update Gradle Wrapper
|
||||
uses: gradle-update/update-gradle-wrapper-action@v1
|
||||
uses: gradle-update/update-gradle-wrapper-action@v2
|
||||
with:
|
||||
target-branch: dev
|
||||
|
|
|
|||
307
CHANGELOG.md
307
CHANGELOG.md
|
|
@ -1,3 +1,310 @@
|
|||
# [6.1.0](https://github.com/ReVanced/revanced-patches/compare/v6.0.1...v6.1.0) (2026-03-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Export internal data documents provider:** Correct S_IFLNK constant and symlink detection mask ([#6819](https://github.com/ReVanced/revanced-patches/issues/6819)) ([252617b](https://github.com/ReVanced/revanced-patches/commit/252617b8dd3f24e1ff9a04ba1d91b43dc29bd757))
|
||||
* **YouTube - Custom branding:** Fix double icons and change default branding to ReVanced ([#6806](https://github.com/ReVanced/revanced-patches/issues/6806)) ([e51c529](https://github.com/ReVanced/revanced-patches/commit/e51c5292c171325e7cfa0f5ee85474d9b3961a34))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Spoof root of trust` and `Spoof keystore security level` patch ([#6751](https://github.com/ReVanced/revanced-patches/issues/6751)) ([4bc8c7c](https://github.com/ReVanced/revanced-patches/commit/4bc8c7c0f60a095533f07dc281f0320f8eb22f3c))
|
||||
* **Announcements:** Support ReVanced API v5 announcements ([a05386e](https://github.com/ReVanced/revanced-patches/commit/a05386e8bc24c085b5c74f3674c402c5dd5ad468))
|
||||
* Change contact email in patches about ([df1c3a4](https://github.com/ReVanced/revanced-patches/commit/df1c3a4a70fd2595d77b539299f1f7301bc60d24))
|
||||
* **Instagram:** Add `Enable location sticker redesign` patch ([#6808](https://github.com/ReVanced/revanced-patches/issues/6808)) ([4b699da](https://github.com/ReVanced/revanced-patches/commit/4b699da220e5d1527c390792b6228e2d9cffedb7))
|
||||
* **Spoof video streams:** Add Android Reel client to fix playback issues ([#6830](https://github.com/ReVanced/revanced-patches/issues/6830)) ([4b6c3e3](https://github.com/ReVanced/revanced-patches/commit/4b6c3e312328fbf6a1c7065e27d8ff04573e58be))
|
||||
|
||||
# [6.1.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.3...v6.1.0-dev.4) (2026-03-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Custom branding:** Fix double icons and change default branding to ReVanced ([#6806](https://github.com/ReVanced/revanced-patches/issues/6806)) ([e51c529](https://github.com/ReVanced/revanced-patches/commit/e51c5292c171325e7cfa0f5ee85474d9b3961a34))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Spoof root of trust` and `Spoof keystore security level` patch ([#6751](https://github.com/ReVanced/revanced-patches/issues/6751)) ([4bc8c7c](https://github.com/ReVanced/revanced-patches/commit/4bc8c7c0f60a095533f07dc281f0320f8eb22f3c))
|
||||
* **Instagram:** Add `Enable location sticker redesign` patch ([#6808](https://github.com/ReVanced/revanced-patches/issues/6808)) ([4b699da](https://github.com/ReVanced/revanced-patches/commit/4b699da220e5d1527c390792b6228e2d9cffedb7))
|
||||
|
||||
# [6.1.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.2...v6.1.0-dev.3) (2026-03-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spoof video streams:** Add Android Reel client to fix playback issues ([#6830](https://github.com/ReVanced/revanced-patches/issues/6830)) ([4b6c3e3](https://github.com/ReVanced/revanced-patches/commit/4b6c3e312328fbf6a1c7065e27d8ff04573e58be))
|
||||
|
||||
# [6.1.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v6.1.0-dev.1...v6.1.0-dev.2) (2026-03-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Announcements:** Support ReVanced API v5 announcements ([a05386e](https://github.com/ReVanced/revanced-patches/commit/a05386e8bc24c085b5c74f3674c402c5dd5ad468))
|
||||
|
||||
# [6.1.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.2-dev.1...v6.1.0-dev.1) (2026-03-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Change contact email in patches about ([df1c3a4](https://github.com/ReVanced/revanced-patches/commit/df1c3a4a70fd2595d77b539299f1f7301bc60d24))
|
||||
|
||||
## [6.0.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.1...v6.0.2-dev.1) (2026-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Export internal data documents provider:** Correct S_IFLNK constant and symlink detection mask ([#6819](https://github.com/ReVanced/revanced-patches/issues/6819)) ([252617b](https://github.com/ReVanced/revanced-patches/commit/252617b8dd3f24e1ff9a04ba1d91b43dc29bd757))
|
||||
|
||||
## [6.0.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.0...v6.0.1) (2026-03-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **GmsCore support:** use `prefixOrReplace` for non-matching APP_AUTHORITIES in content URL transformation ([#6801](https://github.com/ReVanced/revanced-patches/issues/6801)) ([8f6f128](https://github.com/ReVanced/revanced-patches/commit/8f6f128d718c20c56668ed3801b434a5cbb04dfd))
|
||||
* **YouTube Music - Hide buttons:** Crashes on startup due to null LayoutParams ([#6799](https://github.com/ReVanced/revanced-patches/issues/6799)) ([3e32c38](https://github.com/ReVanced/revanced-patches/commit/3e32c387328b061f33b361ed022ae18e447a7904))
|
||||
* **YouTube:** Use correct query parameters for DeArrow requests ([#6780](https://github.com/ReVanced/revanced-patches/issues/6780)) ([02a48e7](https://github.com/ReVanced/revanced-patches/commit/02a48e7a5f2b1ffd64a80651b49666de27ab7014))
|
||||
|
||||
## [6.0.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v6.0.1-dev.2...v6.0.1-dev.3) (2026-03-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **GmsCore support:** use `prefixOrReplace` for non-matching APP_AUTHORITIES in content URL transformation ([#6801](https://github.com/ReVanced/revanced-patches/issues/6801)) ([8f6f128](https://github.com/ReVanced/revanced-patches/commit/8f6f128d718c20c56668ed3801b434a5cbb04dfd))
|
||||
|
||||
## [6.0.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v6.0.1-dev.1...v6.0.1-dev.2) (2026-03-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube Music - Hide buttons:** Crashes on startup due to null LayoutParams ([#6799](https://github.com/ReVanced/revanced-patches/issues/6799)) ([3e32c38](https://github.com/ReVanced/revanced-patches/commit/3e32c387328b061f33b361ed022ae18e447a7904))
|
||||
|
||||
## [6.0.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v6.0.0...v6.0.1-dev.1) (2026-03-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Use correct query parameters for DeArrow requests ([#6780](https://github.com/ReVanced/revanced-patches/issues/6780)) ([02a48e7](https://github.com/ReVanced/revanced-patches/commit/02a48e7a5f2b1ffd64a80651b49666de27ab7014))
|
||||
|
||||
# [6.0.0](https://github.com/ReVanced/revanced-patches/compare/v5.50.2...v6.0.0) (2026-03-14)
|
||||
|
||||
|
||||
* build(Needs bump)!: Update to ReVanced Patcher v22 ([#6542](https://github.com/ReVanced/revanced-patches/issues/6542)) ([ab2ac36](https://github.com/ReVanced/revanced-patches/commit/ab2ac36e3041cda87b659924ea2b75089f0bdb6e))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add minSdk to all extension projects ([#6778](https://github.com/ReVanced/revanced-patches/issues/6778)) ([7517f57](https://github.com/ReVanced/revanced-patches/commit/7517f57ac7a54e1c914e8dd8cc3e1aa908e28e54))
|
||||
* **Check environment:** Use another (also more suitable) API to circumvent a bug ([393700f](https://github.com/ReVanced/revanced-patches/commit/393700f74ac141bfa109988202707b40d35a64ea))
|
||||
* **Custom branding:** Fix defaults ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([3e00a99](https://github.com/ReVanced/revanced-patches/commit/3e00a99c1bb3af24f9e8420e8c7c2bbaeb003c6c))
|
||||
* **Custom branding:** Resolve background playback crash with custom branded root installation ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([6aba2d1](https://github.com/ReVanced/revanced-patches/commit/6aba2d127472643c346108d481513442fa9a3fde))
|
||||
* **Enable debugging:** Add missing preference to log protocol buffer ([26d8a9e](https://github.com/ReVanced/revanced-patches/commit/26d8a9e5f891e08fe3c23601e8238de6a301b8df))
|
||||
* Fix return type check to match method successfully ([0a73452](https://github.com/ReVanced/revanced-patches/commit/0a734528dc4407571ae1dba3e80347bc9f236e3e))
|
||||
* **GmsCore support:** Handle GmsCore flavors when checking for updates ([2aa19f5](https://github.com/ReVanced/revanced-patches/commit/2aa19f5995fd050c40b15331a77d58144a5a1f69))
|
||||
* **GmsCore support:** Insert check after another missing necessary context hook ([3c0c5a8](https://github.com/ReVanced/revanced-patches/commit/3c0c5a86d8e24b47b1c30bc5a7fe994240014e2d))
|
||||
* **GmsCore support:** Insert check after necessary context hook ([03e8e3d](https://github.com/ReVanced/revanced-patches/commit/03e8e3d75cb3b03987299885cea5eb615a5cef23))
|
||||
* **GmsCore support:** Rename MicroG GmsCore specific strings as well and rename app specific strings correctly ([c2ac1f0](https://github.com/ReVanced/revanced-patches/commit/c2ac1f04a0ac180555a9d19e7ff41525487fbc6d))
|
||||
* **GmsCore support:** Try replacing in strings before prefixing to handle more edge cases ([4d94a41](https://github.com/ReVanced/revanced-patches/commit/4d94a41c46f2d4e1bf33debc95b8aa84a64964bb))
|
||||
* **Hex patch:** Fix bug in implementation of Boyer-Moore algorithm ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([f59323c](https://github.com/ReVanced/revanced-patches/commit/f59323c87d8da36b39e19936c8ed5c07d3903b16))
|
||||
* **Hex:** Add back name, which was accidentally removed from the patch ([6a547a9](https://github.com/ReVanced/revanced-patches/commit/6a547a97e52b7914bb6602f3ecc2c6cecd50e946))
|
||||
* **Instagram:** Update fingerprints for version `417.0.0.54.77` ([#6734](https://github.com/ReVanced/revanced-patches/issues/6734)) ([55f510d](https://github.com/ReVanced/revanced-patches/commit/55f510dbedd28678411b4f11d9bbdd303fa68a0d))
|
||||
* Move strings to correct patch ([4dfe3fb](https://github.com/ReVanced/revanced-patches/commit/4dfe3fb08812ed572e01e58a8604c1be9e989438))
|
||||
* **ProtonVPN - Remove delay:** Make it work on latest version by patching the correct class ([#6757](https://github.com/ReVanced/revanced-patches/issues/6757)) ([e0dc009](https://github.com/ReVanced/revanced-patches/commit/e0dc009780afea9c2f393c4f348cda5ca9c3cbbf))
|
||||
* **Reddit clients:** Fix patching broken during patcher migration by searching for strings with contains([#6681](https://github.com/ReVanced/revanced-patches/issues/6681)) ([00da402](https://github.com/ReVanced/revanced-patches/commit/00da4027707068f06ee7041b53d1316a7b218d5d))
|
||||
* Rename string keys correctly ([16e00ab](https://github.com/ReVanced/revanced-patches/commit/16e00ab4c0ff10e58adea40c7de72658788fcd97))
|
||||
* **Spotify - Sanitize sharing links:** Update patch to latest app versions ([#6685](https://github.com/ReVanced/revanced-patches/issues/6685)) ([bb7448b](https://github.com/ReVanced/revanced-patches/commit/bb7448bc9d789843371d16bfccc9815662913333))
|
||||
* Use correct string key ([9d55d00](https://github.com/ReVanced/revanced-patches/commit/9d55d00ff46a2cd18111a91a98dbc8e3137dd0ed))
|
||||
* Use custom comparison block for strings in `anyOf` ([56a087d](https://github.com/ReVanced/revanced-patches/commit/56a087dbacf331ccadfe753cbc1ced77e318fc27))
|
||||
* Use positional substitutes in strings where multiple are present ([aa8c87f](https://github.com/ReVanced/revanced-patches/commit/aa8c87f8650bd5def5f726f02be5d62d72a3007b))
|
||||
* **YouTube - Enable Debugging Patch:** Use correct Protocolbuffer setting name ([#6711](https://github.com/ReVanced/revanced-patches/issues/6711)) ([f934022](https://github.com/ReVanced/revanced-patches/commit/f934022f37ba178ac23abfa9bcd59a0c12abe43f))
|
||||
* **YouTube - Exit fullscreen mode:** Handle exiting fullscreen on first opened video ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([88724d4](https://github.com/ReVanced/revanced-patches/commit/88724d47b13d56a90384b0a2588ba82ccdd5b101))
|
||||
* **YouTube - Hide ads:** Empty space left when ads are hidden on tablets ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([c1c2aa9](https://github.com/ReVanced/revanced-patches/commit/c1c2aa98b2d7ce900eb152bc736f3c1a5558d9fc))
|
||||
* **YouTube - Hide ads:** Fix "Hide YouTube Premium promotions" hiding YouTube Doodles ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([d26e352](https://github.com/ReVanced/revanced-patches/commit/d26e352850c2659a65b13ff1ba50dcd18278603a))
|
||||
* **YouTube - Hide ads:** Hide new type of general ad, movie ad and web search result ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([9b12dd1](https://github.com/ReVanced/revanced-patches/commit/9b12dd106546d94004c971b887ffa7627ae5a8d4))
|
||||
* **YouTube - Hide ads:** Hide new type of player ad ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([c97aefc](https://github.com/ReVanced/revanced-patches/commit/c97aefc272b83b522e5ac393ec41d03630cee6fb))
|
||||
* **YouTube - Hide ads:** Hide video ads does not hide Shorts ads ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([8d274a7](https://github.com/ReVanced/revanced-patches/commit/8d274a7afc3abfafc2b702b27f022316c854dae6))
|
||||
* **YouTube - Hide ads:** Support Hide fullscreen ads on Android 13+ devices ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([02b405e](https://github.com/ReVanced/revanced-patches/commit/02b405e6ac5beeff81c7705379e6c6eb1561270d))
|
||||
* **YouTube - Hide ads:** YouTube Doodles unclickable when Hide ads is enabled ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([5d45b6d](https://github.com/ReVanced/revanced-patches/commit/5d45b6da74165ca69a336aa36e90daafaaf87411))
|
||||
* **YouTube - Hide end screen cards:** Resolve patching 20.31.4x ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([3ff303f](https://github.com/ReVanced/revanced-patches/commit/3ff303f045c4fbda0331e3f1e9fbba50f97dedab))
|
||||
* **YouTube - Hide layout components:** Ensure featured places also hide watch history shelf ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([d639faf](https://github.com/ReVanced/revanced-patches/commit/d639faf71f476bcd7fffa08bfbb0e77c02450c9f))
|
||||
* **YouTube - Hide layout components:** Fix certain description components not working ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([1bf64eb](https://github.com/ReVanced/revanced-patches/commit/1bf64eb8b06435dea9cd292376c5feda6683e0a6))
|
||||
* **YouTube - Hide layout components:** Fix empty space issues (subscribed channels bar, show more button, landscape mode) ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([22ef700](https://github.com/ReVanced/revanced-patches/commit/22ef7002e07df919c30e9274a2479925a4be69c0))
|
||||
* **YouTube - Hide layout components:** Fix side effect of Disable translucent status bar ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([5760c58](https://github.com/ReVanced/revanced-patches/commit/5760c5860ac2dc6a41821cc66f849a58e44bf3e7))
|
||||
* **YouTube - Hide layout components:** Resolve "Hide community posts" not working in search results ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([3153222](https://github.com/ReVanced/revanced-patches/commit/315322220d6a09814406394414bcfcff61ead786))
|
||||
* **YouTube - Hide layout components:** Resolve community posts sometimes showing in player suggestions ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([828df77](https://github.com/ReVanced/revanced-patches/commit/828df77810b551c70e03d888dc0fe1555c488f51))
|
||||
* **YouTube - Hide Shorts components:** Action buttons not hidden in 20.22+ ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([a90a0b1](https://github.com/ReVanced/revanced-patches/commit/a90a0b1199e66cace3eb1b8c827314ceaf514ecf))
|
||||
* **YouTube - Hide Shorts components:** Do not hide channel page headers when hiding shorts ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([1246e43](https://github.com/ReVanced/revanced-patches/commit/1246e430f2104bc4a33881fa4dbb188201c02202))
|
||||
* **YouTube - Hide Shorts components:** Find resource id only for 21.05+ ([63161e9](https://github.com/ReVanced/revanced-patches/commit/63161e9fb357387685294e4a80de94cb351c6713))
|
||||
* **YouTube - Hide Shorts components:** Fix sound metadata label hiding other components ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([49d1f65](https://github.com/ReVanced/revanced-patches/commit/49d1f65fcae5b6732b768f6184969a6c796bc5e3))
|
||||
* **YouTube - Hide Shorts components:** Hide new type of sound metadata label ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([a6b8d2f](https://github.com/ReVanced/revanced-patches/commit/a6b8d2f1039b7896b21826a46f3f13b32d16b51d))
|
||||
* **YouTube - Hide Shorts components:** Resolve hiding Shorts not working ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([ae69bdc](https://github.com/ReVanced/revanced-patches/commit/ae69bdc1d376a05b6854401586408cb6a9bda7eb))
|
||||
* **YouTube - Loop video:** Enable loop video not working in playlist ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([06dbf7e](https://github.com/ReVanced/revanced-patches/commit/06dbf7ee80c836404e3698c9db6176da9a2ab8e1))
|
||||
* **YouTube - Loop video:** Fix looping button state ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([14d0135](https://github.com/ReVanced/revanced-patches/commit/14d0135b3c41bb0c06fb8cd6569a489c41e51105))
|
||||
* **YouTube - Loop video:** Wrong icon applied ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([b34adf6](https://github.com/ReVanced/revanced-patches/commit/b34adf6437294b0b28500c207b5f29ddd2ed294d))
|
||||
* **YouTube - Open Shorts in regular player:** Fix back behavior with 20.51 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([46ec3d3](https://github.com/ReVanced/revanced-patches/commit/46ec3d3bdd7d0368e1503a1b1be815eaf9b56525))
|
||||
* **YouTube - Open Shorts in regular player:** Resolve back button closing app instead of exiting fullscreen ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([b787c46](https://github.com/ReVanced/revanced-patches/commit/b787c469fd856dff74870fcb61bb3fc3dc5514b7))
|
||||
* **YouTube - Playback speed:** Use correct extension method name ([b8b4cfb](https://github.com/ReVanced/revanced-patches/commit/b8b4cfbd016058a158364f4549e7ef6ed4d154e0))
|
||||
* **YouTube - Remove background playback restrictions:** Fix background playback not working with certain offline videos ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([2d098f2](https://github.com/ReVanced/revanced-patches/commit/2d098f2352b7dc1f0dc185ac65074443289ef2de))
|
||||
* **YouTube - Remove viewer discretion dialog:** Not working on 20.14.43+ ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([64c397e](https://github.com/ReVanced/revanced-patches/commit/64c397eb1c46bdd77f2b05d03c22a841971bea81))
|
||||
* **YouTube - Return YouTube Dislike:** Fix incorrect dislike counts after cancel ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([ad10d76](https://github.com/ReVanced/revanced-patches/commit/ad10d760354dba1e8f470972955a706da9b85c02))
|
||||
* **YouTube - ReturnYouTubeDislike:** Fix dislikes not showing with 20.31+ ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([2033883](https://github.com/ReVanced/revanced-patches/commit/203388329484616cc83aef2c3bda38a3069839ca))
|
||||
* **YouTube - Settings:** Icon not drawn correctly on some systems ([#6683](https://github.com/ReVanced/revanced-patches/issues/6683)) ([ddb6396](https://github.com/ReVanced/revanced-patches/commit/ddb6396b3f3f7a2c29b9fa171e189f9931ba0e02))
|
||||
* **YouTube - SponsorBlock:** Do not show context toast when auto skipping in feed ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([88157ac](https://github.com/ReVanced/revanced-patches/commit/88157ac5b791d4d56e8347203a02f5c78014235b))
|
||||
* **YouTube - SponsorBlock:** Resolve segments not fetching on experimental app targets ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([2067799](https://github.com/ReVanced/revanced-patches/commit/206779942d9b4e8131c4df1acb1e7eab63ec75a0))
|
||||
* **YouTube - SponsorBlock:** Show correct nested skip segment when seeking ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([f5ef68b](https://github.com/ReVanced/revanced-patches/commit/f5ef68b61a5880a574f6d0f06e4b96c00daf11bb))
|
||||
* **YouTube - Spoof app version:** Remove target `19.35.36` no longer supported by YouTube ([#6717](https://github.com/ReVanced/revanced-patches/issues/6717)) ([46fb366](https://github.com/ReVanced/revanced-patches/commit/46fb3669ee59534327d7c3d78e07b813d8a2badb))
|
||||
* **YouTube - Spoof video streams:** Make it work on 21.x ([#6705](https://github.com/ReVanced/revanced-patches/issues/6705)) ([fdfed3c](https://github.com/ReVanced/revanced-patches/commit/fdfed3c9dd46f477c1cc1b9db0f08054ffa32293))
|
||||
* **YouTube Music - Navigation bar:** Hide library tab with 8.24+ ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([cfcae43](https://github.com/ReVanced/revanced-patches/commit/cfcae434652b747345cb31b66748f0cc3220eb4a))
|
||||
* **YouTube Music:** Prevent crash on bold icons loading ([#6712](https://github.com/ReVanced/revanced-patches/issues/6712)) ([e9bfb7c](https://github.com/ReVanced/revanced-patches/commit/e9bfb7ca9bcd1499f1abe8872999aefff10cd187))
|
||||
* **YouTube:** Add back missing custom filter by adding the preference to the correct screen ([2a10489](https://github.com/ReVanced/revanced-patches/commit/2a10489a869cbab1ed01502bc6fe9330c4052e06))
|
||||
* **YouTube:** Change recommended version to 20.37.48 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([3dd305c](https://github.com/ReVanced/revanced-patches/commit/3dd305ca5d092144a924e150a668443b8f7ec3d8))
|
||||
* **YouTube:** Changes the default values for some settings ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([dce204b](https://github.com/ReVanced/revanced-patches/commit/dce204b41beb13b675d04afea3129df73a182172))
|
||||
* **YouTube:** Do not show bold icons if old settings menus is enabled ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([30bd852](https://github.com/ReVanced/revanced-patches/commit/30bd852ba5236ca25a7cc49fc23f987def27d23a))
|
||||
* **YouTube:** Fix patching unsupported 20.13.41 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([ed45375](https://github.com/ReVanced/revanced-patches/commit/ed453751057310a053600c4d50c87532a3f94989))
|
||||
* **YouTube:** Ignore cairo flag in debug flag manager ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([093497c](https://github.com/ReVanced/revanced-patches/commit/093497c34f7d6c431ce7958d6b0f85b9dd0373cd))
|
||||
* **YouTube:** Remove 19.43.41 that YouTube no longer supports ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([a8526dc](https://github.com/ReVanced/revanced-patches/commit/a8526dc8ae325b3b3d386ad1d23670b05a48da51))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add overlay buttons animation ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([f6fc6aa](https://github.com/ReVanced/revanced-patches/commit/f6fc6aa5ac6364dc2806e62618c300a8542b3cb0))
|
||||
* **Check environment patch:** Support another ReVanced Manager debug variant package name ([e4dea68](https://github.com/ReVanced/revanced-patches/commit/e4dea682c6640ce817d5e30cfddec953fe85436f))
|
||||
* **Custom branding:** Default to user-provided icon and name when provided ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([f19c35e](https://github.com/ReVanced/revanced-patches/commit/f19c35e21cc77e8f6f746f7f910d520f86981dd5))
|
||||
* **Enable debugging:** Allow overriding String/long/double flags in debug flag manager ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([1f91bc8](https://github.com/ReVanced/revanced-patches/commit/1f91bc8a20134c5519b8e031badfa741f7cac7a7))
|
||||
* **GMX Mail:** Add `Force enable Freephone` patch ([#6650](https://github.com/ReVanced/revanced-patches/issues/6650)) ([997b5d6](https://github.com/ReVanced/revanced-patches/commit/997b5d63d1fc1684bea9e5b265f3aca53ad5fd88))
|
||||
* **GMX Mail:** Add `Hide ads` and `Hide Premium upgrade button` patches ([#6583](https://github.com/ReVanced/revanced-patches/issues/6583)) ([2976ea3](https://github.com/ReVanced/revanced-patches/commit/2976ea3ddd09d26eeedf646f0a1020fa582d0ec0))
|
||||
* Handle multiple branch conditionals jumping to the same instruction index ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([2f7b57d](https://github.com/ReVanced/revanced-patches/commit/2f7b57d071d316985a1fec215045b6b78ede6212))
|
||||
* **Instagram:** Add `Disable Reels auto-scroll` patch ([#6736](https://github.com/ReVanced/revanced-patches/issues/6736)) ([806d6c7](https://github.com/ReVanced/revanced-patches/commit/806d6c799fb67c0fb630ae954ef615ff01597b1f))
|
||||
* Perform full search of free registers ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([01ef43a](https://github.com/ReVanced/revanced-patches/commit/01ef43ababdf015f1ad3edaf45445da0e72199f2))
|
||||
* **Photoshop Mix:** Add `Bypass login` patch ([#6745](https://github.com/ReVanced/revanced-patches/issues/6745)) ([24caae9](https://github.com/ReVanced/revanced-patches/commit/24caae98b7b4d61b388f644cc1512438e408e6b1))
|
||||
* Update YouTube & YouTube Music patches ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([88d33b8](https://github.com/ReVanced/revanced-patches/commit/88d33b847de4d2ad834a4940ee257e06e3c3ad31))
|
||||
* Use more informative patch error if the same APK is patched twice ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([26e5ce1](https://github.com/ReVanced/revanced-patches/commit/26e5ce1a325c2a6e78a5486d661f7750ecc792a3))
|
||||
* **YouTube - Disable haptic feedback:** Add Disable tap and hold haptics setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([f135122](https://github.com/ReVanced/revanced-patches/commit/f135122df1a5e6a8b822652abb2451ea4e4a3d08))
|
||||
* **YouTube - Hide ads:** Add Hide player popup ads setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([487a95d](https://github.com/ReVanced/revanced-patches/commit/487a95d3efa878d9b41f1b719924c5504e0a1d0a))
|
||||
* **YouTube - Hide layout components:** Add "Hide channel tab filter" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([0adcd8c](https://github.com/ReVanced/revanced-patches/commit/0adcd8c62e12619d5adaac5ee9886613deb53ca4))
|
||||
* **YouTube - Hide layout components:** Add "Hide collapse button" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([1554fd9](https://github.com/ReVanced/revanced-patches/commit/1554fd916d1bcc9c67319d55b21072423926fc32))
|
||||
* **YouTube - Hide layout components:** Add "Hide comments section in Home feed" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([5278434](https://github.com/ReVanced/revanced-patches/commit/5278434534653ea741e67cc1e5258abb7ca0e21e))
|
||||
* **YouTube - Hide layout components:** Add "Hide course progress" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([1927564](https://github.com/ReVanced/revanced-patches/commit/192756443a1b2ede413e2d4ae55eed2bd9d57aac))
|
||||
* **YouTube - Hide layout components:** Add "Hide explore this course" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([3e24762](https://github.com/ReVanced/revanced-patches/commit/3e24762c1847dfc467a5d6bf65cc1c3c0931ca0f))
|
||||
* **YouTube - Hide layout components:** Add "Hide featured links", "Hide featured videos", "Hide join button", and "Hide subscribe button" options ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([f9e843d](https://github.com/ReVanced/revanced-patches/commit/f9e843d75641d4a87dfbe05fa8fd407ccc0345d6))
|
||||
* **YouTube - Hide layout components:** Add "Hide feed flyout menu filter" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([a93de46](https://github.com/ReVanced/revanced-patches/commit/a93de46572a7bd1ff30a1fb653e3f7afb1c67571))
|
||||
* **YouTube - Hide layout components:** Add "Hide fullscreen button" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([b07b160](https://github.com/ReVanced/revanced-patches/commit/b07b1609e4bd9341611d6aa0194c9764616719b4))
|
||||
* **YouTube - Hide layout components:** Add "Hide latest videos button" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([ebfdd8d](https://github.com/ReVanced/revanced-patches/commit/ebfdd8df2c5323290f6e655ebf0dd1db683f33dd))
|
||||
* **YouTube - Hide layout components:** Add "Hide live chat replay button" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([a6bd311](https://github.com/ReVanced/revanced-patches/commit/a6bd3116f97e539482c752e8e4e1b1e8e90ed464))
|
||||
* **YouTube - Hide layout components:** Add "Hide quizzes" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([70b9e10](https://github.com/ReVanced/revanced-patches/commit/70b9e103aea817bed1d0972444c7b0726214c69c))
|
||||
* **YouTube - Hide layout components:** Add "Hide search box trending results" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([21bf455](https://github.com/ReVanced/revanced-patches/commit/21bf455c3f61e5fd19f97a1580ecb26ac40dcdce))
|
||||
* **YouTube - Hide layout components:** Add "Hide subscribed channels bar" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([e41a40f](https://github.com/ReVanced/revanced-patches/commit/e41a40f0d754397f9cea09f387cc901f0397787e))
|
||||
* **YouTube - Hide layout components:** Add "Hide video title" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([2cfbe08](https://github.com/ReVanced/revanced-patches/commit/2cfbe08b2137b2520dd37927202a4586af8326ff))
|
||||
* **YouTube - Hide layout components:** Apply hide search suggestions only to more recent app targets ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([a43c0e1](https://github.com/ReVanced/revanced-patches/commit/a43c0e111bfe290f7dec3c9b75b882ea9dc5630f))
|
||||
* **YouTube - Hide layout components:** Replace "Hide search suggestions" with "Hide You may like section" ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([80f6b01](https://github.com/ReVanced/revanced-patches/commit/80f6b01c64971881bb9144cada0e91bb78b9f38d))
|
||||
* **YouTube - Hide Shorts components:** Add "Hide AI button" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([99aace4](https://github.com/ReVanced/revanced-patches/commit/99aace4178ccc9aeaaeb0b19cd6f520c10ef7df2))
|
||||
* **YouTube - Hide Shorts components:** Add "Hide in video description" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([e0a8b7b](https://github.com/ReVanced/revanced-patches/commit/e0a8b7bc59113ce57e5b8b358bad9171a4ea1f99))
|
||||
* **YouTube - Navigation bar:** Add settings to hide toolbar buttons ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([d72e39f](https://github.com/ReVanced/revanced-patches/commit/d72e39f2a8fc0894667546826ef07cb3edf78e50))
|
||||
* **YouTube - Navigation buttons:** Add setting to use narrow navigation bar buttons ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([e48a5d7](https://github.com/ReVanced/revanced-patches/commit/e48a5d76f7651b0edcdb5a9b27e596df41e9c6af))
|
||||
* **YouTube - SponsorBlock:** Show skip button if player overlay controls are active ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([35ec655](https://github.com/ReVanced/revanced-patches/commit/35ec655f83ffe7ab661dca07107a74f2f9617037))
|
||||
* **YouTube - Theme:** Add "Hide splash screen" setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([ca6e184](https://github.com/ReVanced/revanced-patches/commit/ca6e184172e67cca48ea4c70cfe6371e806dd793))
|
||||
* **YouTube - Video quality:** Add Hide Premium video quality setting ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([50a2b67](https://github.com/ReVanced/revanced-patches/commit/50a2b67ef6e6382894636acdc1c2fcf7236ab4ee))
|
||||
* **YouTube Music:** Add experimental support for 9.02.50 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([50a102d](https://github.com/ReVanced/revanced-patches/commit/50a102d8afc573936f790991381b0a8d2f8dd54d))
|
||||
* **YouTube Music:** Add experimental support for 9.03.52 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([d5b9c0c](https://github.com/ReVanced/revanced-patches/commit/d5b9c0c03d334ff31c9601a48a3beb1a4db98310))
|
||||
* **YouTube Music:** Change recommended version to 8.37.56 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([d1e7900](https://github.com/ReVanced/revanced-patches/commit/d1e7900793ceef7b53b140ba9efe25025a8aac01))
|
||||
* **YouTube Music:** Support version 8.40.54 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([62f130c](https://github.com/ReVanced/revanced-patches/commit/62f130cc883d69d40c364cac45158012dd01272f))
|
||||
* **YouTube Music:** Unofficial support of 8.50.51 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([c1d7cae](https://github.com/ReVanced/revanced-patches/commit/c1d7caeee2cfa425769571b0ebff2da86e709ef9))
|
||||
* **YouTube:** Add experimental support for 21.02.32 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([7904b60](https://github.com/ReVanced/revanced-patches/commit/7904b60dbea526af45b4a69dc349c6250453b385))
|
||||
* **YouTube:** Add experimental support for 21.03.34 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([1ae36a1](https://github.com/ReVanced/revanced-patches/commit/1ae36a1cc72f0fb29d592206f74fcd40e37acaba))
|
||||
* **YouTube:** Add experimental support for 21.04.221 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([66e113a](https://github.com/ReVanced/revanced-patches/commit/66e113a96639d0c99126749125adf234a9b10cab))
|
||||
* **YouTube:** Add experimental support for 21.05.264 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([f646c82](https://github.com/ReVanced/revanced-patches/commit/f646c820d7d6027cf013e0968189a1e2cfd9e641))
|
||||
* **YouTube:** Add experimental support for 21.06.251 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([44b17d4](https://github.com/ReVanced/revanced-patches/commit/44b17d47588251b9fab5c801a49ace2ce371fa99))
|
||||
* **YouTube:** Add experimental support for 21.06.257 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([abb703d](https://github.com/ReVanced/revanced-patches/commit/abb703dcb2ac96f30e699a33d3a896b775bb0851))
|
||||
* **YouTube:** Add experimental support for 21.07.240 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([79b0c1f](https://github.com/ReVanced/revanced-patches/commit/79b0c1f72ff5b52b162f3f861d5e10c657efa097))
|
||||
* **YouTube:** Add Hide autoplay preview patch ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([79e3955](https://github.com/ReVanced/revanced-patches/commit/79e3955fde7068eac90ae404b3869c27f17bd5f7))
|
||||
* **YouTube:** Add more double tap to seek length options ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([fb04071](https://github.com/ReVanced/revanced-patches/commit/fb04071528683d38913c57f628cbab64bf0ef6a4))
|
||||
* **YouTube:** Remove obsolete seekbar thumbnail patch ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([9909fc1](https://github.com/ReVanced/revanced-patches/commit/9909fc1e5d490e9edb59894d66c6a929fbaebb3b))
|
||||
* **YouTube:** Support version 20.40.45 ([#6571](https://github.com/ReVanced/revanced-patches/issues/6571)) ([96c85d0](https://github.com/ReVanced/revanced-patches/commit/96c85d03712e79217dc8f97bcda5f38c0e47f064))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Deprecated APIs have been removed, and various APIs now use the updated ReVanced Patcher v22 APIs.
|
||||
|
||||
# [6.0.0-dev.26](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.25...v6.0.0-dev.26) (2026-03-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add minSdk to all extension projects ([#6778](https://github.com/ReVanced/revanced-patches/issues/6778)) ([7517f57](https://github.com/ReVanced/revanced-patches/commit/7517f57ac7a54e1c914e8dd8cc3e1aa908e28e54))
|
||||
|
||||
# [6.0.0-dev.25](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.24...v6.0.0-dev.25) (2026-03-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Instagram:** Add `Disable Reels auto-scroll` patch ([#6736](https://github.com/ReVanced/revanced-patches/issues/6736)) ([806d6c7](https://github.com/ReVanced/revanced-patches/commit/806d6c799fb67c0fb630ae954ef615ff01597b1f))
|
||||
|
||||
# [6.0.0-dev.24](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.23...v6.0.0-dev.24) (2026-03-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Photoshop Mix:** Add `Bypass login` patch ([#6745](https://github.com/ReVanced/revanced-patches/issues/6745)) ([24caae9](https://github.com/ReVanced/revanced-patches/commit/24caae98b7b4d61b388f644cc1512438e408e6b1))
|
||||
|
||||
# [6.0.0-dev.23](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.22...v6.0.0-dev.23) (2026-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ProtonVPN - Remove delay:** Make it work on latest version by patching the correct class ([#6757](https://github.com/ReVanced/revanced-patches/issues/6757)) ([e0dc009](https://github.com/ReVanced/revanced-patches/commit/e0dc009780afea9c2f393c4f348cda5ca9c3cbbf))
|
||||
|
||||
# [6.0.0-dev.22](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.21...v6.0.0-dev.22) (2026-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Settings:** Icon not drawn correctly on some systems ([#6683](https://github.com/ReVanced/revanced-patches/issues/6683)) ([ddb6396](https://github.com/ReVanced/revanced-patches/commit/ddb6396b3f3f7a2c29b9fa171e189f9931ba0e02))
|
||||
|
||||
# [6.0.0-dev.21](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.20...v6.0.0-dev.21) (2026-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Instagram:** Update fingerprints for version `417.0.0.54.77` ([#6734](https://github.com/ReVanced/revanced-patches/issues/6734)) ([55f510d](https://github.com/ReVanced/revanced-patches/commit/55f510dbedd28678411b4f11d9bbdd303fa68a0d))
|
||||
* **Spotify - Sanitize sharing links:** Update patch to latest app versions ([#6685](https://github.com/ReVanced/revanced-patches/issues/6685)) ([bb7448b](https://github.com/ReVanced/revanced-patches/commit/bb7448bc9d789843371d16bfccc9815662913333))
|
||||
|
||||
# [6.0.0-dev.20](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.19...v6.0.0-dev.20) (2026-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Enable Debugging Patch:** Use correct Protocolbuffer setting name ([#6711](https://github.com/ReVanced/revanced-patches/issues/6711)) ([f934022](https://github.com/ReVanced/revanced-patches/commit/f934022f37ba178ac23abfa9bcd59a0c12abe43f))
|
||||
|
||||
# [6.0.0-dev.19](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.18...v6.0.0-dev.19) (2026-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Hex:** Add back name, which was accidentally removed from the patch ([6a547a9](https://github.com/ReVanced/revanced-patches/commit/6a547a97e52b7914bb6602f3ecc2c6cecd50e946))
|
||||
|
||||
# [6.0.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.17...v6.0.0-dev.18) (2026-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide Shorts components:** Find resource id only for 21.05+ ([63161e9](https://github.com/ReVanced/revanced-patches/commit/63161e9fb357387685294e4a80de94cb351c6713))
|
||||
|
||||
# [6.0.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.16...v6.0.0-dev.17) (2026-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Make it work on 21.x ([#6705](https://github.com/ReVanced/revanced-patches/issues/6705)) ([fdfed3c](https://github.com/ReVanced/revanced-patches/commit/fdfed3c9dd46f477c1cc1b9db0f08054ffa32293))
|
||||
|
||||
# [6.0.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.15...v6.0.0-dev.16) (2026-03-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof app version:** Remove target `19.35.36` no longer supported by YouTube ([#6717](https://github.com/ReVanced/revanced-patches/issues/6717)) ([46fb366](https://github.com/ReVanced/revanced-patches/commit/46fb3669ee59534327d7c3d78e07b813d8a2badb))
|
||||
* **YouTube Music:** Prevent crash on bold icons loading ([#6712](https://github.com/ReVanced/revanced-patches/issues/6712)) ([e9bfb7c](https://github.com/ReVanced/revanced-patches/commit/e9bfb7ca9bcd1499f1abe8872999aefff10cd187))
|
||||
|
||||
# [6.0.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v6.0.0-dev.14...v6.0.0-dev.15) (2026-03-05)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
defaultConfig {
|
||||
minSdk = 23
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,10 @@ public class InternalDataDocumentsProvider extends DocumentsProvider {
|
|||
private static final String[] directoryColumns =
|
||||
{"document_id", "mime_type", "_display_name", "last_modified", "flags",
|
||||
"_size", "full_path", "lstat_info"};
|
||||
private static final int S_IFLNK = 0x8000;
|
||||
@SuppressWarnings("OctalInteger")
|
||||
private static final int S_IFMT = 0170000;
|
||||
@SuppressWarnings("OctalInteger")
|
||||
private static final int S_IFLNK = 0120000;
|
||||
|
||||
private String packageName;
|
||||
private File dataDirectory;
|
||||
|
|
@ -47,7 +50,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider {
|
|||
if (root.isDirectory()) {
|
||||
try {
|
||||
// Only delete recursively if the directory is not a symlink
|
||||
if ((Os.lstat(root.getPath()).st_mode & S_IFLNK) != S_IFLNK) {
|
||||
if ((Os.lstat(root.getPath()).st_mode & S_IFMT) != S_IFLNK) {
|
||||
File[] files = root.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
|
|
@ -324,7 +327,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider {
|
|||
sb.append(";");
|
||||
sb.append(lstat.st_gid);
|
||||
// Append symlink target if it is a symlink
|
||||
if ((lstat.st_mode & S_IFLNK) == S_IFLNK) {
|
||||
if ((lstat.st_mode & S_IFMT) == S_IFLNK) {
|
||||
sb.append(";");
|
||||
sb.append(Os.readlink(path));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,8 @@
|
|||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
aidl = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.extension.playintegrity;
|
||||
package app.revanced.extension.play;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
|
@ -1,14 +1,7 @@
|
|||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -3,3 +3,9 @@ dependencies {
|
|||
compileOnly(libs.annotation)
|
||||
compileOnly(libs.okhttp)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 22
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,3 +4,9 @@ dependencies {
|
|||
compileOnly(libs.annotation)
|
||||
compileOnly(libs.okhttp)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:cricbuzz:stub"))
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package app.revanced.extension.music;
|
||||
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
public class VersionCheckUtils {
|
||||
private static boolean isVersionOrGreater(String version) {
|
||||
return Utils.getAppVersionName().compareTo(version) >= 0;
|
||||
}
|
||||
|
||||
public static final boolean IS_8_40_OR_GREATER = isVersionOrGreater("8.40.00");
|
||||
}
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package app.revanced.extension.music.patches.spoof;
|
||||
|
||||
import static app.revanced.extension.music.settings.Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_NO_SDK;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_REEL;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.VISIONOS;
|
||||
|
|
@ -18,8 +18,8 @@ public class SpoofVideoStreamsPatch {
|
|||
*/
|
||||
public static void setClientOrderToUse() {
|
||||
List<ClientType> availableClients = List.of(
|
||||
ANDROID_REEL,
|
||||
ANDROID_VR_1_43_32,
|
||||
ANDROID_NO_SDK,
|
||||
VISIONOS,
|
||||
ANDROID_VR_1_61_48
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.preference.PreferenceFragment;
|
|||
import android.view.View;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import app.revanced.extension.music.VersionCheckUtils;
|
||||
import app.revanced.extension.music.settings.preference.MusicPreferenceFragment;
|
||||
import app.revanced.extension.music.settings.search.MusicSearchViewController;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
|
@ -23,6 +24,24 @@ public class MusicActivityHook extends BaseActivityHook {
|
|||
@SuppressLint("StaticFieldLeak")
|
||||
public static MusicSearchViewController searchViewController;
|
||||
|
||||
/**
|
||||
* How much time has passed since the first launch of the app. Simple check to prevent
|
||||
* forcing bold icons on first launch where the settings menu is partially broken
|
||||
* due to missing icon resources the client has not yet received.
|
||||
*
|
||||
* @see app.revanced.extension.youtube.settings.YouTubeActivityHook#MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS
|
||||
*/
|
||||
private static final long MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS = 30 * 1000; // 30 seconds.
|
||||
|
||||
static {
|
||||
final boolean useBoldIcons = VersionCheckUtils.IS_8_40_OR_GREATER
|
||||
&& !Settings.SETTINGS_DISABLE_BOLD_ICONS.get()
|
||||
&& (System.currentTimeMillis() - Settings.FIRST_TIME_APP_LAUNCHED.get())
|
||||
> MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS;
|
||||
|
||||
Utils.setAppIsUsingBoldIcons(useBoldIcons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
|
|
@ -116,4 +135,14 @@ public class MusicActivityHook extends BaseActivityHook {
|
|||
public static boolean handleFinish() {
|
||||
return MusicSearchViewController.handleFinish(searchViewController);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* <p>
|
||||
* Decides whether to use bold icons.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean useBoldIcons(boolean original) {
|
||||
return Utils.appIsUsingBoldIcons();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class Settings extends YouTubeAndMusicSettings {
|
|||
|
||||
// Miscellaneous
|
||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type",
|
||||
ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
ClientType.ANDROID_REEL, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
|
||||
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", TRUE, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ dependencies {
|
|||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
minSdk = 23
|
||||
}
|
||||
}
|
||||
|
|
@ -2,3 +2,9 @@ dependencies {
|
|||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:nunl:stub"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,3 +2,9 @@ dependencies {
|
|||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:primevideo:stub"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
dependencies {
|
||||
compileOnly(project(":extensions:reddit:stub"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 28
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,3 +2,9 @@ dependencies {
|
|||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:samsung:radio:stub"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@ dependencies {
|
|||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
minSdk = 23
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.android.library)
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
@ -19,4 +19,6 @@ android {
|
|||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
compileOnly(libs.okhttp)
|
||||
compileOnly(libs.protobuf.javalite)
|
||||
implementation(project(":extensions:shared:protobuf", configuration = "shadowRuntimeElements"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,11 +32,7 @@ import android.view.Window;
|
|||
import android.view.WindowManager;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
|
|
@ -65,7 +61,6 @@ import app.revanced.extension.shared.settings.BooleanSetting;
|
|||
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
|
||||
import app.revanced.extension.shared.ui.Dim;
|
||||
|
||||
@SuppressWarnings("NewApi")
|
||||
public class Utils {
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
|
|
@ -139,6 +134,7 @@ public class Utils {
|
|||
return versionName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static String getApplicationName() {
|
||||
if (applicationLabel == null) {
|
||||
try {
|
||||
|
|
@ -185,24 +181,13 @@ public class Utils {
|
|||
* @param view The view to hide.
|
||||
*/
|
||||
public static void hideViewBy0dp(View view) {
|
||||
if (view instanceof LinearLayout) {
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams);
|
||||
} else if (view instanceof FrameLayout) {
|
||||
FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams2);
|
||||
} else if (view instanceof RelativeLayout) {
|
||||
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams3);
|
||||
} else if (view instanceof Toolbar) {
|
||||
Toolbar.LayoutParams layoutParams4 = new Toolbar.LayoutParams(0, 0);
|
||||
view.setLayoutParams(layoutParams4);
|
||||
} else {
|
||||
ViewGroup.LayoutParams params = view.getLayoutParams();
|
||||
params.width = 0;
|
||||
params.height = 0;
|
||||
view.setLayoutParams(params);
|
||||
}
|
||||
ViewGroup.LayoutParams params = view.getLayoutParams();
|
||||
if (params == null)
|
||||
params = new ViewGroup.LayoutParams(0, 0);
|
||||
|
||||
params.width = 0;
|
||||
params.height = 0;
|
||||
view.setLayoutParams(params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -479,6 +464,7 @@ public class Utils {
|
|||
return str != null && !str.isEmpty();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean isTablet() {
|
||||
return context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
|
||||
}
|
||||
|
|
@ -518,6 +504,7 @@ public class Utils {
|
|||
return getTextDirectionString(isRightToLeftLocale());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static String getTextDirectionString(Locale locale) {
|
||||
return getTextDirectionString(isRightToLeftLocale(locale));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
|||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.shared.Utils.DialogFragmentOnStartAction;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.Html;
|
||||
import android.util.Pair;
|
||||
import android.view.Gravity;
|
||||
|
|
@ -19,6 +19,7 @@ import android.widget.ImageView;
|
|||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ import app.revanced.extension.shared.Utils;
|
|||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
import app.revanced.extension.shared.ui.CustomDialog;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
abstract class Check {
|
||||
private static final int NUMBER_OF_TIMES_TO_IGNORE_WARNING_BEFORE_DISABLING = 2;
|
||||
|
||||
|
|
@ -76,7 +78,6 @@ abstract class Check {
|
|||
BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.save(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
static void issueWarning(Activity activity, Collection<Check> failedChecks) {
|
||||
final var reasons = new StringBuilder();
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import android.util.Base64;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
|
@ -29,6 +31,7 @@ import static app.revanced.extension.shared.checks.PatchInfo.Build.*;
|
|||
* <br>
|
||||
* Various indicators help to detect if the app was patched by the user.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressWarnings("unused")
|
||||
public final class CheckEnvironmentPatch {
|
||||
private static final boolean DEBUG_ALWAYS_SHOW_CHECK_FAILED_DIALOG = debugAlwaysShowWarning();
|
||||
|
|
@ -121,7 +124,7 @@ public final class CheckEnvironmentPatch {
|
|||
* If the build properties are different, the app was likely downloaded pre-patched or patched on another device.
|
||||
*/
|
||||
private static class CheckWasPatchedOnSameDevice extends Check {
|
||||
@SuppressLint({"NewApi", "HardwareIds"})
|
||||
@SuppressLint("HardwareIds")
|
||||
@Override
|
||||
protected Boolean check() {
|
||||
if (PATCH_BOARD.isEmpty()) {
|
||||
|
|
@ -195,7 +198,7 @@ public final class CheckEnvironmentPatch {
|
|||
PackageManager packageManager = context.getPackageManager();
|
||||
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
|
||||
|
||||
// Duration since initial install or last update, which ever is sooner.
|
||||
// Duration since initial install or last update, whichever is sooner.
|
||||
durationBetweenPatchingAndInstallation = packageInfo.lastUpdateTime - PatchInfo.PATCH_TIME;
|
||||
Logger.printInfo(() -> "App was installed/updated: "
|
||||
+ (durationBetweenPatchingAndInstallation / (60 * 1000) + " minutes after patching"));
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ public class CustomBrandingPatch {
|
|||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* <p>
|
||||
* The total number of app name aliases, including dummy aliases.
|
||||
*/
|
||||
private static int numberOfPresetAppNames() {
|
||||
|
|
@ -146,13 +146,13 @@ public class CustomBrandingPatch {
|
|||
public static int getDefaultAppNameIndex() {
|
||||
return userProvidedCustomName()
|
||||
? numberOfPresetAppNames()
|
||||
: 1;
|
||||
: 2;
|
||||
}
|
||||
|
||||
public static BrandingTheme getDefaultIconStyle() {
|
||||
return userProvidedCustomIcon()
|
||||
? BrandingTheme.CUSTOM
|
||||
: BrandingTheme.ORIGINAL;
|
||||
: BrandingTheme.ROUNDED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public final class LithoFilterPatch {
|
|||
}
|
||||
builder.append(" Path: ");
|
||||
builder.append(path);
|
||||
if (YouTubeAndMusicSettings.DEBUG_PROTOBUFFER.get()) {
|
||||
if (YouTubeAndMusicSettings.DEBUG_PROTOCOLBUFFER.get()) {
|
||||
builder.append(" BufferStrings: ");
|
||||
findAsciiStrings(builder, buffer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,15 @@ import android.annotation.SuppressLint;
|
|||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.ResourceType;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
|
@ -22,7 +25,8 @@ import app.revanced.extension.shared.ui.Dim;
|
|||
* Base class for hooking activities to inject a custom PreferenceFragment with a toolbar.
|
||||
* Provides common logic for initializing the activity and setting up the toolbar.
|
||||
*/
|
||||
@SuppressWarnings({"deprecation", "NewApi"})
|
||||
@SuppressWarnings("deprecation")
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public abstract class BaseActivityHook extends Activity {
|
||||
|
||||
private static final int ID_REVANCED_SETTINGS_FRAGMENTS =
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public class BaseSettings {
|
|||
//
|
||||
|
||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
|
||||
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
|
||||
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_video_streams_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
|
||||
|
||||
public static final BooleanSetting SANITIZE_SHARING_LINKS = new BooleanSetting("revanced_sanitize_sharing_links", TRUE);
|
||||
public static final BooleanSetting REPLACE_MUSIC_LINKS_WITH_YOUTUBE = new BooleanSetting("revanced_replace_music_with_youtube", FALSE);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ public class YouTubeAndMusicSettings extends BaseSettings {
|
|||
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
||||
|
||||
// Miscellaneous
|
||||
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, false,
|
||||
"revanced_debug_protobuffer_user_dialog_message", parent(BaseSettings.DEBUG));
|
||||
public static final BooleanSetting DEBUG_PROTOCOLBUFFER = new BooleanSetting("revanced_debug_protocolbuffer", FALSE, false,
|
||||
"revanced_debug_protocolbuffer_user_dialog_message", parent(BaseSettings.DEBUG));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,15 +15,16 @@ import android.widget.TextView;
|
|||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.ResourceType;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BaseActivityHook;
|
||||
import app.revanced.extension.shared.ui.Dim;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
|
||||
@SuppressWarnings({"deprecation", "NewApi"})
|
||||
@SuppressWarnings("deprecation")
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public class ToolbarPreferenceFragment extends AbstractPreferenceFragment {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,9 +9,34 @@ import java.util.Locale;
|
|||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
@SuppressWarnings("ConstantLocale")
|
||||
public enum ClientType {
|
||||
/**
|
||||
* Video not playable: Paid, Movie, Private, Age-restricted.
|
||||
* Uses non-adaptive bitrate.
|
||||
* AV1 codec available.
|
||||
*/
|
||||
ANDROID_REEL(
|
||||
3,
|
||||
"ANDROID",
|
||||
"com.google.android.youtube",
|
||||
Build.MANUFACTURER,
|
||||
Build.MODEL,
|
||||
"Android",
|
||||
Build.VERSION.RELEASE,
|
||||
String.valueOf(Build.VERSION.SDK_INT),
|
||||
Build.ID,
|
||||
"20.44.38",
|
||||
// This client has been used by most open-source YouTube stream extraction tools since 2024, including NewPipe Extractor, SmartTube, and Grayjay.
|
||||
// This client can log in, but if an access token is used in the request, GVS can more easily identify the request as coming from ReVanced.
|
||||
// This means that the GVS server can strengthen its validation of the ANDROID_REEL client.
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
"Android Reel"
|
||||
),
|
||||
/**
|
||||
* Video not playable: Kids / Paid / Movie / Private / Age-restricted.
|
||||
* This client can only be used when logged out.
|
||||
|
|
@ -28,10 +53,10 @@ public enum ClientType {
|
|||
// Android 12.1
|
||||
"32",
|
||||
"SQ3A.220605.009.A1",
|
||||
"132.0.6808.3",
|
||||
"1.61.48",
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
"Android VR 1.61"
|
||||
),
|
||||
/**
|
||||
|
|
@ -48,39 +73,12 @@ public enum ClientType {
|
|||
ANDROID_VR_1_61_48.osVersion,
|
||||
Objects.requireNonNull(ANDROID_VR_1_61_48.androidSdkVersion),
|
||||
Objects.requireNonNull(ANDROID_VR_1_61_48.buildId),
|
||||
"107.0.5284.2",
|
||||
"1.43.32",
|
||||
ANDROID_VR_1_61_48.useAuth,
|
||||
ANDROID_VR_1_61_48.supportsMultiAudioTracks,
|
||||
ANDROID_VR_1_61_48.usePlayerEndpoint,
|
||||
"Android VR 1.43"
|
||||
),
|
||||
/**
|
||||
* Video not playable: Paid / Movie / Private / Age-restricted.
|
||||
* Note: The 'Authorization' key must be excluded from the header.
|
||||
*
|
||||
* According to TeamNewPipe in 2022, if the 'androidSdkVersion' field is missing,
|
||||
* the GVS did not return a valid response:
|
||||
* [NewPipe#8713 (comment)](https://github.com/TeamNewPipe/NewPipe/issues/8713#issuecomment-1207443550).
|
||||
*
|
||||
* According to the latest commit in yt-dlp, the GVS returns a valid response
|
||||
* even if the 'androidSdkVersion' field is missing:
|
||||
* [yt-dlp#14693](https://github.com/yt-dlp/yt-dlp/pull/14693).
|
||||
*
|
||||
* For some reason, PoToken is not required.
|
||||
*/
|
||||
ANDROID_NO_SDK(
|
||||
3,
|
||||
"ANDROID",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
Build.VERSION.RELEASE,
|
||||
"20.05.46",
|
||||
"com.google.android.youtube/20.05.46 (Linux; U; Android " + Build.VERSION.RELEASE + ") gzip",
|
||||
false,
|
||||
true,
|
||||
"Android No SDK"
|
||||
),
|
||||
/**
|
||||
* Cannot play livestreams and lacks HDR, but can play videos with music and labeled "for children".
|
||||
* <a href="https://dumps.tadiphone.dev/dumps/google/barbet">Google Pixel 9 Pro Fold</a>
|
||||
|
|
@ -95,10 +93,10 @@ public enum ClientType {
|
|||
"15",
|
||||
"35",
|
||||
"AP3A.241005.015.A2",
|
||||
"132.0.6779.0",
|
||||
"23.47.101",
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
"Android Studio"
|
||||
),
|
||||
/**
|
||||
|
|
@ -114,32 +112,8 @@ public enum ClientType {
|
|||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15",
|
||||
false,
|
||||
false,
|
||||
"visionOS"
|
||||
),
|
||||
/**
|
||||
* The device machine id for the iPad 6th Gen (iPad7,6).
|
||||
* AV1 hardware decoding is not supported.
|
||||
* See [this GitHub Gist](https://gist.github.com/adamawolf/3048717) for more information.
|
||||
*
|
||||
* Based on Google's actions to date, PoToken may not be required on devices with very low specs.
|
||||
* For example, suppose the User-Agent for a PlayStation 3 (with 256MB of RAM) is used.
|
||||
* Accessing 'Web' (https://www.youtube.com) will redirect to 'TV' (https://www.youtube.com/tv).
|
||||
* 'TV' target devices with very low specs, such as embedded devices, game consoles, and blu-ray players, so PoToken is not required.
|
||||
*
|
||||
* For this reason, the device machine id for the iPad 6th Gen (with 2GB of RAM),
|
||||
* the lowest spec device capable of running iPadOS 17, was used.
|
||||
*/
|
||||
IPADOS(5,
|
||||
"IOS",
|
||||
"Apple",
|
||||
"iPad7,6",
|
||||
"iPadOS",
|
||||
"17.7.10.21H450",
|
||||
"19.22.3",
|
||||
"com.google.ios.youtube/19.22.3 (iPad7,6; U; CPU iPadOS 17_7_10 like Mac OS X; " + Locale.getDefault() + ")",
|
||||
false,
|
||||
true,
|
||||
"iPadOS"
|
||||
"visionOS"
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
@ -195,13 +169,6 @@ public enum ClientType {
|
|||
@Nullable
|
||||
private final String buildId;
|
||||
|
||||
/**
|
||||
* Cronet release version, as found in decompiled client apk.
|
||||
* Field is null if not applicable.
|
||||
*/
|
||||
@Nullable
|
||||
private final String cronetVersion;
|
||||
|
||||
/**
|
||||
* App version.
|
||||
*/
|
||||
|
|
@ -217,6 +184,11 @@ public enum ClientType {
|
|||
*/
|
||||
public final boolean supportsMultiAudioTracks;
|
||||
|
||||
/**
|
||||
* If the client should use the player endpoint for stream extraction.
|
||||
*/
|
||||
public final boolean usePlayerEndpoint;
|
||||
|
||||
/**
|
||||
* Friendly name displayed in stats for nerds.
|
||||
*/
|
||||
|
|
@ -234,10 +206,10 @@ public enum ClientType {
|
|||
String osVersion,
|
||||
@NonNull String androidSdkVersion,
|
||||
@NonNull String buildId,
|
||||
@NonNull String cronetVersion,
|
||||
String clientVersion,
|
||||
boolean useAuth,
|
||||
boolean supportsMultiAudioTracks,
|
||||
boolean usePlayerEndpoint,
|
||||
String friendlyName) {
|
||||
this.id = id;
|
||||
this.clientName = clientName;
|
||||
|
|
@ -248,21 +220,20 @@ public enum ClientType {
|
|||
this.osVersion = osVersion;
|
||||
this.androidSdkVersion = androidSdkVersion;
|
||||
this.buildId = buildId;
|
||||
this.cronetVersion = cronetVersion;
|
||||
this.clientVersion = clientVersion;
|
||||
this.useAuth = useAuth;
|
||||
this.supportsMultiAudioTracks = supportsMultiAudioTracks;
|
||||
this.usePlayerEndpoint = usePlayerEndpoint;
|
||||
this.friendlyName = friendlyName;
|
||||
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)",
|
||||
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s)",
|
||||
packageName,
|
||||
clientVersion,
|
||||
osVersion,
|
||||
defaultLocale,
|
||||
deviceModel,
|
||||
Objects.requireNonNull(buildId),
|
||||
Objects.requireNonNull(cronetVersion)
|
||||
buildId
|
||||
);
|
||||
Logger.printDebug(() -> "userAgent: " + this.userAgent);
|
||||
}
|
||||
|
|
@ -278,6 +249,7 @@ public enum ClientType {
|
|||
String userAgent,
|
||||
boolean useAuth,
|
||||
boolean supportsMultiAudioTracks,
|
||||
boolean usePlayerEndpoint,
|
||||
String friendlyName) {
|
||||
this.id = id;
|
||||
this.clientName = clientName;
|
||||
|
|
@ -289,10 +261,10 @@ public enum ClientType {
|
|||
this.userAgent = userAgent;
|
||||
this.useAuth = useAuth;
|
||||
this.supportsMultiAudioTracks = supportsMultiAudioTracks;
|
||||
this.usePlayerEndpoint = usePlayerEndpoint;
|
||||
this.friendlyName = friendlyName;
|
||||
this.packageName = null;
|
||||
this.androidSdkVersion = null;
|
||||
this.buildId = null;
|
||||
this.cronetVersion = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import android.text.TextUtils;
|
|||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
|
@ -39,7 +38,7 @@ public class SpoofVideoStreamsPatch {
|
|||
@Nullable
|
||||
private static volatile AppLanguage languageOverride;
|
||||
|
||||
private static volatile ClientType preferredClient = ClientType.ANDROID_VR_1_43_32;
|
||||
private static volatile ClientType preferredClient = ClientType.ANDROID_REEL;
|
||||
|
||||
/**
|
||||
* @return If this patch was included during patching.
|
||||
|
|
@ -250,7 +249,7 @@ public class SpoofVideoStreamsPatch {
|
|||
* Called after {@link #fetchStreams(String, Map)}.
|
||||
*/
|
||||
@Nullable
|
||||
public static ByteBuffer getStreamingData(String videoId) {
|
||||
public static byte[] getStreamingData(String videoId) {
|
||||
if (SPOOF_STREAMING_DATA) {
|
||||
try {
|
||||
StreamingDataRequest request = StreamingDataRequest.getRequestForVideoId(videoId);
|
||||
|
|
|
|||
|
|
@ -15,13 +15,20 @@ import app.revanced.extension.shared.spoof.ClientType;
|
|||
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||
|
||||
final class PlayerRoutes {
|
||||
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
|
||||
static final Route.CompiledRoute GET_PLAYER_STREAMING_DATA = new Route(
|
||||
Route.Method.POST,
|
||||
"player" +
|
||||
"?fields=streamingData" +
|
||||
"&alt=proto"
|
||||
).compile();
|
||||
|
||||
static final Route.CompiledRoute GET_REEL_STREAMING_DATA = new Route(
|
||||
Route.Method.POST,
|
||||
"reel/reel_item_watch" +
|
||||
"?fields=playerResponse.playabilityStatus,playerResponse.streamingData" +
|
||||
"&alt=proto"
|
||||
).compile();
|
||||
|
||||
private static final String YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/";
|
||||
|
||||
/**
|
||||
|
|
@ -47,6 +54,7 @@ final class PlayerRoutes {
|
|||
Locale streamLocale = language.getLocale();
|
||||
|
||||
JSONObject client = new JSONObject();
|
||||
|
||||
client.put("deviceMake", clientType.deviceMake);
|
||||
client.put("deviceModel", clientType.deviceModel);
|
||||
client.put("clientName", clientType.clientName);
|
||||
|
|
@ -61,9 +69,19 @@ final class PlayerRoutes {
|
|||
context.put("client", client);
|
||||
|
||||
innerTubeBody.put("context", context);
|
||||
innerTubeBody.put("contentCheckOk", true);
|
||||
innerTubeBody.put("racyCheckOk", true);
|
||||
innerTubeBody.put("videoId", videoId);
|
||||
|
||||
if (clientType.usePlayerEndpoint) {
|
||||
innerTubeBody.put("contentCheckOk", true);
|
||||
innerTubeBody.put("racyCheckOk", true);
|
||||
innerTubeBody.put("videoId", videoId);
|
||||
} else {
|
||||
JSONObject playerRequest = new JSONObject();
|
||||
playerRequest.put("contentCheckOk", true);
|
||||
playerRequest.put("racyCheckOk", true);
|
||||
playerRequest.put("videoId", videoId);
|
||||
innerTubeBody.put("playerRequest", playerRequest);
|
||||
innerTubeBody.put("disablePlayerResponse", false);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Logger.printException(() -> "Failed to create innerTubeBody", e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
package app.revanced.extension.shared.spoof.requests;
|
||||
|
||||
import static app.revanced.extension.shared.ByteTrieSearch.convertStringsToBytes;
|
||||
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
||||
import static app.revanced.extension.shared.Utils.isNotEmpty;
|
||||
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_PLAYER_STREAMING_DATA;
|
||||
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_REEL_STREAMING_DATA;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
|
@ -27,6 +26,11 @@ import java.util.concurrent.TimeoutException;
|
|||
import app.revanced.extension.shared.ByteTrieSearch;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.innertube.PlayerResponseOuterClass;
|
||||
import app.revanced.extension.shared.innertube.PlayerResponseOuterClass.PlayerResponse;
|
||||
import app.revanced.extension.shared.innertube.PlayerResponseOuterClass.StreamingData;
|
||||
import app.revanced.extension.shared.innertube.ReelItemWatchResponseOuterClass.ReelItemWatchResponse;
|
||||
import app.revanced.extension.shared.requests.Route;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
import app.revanced.extension.shared.spoof.ClientType;
|
||||
|
||||
|
|
@ -41,7 +45,7 @@ import app.revanced.extension.shared.spoof.ClientType;
|
|||
*/
|
||||
public class StreamingDataRequest {
|
||||
|
||||
private static volatile ClientType[] clientOrderToUse = ClientType.values();
|
||||
private static volatile ClientType[] clientOrderToUse = ClientType.values();
|
||||
|
||||
public static void setClientOrderToUse(List<ClientType> availableClients, ClientType preferredClient) {
|
||||
Objects.requireNonNull(preferredClient);
|
||||
|
|
@ -111,7 +115,7 @@ public class StreamingDataRequest {
|
|||
|
||||
private final String videoId;
|
||||
|
||||
private final Future<ByteBuffer> future;
|
||||
private final Future<byte[]> future;
|
||||
|
||||
private StreamingDataRequest(String videoId, Map<String, String> playerHeaders) {
|
||||
Objects.requireNonNull(playerHeaders);
|
||||
|
|
@ -134,6 +138,12 @@ public class StreamingDataRequest {
|
|||
Logger.printInfo(() -> toastMessage, ex);
|
||||
}
|
||||
|
||||
private static void handleDebugToast(String toastMessage, ClientType clientType) {
|
||||
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
|
||||
Utils.showToastShort(String.format(toastMessage, clientType));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static HttpURLConnection send(ClientType clientType,
|
||||
String videoId,
|
||||
|
|
@ -146,7 +156,10 @@ public class StreamingDataRequest {
|
|||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
|
||||
Route.CompiledRoute route = clientType.usePlayerEndpoint ?
|
||||
GET_PLAYER_STREAMING_DATA : GET_REEL_STREAMING_DATA;
|
||||
|
||||
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(route, clientType);
|
||||
connection.setConnectTimeout(HTTP_TIMEOUT_MILLISECONDS);
|
||||
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
|
||||
|
||||
|
|
@ -203,7 +216,7 @@ public class StreamingDataRequest {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static ByteBuffer fetch(String videoId, Map<String, String> playerHeaders) {
|
||||
private static byte[] fetch(String videoId, Map<String, String> playerHeaders) {
|
||||
final boolean debugEnabled = BaseSettings.DEBUG.get();
|
||||
|
||||
// Retry with different client if empty response body is received.
|
||||
|
|
@ -214,33 +227,11 @@ public class StreamingDataRequest {
|
|||
|
||||
HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast);
|
||||
if (connection != null) {
|
||||
try {
|
||||
// gzip encoding doesn't response with content length (-1),
|
||||
// but empty response body does.
|
||||
if (connection.getContentLength() == 0) {
|
||||
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
|
||||
Utils.showToastShort("Debug: Ignoring empty spoof stream client " + clientType);
|
||||
}
|
||||
} else {
|
||||
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
byte[] playerResponseBuffer = buildPlayerResponseBuffer(clientType, connection);
|
||||
if (playerResponseBuffer != null) {
|
||||
lastSpoofedClientType = clientType;
|
||||
|
||||
byte[] buffer = new byte[2048];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) >= 0) {
|
||||
baos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
if (clientType == ClientType.ANDROID_CREATOR && liveStreamBufferSearch.matches(buffer)) {
|
||||
Logger.printDebug(() -> "Skipping Android Studio as video is a livestream: " + videoId);
|
||||
} else {
|
||||
lastSpoofedClientType = clientType;
|
||||
|
||||
return ByteBuffer.wrap(baos.toByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.printException(() -> "Fetch failed while processing response data", ex);
|
||||
return playerResponseBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -250,12 +241,61 @@ public class StreamingDataRequest {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] buildPlayerResponseBuffer(ClientType clientType,
|
||||
HttpURLConnection connection) {
|
||||
// gzip encoding doesn't response with content length (-1),
|
||||
// but empty response body does.
|
||||
if (connection.getContentLength() == 0) {
|
||||
handleDebugToast("Debug: Ignoring empty spoof stream client (%s)", clientType);
|
||||
return null;
|
||||
}
|
||||
|
||||
try (InputStream inputStream = connection.getInputStream()) {
|
||||
PlayerResponse playerResponse = clientType.usePlayerEndpoint
|
||||
? PlayerResponse.parseFrom(inputStream)
|
||||
: ReelItemWatchResponse.parseFrom(inputStream).getPlayerResponse();
|
||||
|
||||
var playabilityStatus = playerResponse.getPlayabilityStatus();
|
||||
if (playabilityStatus.getStatus() != PlayerResponseOuterClass.Status.OK) {
|
||||
handleDebugToast("Debug: Ignoring unplayable video (%s)", clientType);
|
||||
String reason = playabilityStatus.getReason();
|
||||
if (isNotEmpty(reason)) {
|
||||
Logger.printDebug(() -> String.format("Debug: Ignoring unplayable video (%s), reason: %s", clientType, reason));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
PlayerResponse.Builder responseBuilder = playerResponse.toBuilder();
|
||||
if (!playerResponse.hasStreamingData()) {
|
||||
handleDebugToast("Debug: Ignoring empty streaming data (%s)", clientType);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Android Studio only supports the HLS protocol for live streams.
|
||||
// HLS protocol can theoretically be played with ExoPlayer,
|
||||
// but the related code has not yet been implemented.
|
||||
// If DASH protocol is not available, the client will be skipped.
|
||||
StreamingData streamingData = playerResponse.getStreamingData();
|
||||
if (streamingData.getAdaptiveFormatsCount() == 0) {
|
||||
handleDebugToast("Debug: Ignoring empty adaptiveFormat (%s)", clientType);
|
||||
return null;
|
||||
}
|
||||
|
||||
return responseBuilder.build().toByteArray();
|
||||
} catch (IOException ex) {
|
||||
Logger.printException(() -> "Failed to write player response to buffer array", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean fetchCompleted() {
|
||||
return future.isDone();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ByteBuffer getStream() {
|
||||
public byte[] getStream() {
|
||||
try {
|
||||
return future.get(MAX_MILLISECONDS_TO_WAIT_FOR_FETCH, TimeUnit.MILLISECONDS);
|
||||
} catch (TimeoutException ex) {
|
||||
|
|
|
|||
55
extensions/shared/protobuf/build.gradle.kts
Normal file
55
extensions/shared/protobuf/build.gradle.kts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
alias(libs.plugins.protobuf)
|
||||
alias(libs.plugins.shadow)
|
||||
}
|
||||
|
||||
val shade: Configuration by configurations.creating {
|
||||
configurations.getByName("compileClasspath").extendsFrom(this)
|
||||
configurations.getByName("runtimeClasspath").extendsFrom(this)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
compileOnly(libs.okhttp)
|
||||
shade(libs.protobuf.javalite)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
// Make sure generated proto sources are compiled and end up in the shaded jar
|
||||
main {
|
||||
java.srcDir("$buildDir/generated/source/proto/main/java")
|
||||
}
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = libs.protobuf.protoc.get().toString()
|
||||
}
|
||||
|
||||
generateProtoTasks {
|
||||
all().forEach { task ->
|
||||
task.builtins {
|
||||
named("java") {
|
||||
option("lite")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val shadowJar = tasks.named<ShadowJar>("shadowJar") {
|
||||
configurations = listOf(shade)
|
||||
relocate("com.google.protobuf", "app.revanced.com.google.protobuf")
|
||||
}
|
||||
|
||||
configurations.named("runtimeElements") {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
|
||||
outgoing.artifacts.clear()
|
||||
outgoing.artifact(shadowJar)
|
||||
}!!.let { artifacts { add(it.name, shadowJar) } }
|
||||
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package app.revanced.extension.shared.innertube;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
option java_package = "app.revanced.extension.shared.innertube";
|
||||
|
||||
message PlayerResponse {
|
||||
oneof data {
|
||||
PlayabilityStatus playability_status = 2;
|
||||
StreamingData streaming_data = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message PlayabilityStatus {
|
||||
Status status = 1;
|
||||
string reason = 2;
|
||||
}
|
||||
|
||||
enum Status {
|
||||
OK = 0;
|
||||
ERROR = 1;
|
||||
UNPLAYABLE = 2;
|
||||
LOGIN_REQUIRED = 3;
|
||||
CONTENT_CHECK_REQUIRED = 4;
|
||||
AGE_CHECK_REQUIRED = 5;
|
||||
LIVE_STREAM_OFFLINE = 6;
|
||||
FULLSCREEN_ONLY = 7;
|
||||
GL_PLAYBACK_REQUIRED = 8;
|
||||
AGE_VERIFICATION_REQUIRED = 9;
|
||||
}
|
||||
|
||||
message StreamingData {
|
||||
repeated Format formats = 2;
|
||||
repeated Format adaptiveFormats = 3;
|
||||
string serverAbrStreamingUrl = 15;
|
||||
}
|
||||
|
||||
message Format {
|
||||
string url = 2;
|
||||
string signatureCipher = 48;
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
syntax = "proto3";
|
||||
|
||||
import "app/revanced/extension/shared/innertube/player_response.proto";
|
||||
|
||||
package app.revanced.extension.shared.innertube;
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
option java_package = "app.revanced.extension.shared.innertube";
|
||||
|
||||
message ReelItemWatchResponse {
|
||||
oneof data {
|
||||
PlayerResponse player_response = 4;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,11 +6,6 @@ dependencies {
|
|||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
minSdk = 24
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ android {
|
|||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,3 +3,9 @@ dependencies {
|
|||
compileOnly(project(":extensions:strava:stub"))
|
||||
compileOnly(libs.okhttp)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package app.revanced.extension.strava;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
|
|
@ -28,7 +26,6 @@ import java.util.stream.Stream;
|
|||
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public final class AddMediaDownloadPatch {
|
||||
public static final int ACTION_DOWNLOAD = -1;
|
||||
public static final int ACTION_OPEN_LINK = -2;
|
||||
|
|
@ -85,7 +82,7 @@ public final class AddMediaDownloadPatch {
|
|||
} finally {
|
||||
values.clear();
|
||||
values.put(MediaStore.Images.Media.IS_PENDING, 0);
|
||||
resolver.update(row, values, null);
|
||||
resolver.update(row, values, null, null);
|
||||
}
|
||||
showInfoToast("yis_2024_local_save_image_success", "✔️");
|
||||
} catch (IOException e) {
|
||||
|
|
@ -151,7 +148,7 @@ public final class AddMediaDownloadPatch {
|
|||
} finally {
|
||||
values.clear();
|
||||
values.put(MediaStore.Video.Media.IS_PENDING, 0);
|
||||
resolver.update(row, values, null);
|
||||
resolver.update(row, values, null, null);
|
||||
}
|
||||
showInfoToast("yis_2024_local_save_video_success", "✔️");
|
||||
} catch (IOException e) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package app.revanced.extension.strava;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import com.strava.modularframework.data.Destination;
|
||||
import com.strava.modularframework.data.GenericLayoutModule;
|
||||
import com.strava.modularframework.data.GenericModuleField;
|
||||
|
|
@ -21,7 +19,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public class HideDistractionsPatch {
|
||||
public static boolean upselling;
|
||||
public static boolean promo;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ android {
|
|||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,3 +4,9 @@ dependencies {
|
|||
compileOnly(libs.annotation)
|
||||
compileOnly(libs.okhttp)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 23
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,4 @@ android {
|
|||
defaultConfig {
|
||||
minSdk = 22
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import java.lang.reflect.InvocationTargetException;
|
|||
/**
|
||||
* Hooks AdPersonalizationActivity to inject a custom {@link TikTokPreferenceFragment}.
|
||||
*/
|
||||
@SuppressWarnings({"deprecation", "NewApi", "unused"})
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class TikTokActivityHook {
|
||||
public static Object createSettingsEntry(String entryClazzName, String entryInfoClazzName) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
dependencies {
|
||||
compileOnly(libs.appcompat)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 22
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,4 @@ android {
|
|||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import tv.twitch.android.settings.SettingsActivity;
|
|||
/**
|
||||
* Hooks AppCompatActivity to inject a custom {@link TwitchPreferenceFragment}.
|
||||
*/
|
||||
@SuppressWarnings({"deprecation", "NewApi", "unused"})
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class TwitchActivityHook {
|
||||
private static final int REVANCED_SETTINGS_MENU_ITEM_ID = 0x7;
|
||||
private static final String EXTRA_REVANCED_SETTINGS = "app.revanced.twitch.settings";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,8 +238,8 @@ public final class AlternativeThumbnailsPatch {
|
|||
// See https://github.com/ajayyy/DeArrowThumbnailCache/blob/29eb4359ebdf823626c79d944a901492d760bbbc/app.py#L29.
|
||||
return dearrowAPIURI
|
||||
.buildUpon()
|
||||
.appendQueryParameter("videoId", videoId)
|
||||
.appendQueryParameter("redirectURL", fallbackURL)
|
||||
.appendQueryParameter("videoID", videoId)
|
||||
.appendQueryParameter("redirectUrl", fallbackURL)
|
||||
.build()
|
||||
.toString();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import android.widget.ImageView;
|
||||
import android.view.View;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
|
@ -46,7 +46,7 @@ public class ExitFullscreenPatch {
|
|||
// To fix this, push the perform click to the back fo the main thread,
|
||||
// and by then the overlay controls will be visible since the video is now finished.
|
||||
Utils.runOnMainThreadDelayed(() -> {
|
||||
ImageView button = PlayerControlsPatch.fullscreenButtonRef.get();
|
||||
View button = PlayerControlsPatch.fullscreenButtonRef.get();
|
||||
if (button == null) {
|
||||
Logger.printDebug(() -> "Fullscreen button is null, cannot click");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -109,13 +109,13 @@ public final class HidePlayerOverlayButtonsPatch {
|
|||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static ImageView hideFullscreenButton(ImageView imageView) {
|
||||
public static View hideFullscreenButton(View view) {
|
||||
if (!Settings.HIDE_FULLSCREEN_BUTTON.get()) {
|
||||
return imageView;
|
||||
return view;
|
||||
}
|
||||
|
||||
if (imageView != null) {
|
||||
imageView.setVisibility(View.GONE);
|
||||
if (view != null) {
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package app.revanced.extension.youtube.patches;
|
|||
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
|
|
@ -11,7 +10,7 @@ import app.revanced.extension.shared.Logger;
|
|||
@SuppressWarnings("unused")
|
||||
public class PlayerControlsPatch {
|
||||
|
||||
public static WeakReference<ImageView> fullscreenButtonRef = new WeakReference<>(null);
|
||||
public static WeakReference<View> fullscreenButtonRef = new WeakReference<>(null);
|
||||
|
||||
private static boolean fullscreenButtonVisibilityCallbacksExist() {
|
||||
return false; // Modified during patching if needed.
|
||||
|
|
@ -20,8 +19,8 @@ public class PlayerControlsPatch {
|
|||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void setFullscreenCloseButton(ImageView imageButton) {
|
||||
fullscreenButtonRef = new WeakReference<>(imageButton);
|
||||
public static void setFullscreenCloseButton(View button) {
|
||||
fullscreenButtonRef = new WeakReference<>(button);
|
||||
Logger.printDebug(() -> "Fullscreen button set");
|
||||
|
||||
if (!fullscreenButtonVisibilityCallbacksExist()) {
|
||||
|
|
@ -30,13 +29,13 @@ public class PlayerControlsPatch {
|
|||
|
||||
// Add a global listener, since the protected method
|
||||
// View#onVisibilityChanged() does not have any call backs.
|
||||
imageButton.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
button.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
int lastVisibility = View.VISIBLE;
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
try {
|
||||
final int visibility = imageButton.getVisibility();
|
||||
final int visibility = button.getVisibility();
|
||||
if (lastVisibility != visibility) {
|
||||
lastVisibility = visibility;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ import android.widget.LinearLayout;
|
|||
import android.widget.TextView;
|
||||
|
||||
import app.revanced.extension.shared.ui.CustomDialog;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
|
|
@ -29,9 +31,20 @@ import app.revanced.extension.youtube.settings.Settings;
|
|||
|
||||
@SuppressWarnings("unused")
|
||||
public final class AnnouncementsPatch {
|
||||
private static final String[] ANNOUNCEMENT_TAGS = {
|
||||
"\uD83C\uDF9E\uFE0F YouTube",
|
||||
};
|
||||
|
||||
private AnnouncementsPatch() {
|
||||
}
|
||||
|
||||
private static boolean hasSupportedTag(String wrapperTag) {
|
||||
if (wrapperTag == null) return false;
|
||||
for (var tag : ANNOUNCEMENT_TAGS) if (tag.equals(wrapperTag)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isLatestAlready() throws IOException {
|
||||
HttpURLConnection connection =
|
||||
AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS);
|
||||
|
|
@ -56,19 +69,30 @@ public final class AnnouncementsPatch {
|
|||
|
||||
var jsonString = Requester.parseStringAndDisconnect(connection);
|
||||
|
||||
// Parse the ID. Fall-back to raw string if it fails.
|
||||
int id = Settings.ANNOUNCEMENT_LAST_ID.defaultValue;
|
||||
var id = -1;
|
||||
try {
|
||||
final var announcementIDs = new JSONArray(jsonString);
|
||||
if (announcementIDs.length() == 0) return true;
|
||||
|
||||
id = announcementIDs.getJSONObject(0).getInt("id");
|
||||
final var announcementIDTagPairs = new JSONArray(jsonString);
|
||||
if (announcementIDTagPairs.length() == 0) return true;
|
||||
|
||||
JSONObject latest = null;
|
||||
for (int i = 0, entryCount = announcementIDTagPairs.length(); i < entryCount; i++) {
|
||||
var pair = announcementIDTagPairs.optJSONObject(i);
|
||||
if (pair != null && hasSupportedTag(pair.optString("tag", null))) {
|
||||
latest = pair;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (latest == null || latest.isNull("id")) return true;
|
||||
|
||||
id = latest.getInt("id");
|
||||
} catch (Throwable ex) {
|
||||
Logger.printException(() -> "Failed to parse announcement ID", ex);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do not show the announcement, if the last announcement id is the same as the current one.
|
||||
return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
|
||||
return Settings.ANNOUNCEMENT_LAST_ID.get().equals(id);
|
||||
}
|
||||
|
||||
public static void showAnnouncement(final Activity context) {
|
||||
|
|
@ -95,7 +119,22 @@ public final class AnnouncementsPatch {
|
|||
LocalDateTime archivedAt = LocalDateTime.MAX;
|
||||
Level level = Level.INFO;
|
||||
try {
|
||||
final var announcement = new JSONArray(jsonString).getJSONObject(0);
|
||||
final var announcements = new JSONArray(jsonString);
|
||||
JSONObject latestAnnouncement = null;
|
||||
for (int i = 0, entryCount = announcements.length(); i < entryCount; i++) {
|
||||
var announcementTagPair = announcements.optJSONObject(i);
|
||||
if (announcementTagPair != null && hasSupportedTag(announcementTagPair.optString("tag", null))) {
|
||||
latestAnnouncement = announcementTagPair;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (latestAnnouncement == null || latestAnnouncement.isNull("announcement")) {
|
||||
Logger.printDebug(() -> "No YouTube announcement found in latest announcements response");
|
||||
return;
|
||||
}
|
||||
|
||||
final var announcement = latestAnnouncement.getJSONObject("announcement");
|
||||
|
||||
id = announcement.getInt("id");
|
||||
title = announcement.getString("title");
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import java.net.HttpURLConnection;
|
|||
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
||||
|
||||
public class AnnouncementsRoutes {
|
||||
private static final String ANNOUNCEMENTS_PROVIDER = "https://api.revanced.app/v4";
|
||||
public static final Route GET_LATEST_ANNOUNCEMENT_IDS = new Route(GET, "/announcements/latest/id?tag=\uD83C\uDF9E\uFE0F%20YouTube");
|
||||
public static final Route GET_LATEST_ANNOUNCEMENTS = new Route(GET, "/announcements/latest?tag=\uD83C\uDF9E\uFE0F%20YouTube");
|
||||
private static final String ANNOUNCEMENTS_PROVIDER = "https://api.revanced.app/v5";
|
||||
public static final Route GET_LATEST_ANNOUNCEMENT_IDS = new Route(GET, "/announcements/latest/id");
|
||||
public static final Route GET_LATEST_ANNOUNCEMENTS = new Route(GET, "/announcements/latest");
|
||||
|
||||
private AnnouncementsRoutes() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
package app.revanced.extension.youtube.patches.spoof;
|
||||
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_CREATOR;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_NO_SDK;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.IPADOS;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_REEL;
|
||||
import static app.revanced.extension.shared.spoof.ClientType.VISIONOS;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -44,11 +43,11 @@ public class SpoofVideoStreamsPatch {
|
|||
}
|
||||
|
||||
List<ClientType> availableClients = List.of(
|
||||
VISIONOS,
|
||||
ANDROID_CREATOR,
|
||||
ANDROID_REEL,
|
||||
ANDROID_VR_1_43_32,
|
||||
ANDROID_NO_SDK,
|
||||
IPADOS);
|
||||
VISIONOS,
|
||||
ANDROID_CREATOR
|
||||
);
|
||||
|
||||
app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse(
|
||||
availableClients, client);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package app.revanced.extension.youtube.patches.theme;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
|
|
@ -10,7 +12,6 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import app.revanced.extension.shared.ResourceType;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
|
||||
/**
|
||||
* Dynamic drawable that is either the regular or bolded ReVanced preference icon.
|
||||
|
|
@ -82,4 +83,19 @@ public class ReVancedSettingsIconDynamicDrawable extends Drawable {
|
|||
super.onBoundsChange(bounds);
|
||||
icon.setBounds(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTint(int tintColor) {
|
||||
icon.setTint(tintColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintList(@Nullable ColorStateList tint) {
|
||||
icon.setTintList(tint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
|
||||
icon.setTintMode(tintMode);
|
||||
}
|
||||
}
|
||||
|
|
@ -299,7 +299,7 @@ public class Settings extends YouTubeAndMusicSettings {
|
|||
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
|
||||
public static final BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
|
||||
new ChangeStartPageTypeAvailability());
|
||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.35.36", true, parent(SPOOF_APP_VERSION));
|
||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "20.05.46", true, parent(SPOOF_APP_VERSION));
|
||||
// Navigation buttons
|
||||
public static final BooleanSetting HIDE_HOME_BUTTON = new BooleanSetting("revanced_hide_home_button", FALSE, true);
|
||||
public static final BooleanSetting HIDE_CREATE_BUTTON = new BooleanSetting("revanced_hide_create_button", TRUE, true);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import app.revanced.extension.shared.spoof.ClientType;
|
|||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
||||
public class SpoofVideoStreamsSideEffectsPreference extends Preference {
|
||||
|
||||
@Nullable
|
||||
private ClientType currentClientType;
|
||||
|
|
@ -33,19 +33,19 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
|||
Utils.runOnMainThread(this::updateUI);
|
||||
};
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) {
|
||||
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context) {
|
||||
public SpoofVideoStreamsSideEffectsPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
|
|
@ -88,27 +88,23 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
|||
+ '\n' + str("revanced_spoof_video_streams_about_no_stable_volume")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_av1")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_force_original_audio");
|
||||
case ANDROID_REEL ->
|
||||
summary = str("revanced_spoof_video_streams_about_playback_failure");
|
||||
// VR 1.61 is not exposed in the UI and should never be reached here.
|
||||
case ANDROID_VR_1_43_32, ANDROID_VR_1_61_48 ->
|
||||
summary = str("revanced_spoof_video_streams_about_no_audio_tracks")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_stable_volume");
|
||||
case ANDROID_NO_SDK ->
|
||||
summary = str("revanced_spoof_video_streams_about_playback_failure");
|
||||
case IPADOS ->
|
||||
summary = str("revanced_spoof_video_streams_about_playback_failure")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_av1");
|
||||
case VISIONOS ->
|
||||
summary = str("revanced_spoof_video_streams_about_experimental")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_av1");
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_stable_volume");
|
||||
case VISIONOS -> summary = str("revanced_spoof_video_streams_about_experimental")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_playback_failure")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks")
|
||||
+ '\n' + str("revanced_spoof_video_streams_about_no_av1");
|
||||
default -> Logger.printException(() -> "Unknown client: " + clientType);
|
||||
}
|
||||
|
||||
// Only iPadOS can play children videos in incognito, but it commonly fails at 1 minute
|
||||
// or doesn't start playback at all. List the side effect for other clients
|
||||
// since they will fall over to iPadOS.
|
||||
if (clientType != ClientType.IPADOS && clientType != ClientType.ANDROID_NO_SDK) {
|
||||
summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos");
|
||||
// Only Android Reel and Android VR supports 360° VR immersive mode.
|
||||
if (!clientType.name().startsWith("ANDROID_VR") && clientType != ClientType.ANDROID_REEL) {
|
||||
summary += '\n' + str("revanced_spoof_video_streams_about_no_immersive_mode");
|
||||
}
|
||||
|
||||
// Use better formatting for bullet points.
|
||||
|
|
@ -3,7 +3,6 @@ package app.revanced.extension.youtube.sponsorblock;
|
|||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
|
|
@ -51,7 +50,6 @@ import kotlin.Unit;
|
|||
* <p>
|
||||
* Class is not thread safe. All methods must be called on the main thread unless otherwise specified.
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public class SegmentPlaybackController {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour;
|
|||
import app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory;
|
||||
import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockPreferenceGroup;
|
||||
|
||||
@SuppressWarnings("NewApi")
|
||||
public class SponsorBlockSettings {
|
||||
/**
|
||||
* Minimum length an SB user ID must be, as set by SB API.
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ org.gradle.parallel = true
|
|||
android.useAndroidX = true
|
||||
android.uniquePackageNames = false
|
||||
kotlin.code.style = official
|
||||
version = 6.0.0-dev.15
|
||||
version = 6.1.0
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ okhttp = "5.3.2"
|
|||
retrofit = "3.0.0"
|
||||
guava = "33.5.0-jre"
|
||||
apksig = "9.0.1"
|
||||
# TODO: Adjust once https://github.com/google/protobuf-gradle-plugin/pull/797 is merged.
|
||||
protobuf = "master-SNAPSHOT"
|
||||
protoc = "4.34.0"
|
||||
shadow = "9.4.0"
|
||||
|
||||
[libraries]
|
||||
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
|
||||
|
|
@ -18,6 +22,10 @@ okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
|||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
apksig = { group = "com.android.tools.build", name = "apksig", version.ref = "apksig" }
|
||||
protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.ref = "protoc" }
|
||||
protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc" }
|
||||
|
||||
[plugins]
|
||||
android-library = { id = "com.android.library" }
|
||||
protobuf = { id = "com.google.protobuf", version.ref = "protobuf" }
|
||||
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
|
||||
|
|
|
|||
|
|
@ -89,10 +89,14 @@ public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePa
|
|||
public static final fun setOrGetFallbackPackageName (Ljava/lang/String;)Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrityKt {
|
||||
public final class app/revanced/patches/all/misc/play/DisablePlayIntegrityKt {
|
||||
public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/play/SpoofPlayAgeSignalsKt {
|
||||
public static final fun getSpoofPlayAgeSignalsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt {
|
||||
public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
|
||||
public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z
|
||||
|
|
@ -125,6 +129,14 @@ public final class app/revanced/patches/all/misc/spoof/EnableRomSignatureSpoofin
|
|||
public static final fun getEnableROMSignatureSpoofingPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/spoof/SpoofKeystoreSecurityLevelPatchKt {
|
||||
public static final fun getSpoofKeystoreSecurityLevelPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/spoof/SpoofRootOfTrustPatchKt {
|
||||
public static final fun getSpoofRootOfTrustPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt {
|
||||
public static final fun getSetTargetSDKVersion34Patch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -357,10 +369,18 @@ public final class app/revanced/patches/instagram/reels/DisableReelsScrollingPat
|
|||
public static final fun getDisableReelsScrollingPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/reels/autoscroll/DisableReelsAutoScrollPatchKt {
|
||||
public static final fun getDisableReelsAutoScrollPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/story/flipping/DisableStoryAutoFlippingPatchKt {
|
||||
public static final fun getDisableStoryAutoFlippingPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/story/locationsticker/EnableLocationStickerRedesignPatchKt {
|
||||
public static final fun getEnableLocationStickerRedesignPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt {
|
||||
public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -627,6 +647,10 @@ public final class app/revanced/patches/photomath/misc/unlock/plus/UnlockPlusPat
|
|||
public static final fun getUnlockPlusPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/photoshopmix/BypassLoginPatchKt {
|
||||
public static final fun getBypassLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/piccomafr/misc/SpoofAndroidDeviceIdPatchKt {
|
||||
public static final fun getSpoofAndroidDeviceIDPatch ()Lapp/revanced/patcher/patch/Patch;
|
||||
}
|
||||
|
|
@ -884,6 +908,7 @@ public final class app/revanced/patches/shared/misc/gms/GmsCoreSupportPatchKt {
|
|||
public static synthetic fun gmsCoreSupportPatch$default (Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Lkotlin/Pair;Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch;
|
||||
public static final fun gmsCoreSupportResourcePatch (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/Patch;
|
||||
public static synthetic fun gmsCoreSupportResourcePatch$default (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/patch/Option;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/Patch;
|
||||
public static final fun prefixOrReplace (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/shared/misc/hex/HexPatchBuilder : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
|
||||
|
|
@ -1863,7 +1888,7 @@ public final class app/revanced/patches/youtube/misc/settings/PreferenceScreen :
|
|||
public final fun getADS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
|
||||
public final fun getALTERNATIVE_THUMBNAILS ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
|
||||
public final fun getFEED ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
|
||||
public final fun getGENERAL_LAYOUT ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
|
||||
public final fun getGENERAL ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
|
||||
public final fun getMISC ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
|
||||
public final fun getPLAYER ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
|
||||
public final fun getRETURN_YOUTUBE_DISLIKE ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ patches {
|
|||
description = "Patches for ReVanced"
|
||||
source = "git@github.com:revanced/revanced-patches.git"
|
||||
author = "ReVanced"
|
||||
contact = "contact@revanced.app"
|
||||
contact = "patches@revanced.app"
|
||||
website = "https://revanced.app"
|
||||
license = "GNU General Public License v3.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ import app.revanced.patcher.extensions.replaceInstruction
|
|||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.intOption
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||
import app.revanced.util.forEachInstructionAsSequence
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
|
@ -33,7 +34,8 @@ val spoofSIMProviderPatch = bytecodePatch(
|
|||
validator = { it: String? -> it == null || it.uppercase() in countries.values },
|
||||
)
|
||||
|
||||
fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999)
|
||||
fun isMccMncValid(it: Int?) = it == null || (it in 10000..999999)
|
||||
fun isNumericValid(it: String?, length: Int) = it.isNullOrBlank() || it.equals("random", true) || it.length == length
|
||||
|
||||
val networkCountryIso by isoCountryPatchOption("Network ISO country code")
|
||||
|
||||
|
|
@ -61,46 +63,119 @@ val spoofSIMProviderPatch = bytecodePatch(
|
|||
description = "The full name of the SIM operator.",
|
||||
)
|
||||
|
||||
val imei by stringOption(
|
||||
name = "IMEI value",
|
||||
description = "15-digit IMEI to spoof, blank to skip, or 'random'.",
|
||||
validator = { isNumericValid(it, 15) },
|
||||
)
|
||||
|
||||
val meid by stringOption(
|
||||
name = "MEID value",
|
||||
description = "14-char hex MEID to spoof, blank to skip, or 'random'.",
|
||||
validator = { isNumericValid(it, 14) },
|
||||
)
|
||||
|
||||
val imsi by stringOption(
|
||||
name = "IMSI (Subscriber ID)",
|
||||
description = "15-digit IMSI to spoof, blank to skip, or 'random'.",
|
||||
validator = { isNumericValid(it, 15) },
|
||||
)
|
||||
|
||||
val iccid by stringOption(
|
||||
name = "ICCID (SIM Serial)",
|
||||
description = "19-digit ICCID to spoof, blank to skip, or 'random'.",
|
||||
validator = { isNumericValid(it, 19) },
|
||||
)
|
||||
|
||||
val phone by stringOption(
|
||||
name = "Phone number",
|
||||
description = "Phone number to spoof, blank to skip, or 'random'.",
|
||||
validator = { it.isNullOrBlank() || it.equals("random", ignoreCase = true) || it.startsWith("+") },
|
||||
)
|
||||
|
||||
dependsOn(
|
||||
transformInstructionsPatch(
|
||||
filterMap = { _, _, instruction, instructionIndex ->
|
||||
if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null
|
||||
bytecodePatch {
|
||||
apply {
|
||||
fun generateRandomNumeric(length: Int) = (1..length).map { ('0'..'9').random() }.joinToString("")
|
||||
|
||||
val reference = instruction.reference as? MethodReference ?: return@transformInstructionsPatch null
|
||||
|
||||
val match = MethodCall.entries.firstOrNull { search ->
|
||||
MethodUtil.methodSignaturesMatch(reference, search.reference)
|
||||
} ?: return@transformInstructionsPatch null
|
||||
|
||||
val replacement = when (match) {
|
||||
MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase()
|
||||
MethodCall.NetworkOperator -> networkOperator?.toString()
|
||||
MethodCall.NetworkOperatorName -> networkOperatorName
|
||||
MethodCall.SimCountryIso -> simCountryIso?.lowercase()
|
||||
MethodCall.SimOperator -> simOperator?.toString()
|
||||
MethodCall.SimOperatorName -> simOperatorName
|
||||
fun String?.computeSpoof(randomizer: () -> String): String? {
|
||||
if (this.isNullOrBlank()) return null
|
||||
if (this.equals("random", ignoreCase = true)) return randomizer()
|
||||
return this
|
||||
}
|
||||
replacement?.let { instructionIndex to it }
|
||||
},
|
||||
transform = ::transformMethodCall,
|
||||
),
|
||||
|
||||
// Calculate the Luhn checksum (mod 10) to generate a valid 15th digit, standard for IMEI numbers.
|
||||
// Structure of an IMEI is as follows:
|
||||
// TAC (Type Allocation Code): First 8 digits (e.g., "86" + 6 digits)
|
||||
// SNR (Serial Number): Next 6 digits
|
||||
// CD (Check Digit): The 15th digit
|
||||
val computedImei = imei.computeSpoof {
|
||||
val prefix = "86" + generateRandomNumeric(12)
|
||||
|
||||
val sum = prefix.mapIndexed { i, c ->
|
||||
var d = c.digitToInt()
|
||||
// Double every second digit (index 1, 3, 5...).
|
||||
if (i % 2 != 0) {
|
||||
d *= 2
|
||||
// If result is two digits (e.g. 14), sum them (1+4=5).
|
||||
// This is mathematically equivalent to d - 9.
|
||||
if (d > 9) d -= 9
|
||||
}
|
||||
d
|
||||
}.sum()
|
||||
// Append the calculated check digit to the 14-digit prefix.
|
||||
prefix + ((10 - (sum % 10)) % 10)
|
||||
}
|
||||
|
||||
val computedMeid = meid.computeSpoof { (1..14).map { "0123456789ABCDEF".random() }.joinToString("") }?.uppercase()
|
||||
val computedImsi = imsi.computeSpoof { generateRandomNumeric(15) }
|
||||
val computedIccid = iccid.computeSpoof { "89" + generateRandomNumeric(17) }
|
||||
val computedPhone = phone.computeSpoof { "+" + generateRandomNumeric(11) }
|
||||
|
||||
forEachInstructionAsSequence(
|
||||
match = { _, _, instruction, instructionIndex ->
|
||||
if (instruction !is ReferenceInstruction) return@forEachInstructionAsSequence null
|
||||
|
||||
val reference = instruction.reference as? MethodReference ?: return@forEachInstructionAsSequence null
|
||||
|
||||
val match = MethodCall.entries.firstOrNull { search ->
|
||||
MethodUtil.methodSignaturesMatch(reference, search.reference)
|
||||
} ?: return@forEachInstructionAsSequence null
|
||||
|
||||
val replacement = when (match) {
|
||||
MethodCall.NetworkCountryIso -> networkCountryIso?.lowercase()
|
||||
MethodCall.NetworkOperator -> networkOperator?.toString()
|
||||
MethodCall.NetworkOperatorName -> networkOperatorName
|
||||
MethodCall.SimCountryIso -> simCountryIso?.lowercase()
|
||||
MethodCall.SimOperator -> simOperator?.toString()
|
||||
MethodCall.SimOperatorName -> simOperatorName
|
||||
MethodCall.Imei, MethodCall.ImeiWithSlot, MethodCall.DeviceId, MethodCall.DeviceIdWithSlot -> computedImei
|
||||
MethodCall.Meid, MethodCall.MeidWithSlot -> computedMeid
|
||||
MethodCall.SubscriberId, MethodCall.SubscriberIdWithSlot -> computedImsi
|
||||
MethodCall.SimSerialNumber, MethodCall.SimSerialNumberWithSlot -> computedIccid
|
||||
MethodCall.Line1Number, MethodCall.Line1NumberWithSlot -> computedPhone
|
||||
}
|
||||
replacement?.let { instructionIndex to it }
|
||||
},
|
||||
transform = ::transformMethodCall
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun transformMethodCall(
|
||||
mutableMethod: MutableMethod,
|
||||
entry: Pair<Int, String>,
|
||||
) {
|
||||
val (instructionIndex, methodCallValue) = entry
|
||||
private fun transformMethodCall(mutableMethod: MutableMethod, entry: Pair<Int, String>) {
|
||||
val (index, value) = entry
|
||||
val nextInstr = mutableMethod.getInstruction<Instruction>(index + 1)
|
||||
|
||||
// Get the register which would have contained the return value
|
||||
val register = mutableMethod.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
|
||||
if (nextInstr.opcode.name != "move-result-object") {
|
||||
mutableMethod.replaceInstruction(index, "nop")
|
||||
return
|
||||
}
|
||||
|
||||
// Replace the move-result instruction with our fake value
|
||||
mutableMethod.replaceInstruction(
|
||||
instructionIndex + 1,
|
||||
"const-string v$register, \"$methodCallValue\"",
|
||||
)
|
||||
val register = (nextInstr as OneRegisterInstruction).registerA
|
||||
mutableMethod.replaceInstruction(index, "const-string v$register, \"$value\"")
|
||||
mutableMethod.replaceInstruction(index + 1, "nop")
|
||||
}
|
||||
|
||||
private enum class MethodCall(
|
||||
|
|
@ -154,4 +229,100 @@ private enum class MethodCall(
|
|||
"Ljava/lang/String;",
|
||||
),
|
||||
),
|
||||
Imei(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getImei",
|
||||
emptyList(),
|
||||
"Ljava/lang/String;"
|
||||
),
|
||||
),
|
||||
ImeiWithSlot(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getImei",
|
||||
listOf("I"),
|
||||
"Ljava/lang/String;"
|
||||
),
|
||||
),
|
||||
DeviceId(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getDeviceId",
|
||||
emptyList(),
|
||||
"Ljava/lang/String;"
|
||||
),
|
||||
),
|
||||
DeviceIdWithSlot(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getDeviceId",
|
||||
listOf("I"),
|
||||
"Ljava/lang/String;"
|
||||
),
|
||||
),
|
||||
Meid(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getMeid",
|
||||
emptyList(),
|
||||
"Ljava/lang/String;"
|
||||
),
|
||||
),
|
||||
MeidWithSlot(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getMeid",
|
||||
listOf("I"),
|
||||
"Ljava/lang/String;"
|
||||
),
|
||||
),
|
||||
SubscriberId(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getSubscriberId",
|
||||
emptyList(),
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
),
|
||||
SubscriberIdWithSlot(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getSubscriberId",
|
||||
listOf("I"),
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
),
|
||||
SimSerialNumber(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getSimSerialNumber",
|
||||
emptyList(),
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
),
|
||||
SimSerialNumberWithSlot(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getSimSerialNumber",
|
||||
listOf("I"),
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
),
|
||||
Line1Number(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getLine1Number",
|
||||
emptyList(),
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
),
|
||||
Line1NumberWithSlot(
|
||||
ImmutableMethodReference(
|
||||
"Landroid/telephony/TelephonyManager;",
|
||||
"getLine1Number",
|
||||
listOf("I"),
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package app.revanced.patches.all.misc.connectivity.wifi.spoof
|
|||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.transformation.IMethodCall
|
||||
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
|
||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||
import app.revanced.util.forEachInstructionAsSequence
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||
"Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
|
||||
|
|
@ -19,29 +19,32 @@ val spoofWiFiConnectionPatch = bytecodePatch(
|
|||
extendWith("extensions/all/misc/connectivity/wifi/spoof/spoof-wifi.rve")
|
||||
|
||||
dependsOn(
|
||||
transformInstructionsPatch(
|
||||
filterMap = { classDef, _, instruction, instructionIndex ->
|
||||
filterMapInstruction35c<MethodCall>(
|
||||
EXTENSION_CLASS_DESCRIPTOR_PREFIX,
|
||||
classDef,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
},
|
||||
transform = { method, entry ->
|
||||
val (methodType, instruction, instructionIndex) = entry
|
||||
methodType.replaceInvokeVirtualWithExtension(
|
||||
EXTENSION_CLASS_DESCRIPTOR,
|
||||
method,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
},
|
||||
),
|
||||
bytecodePatch {
|
||||
apply {
|
||||
forEachInstructionAsSequence(
|
||||
match = { classDef, _, instruction, instructionIndex ->
|
||||
filterMapInstruction35c<MethodCall>(
|
||||
EXTENSION_CLASS_DESCRIPTOR_PREFIX,
|
||||
classDef,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
},
|
||||
transform = { method, entry ->
|
||||
val (methodType, instruction, instructionIndex) = entry
|
||||
methodType.replaceInvokeVirtualWithExtension(
|
||||
EXTENSION_CLASS_DESCRIPTOR,
|
||||
method,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Information about method calls we want to replace
|
||||
// Information about method calls we want to replace.
|
||||
@Suppress("unused")
|
||||
private enum class MethodCall(
|
||||
override val definedClassName: String,
|
||||
|
|
@ -89,13 +92,13 @@ private enum class MethodCall(
|
|||
"Landroid/net/NetworkInfo;",
|
||||
"getState",
|
||||
arrayOf(),
|
||||
"Landroid/net/NetworkInfo\$State;",
|
||||
$$"Landroid/net/NetworkInfo$State;",
|
||||
),
|
||||
GetDetailedState(
|
||||
"Landroid/net/NetworkInfo;",
|
||||
"getDetailedState",
|
||||
arrayOf(),
|
||||
"Landroid/net/NetworkInfo\$DetailedState;",
|
||||
$$"Landroid/net/NetworkInfo$DetailedState;",
|
||||
),
|
||||
IsActiveNetworkMetered(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
|
|
@ -132,7 +135,7 @@ private enum class MethodCall(
|
|||
"registerBestMatchingNetworkCallback",
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
|
||||
"Landroid/os/Handler;",
|
||||
),
|
||||
"V",
|
||||
|
|
@ -140,19 +143,19 @@ private enum class MethodCall(
|
|||
RegisterDefaultNetworkCallback1(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"registerDefaultNetworkCallback",
|
||||
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
|
||||
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"),
|
||||
"V",
|
||||
),
|
||||
RegisterDefaultNetworkCallback2(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"registerDefaultNetworkCallback",
|
||||
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;", "Landroid/os/Handler;"),
|
||||
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;", "Landroid/os/Handler;"),
|
||||
"V",
|
||||
),
|
||||
RegisterNetworkCallback1(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"registerNetworkCallback",
|
||||
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
|
||||
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;"),
|
||||
"V",
|
||||
),
|
||||
RegisterNetworkCallback2(
|
||||
|
|
@ -166,7 +169,7 @@ private enum class MethodCall(
|
|||
"registerNetworkCallback",
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
|
||||
"Landroid/os/Handler;",
|
||||
),
|
||||
"V",
|
||||
|
|
@ -174,13 +177,13 @@ private enum class MethodCall(
|
|||
RequestNetwork1(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"requestNetwork",
|
||||
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;"),
|
||||
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;"),
|
||||
"V",
|
||||
),
|
||||
RequestNetwork2(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"requestNetwork",
|
||||
arrayOf("Landroid/net/NetworkRequest;", "Landroid/net/ConnectivityManager\$NetworkCallback;", "I"),
|
||||
arrayOf("Landroid/net/NetworkRequest;", $$"Landroid/net/ConnectivityManager$NetworkCallback;", "I"),
|
||||
"V",
|
||||
),
|
||||
RequestNetwork3(
|
||||
|
|
@ -188,7 +191,7 @@ private enum class MethodCall(
|
|||
"requestNetwork",
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
|
||||
"Landroid/os/Handler;",
|
||||
),
|
||||
"V",
|
||||
|
|
@ -204,7 +207,7 @@ private enum class MethodCall(
|
|||
"requestNetwork",
|
||||
arrayOf(
|
||||
"Landroid/net/NetworkRequest;",
|
||||
"Landroid/net/ConnectivityManager\$NetworkCallback;",
|
||||
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
|
||||
"Landroid/os/Handler;",
|
||||
"I",
|
||||
),
|
||||
|
|
@ -213,7 +216,7 @@ private enum class MethodCall(
|
|||
UnregisterNetworkCallback1(
|
||||
"Landroid/net/ConnectivityManager;",
|
||||
"unregisterNetworkCallback",
|
||||
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
|
||||
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"),
|
||||
"V",
|
||||
),
|
||||
UnregisterNetworkCallback2(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import app.revanced.util.Utils.trimIndentMultiline
|
|||
|
||||
@Suppress("unused")
|
||||
val Hex = rawResourcePatch(
|
||||
name = "Hex",
|
||||
description = "Replaces a hexadecimal patterns of bytes of files in an APK.",
|
||||
use = false,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.all.misc.playintegrity
|
||||
package app.revanced.patches.all.misc.play
|
||||
|
||||
import app.revanced.patcher.extensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
|
|
@ -9,7 +9,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/playintegrity/DisablePlayIntegrityPatch;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/play/DisablePlayIntegrityPatch;"
|
||||
|
||||
private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference(
|
||||
"Landroid/content/Context;",
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
package app.revanced.patches.all.misc.play
|
||||
|
||||
import app.revanced.patcher.extensions.addInstructions
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.extensions.removeInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.intOption
|
||||
import app.revanced.patcher.patch.option
|
||||
import app.revanced.util.forEachInstructionAsSequence
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
|
||||
@Suppress("unused")
|
||||
val spoofPlayAgeSignalsPatch = bytecodePatch(
|
||||
name = "Spoof Play Age Signals",
|
||||
description = "Spoofs Google Play data about the user's age and verification status.",
|
||||
use = false,
|
||||
) {
|
||||
val lowerAgeBound by intOption(
|
||||
name = "Lower age bound",
|
||||
description = "A positive integer.",
|
||||
default = 18,
|
||||
validator = { it == null || it > 0 },
|
||||
)
|
||||
|
||||
val upperAgeBound by intOption(
|
||||
name = "Upper age bound",
|
||||
description = "A positive integer. Must be greater than the lower age bound.",
|
||||
default = Int.MAX_VALUE,
|
||||
validator = { it == null || it > lowerAgeBound!! },
|
||||
)
|
||||
|
||||
val userStatus by intOption(
|
||||
name = "User status",
|
||||
description = "An integer representing the user status.",
|
||||
default = UserStatus.VERIFIED.value,
|
||||
values = UserStatus.entries.associate { it.name to it.value },
|
||||
)
|
||||
|
||||
apply {
|
||||
forEachInstructionAsSequence(match = { classDef, _, instruction, instructionIndex ->
|
||||
// Avoid patching the library itself.
|
||||
if (classDef.type.startsWith("Lcom/google/android/play/agesignals/")) return@forEachInstructionAsSequence null
|
||||
|
||||
// Keep method calls only.
|
||||
val reference = instruction.getReference<MethodReference>()
|
||||
?: return@forEachInstructionAsSequence null
|
||||
|
||||
val match = MethodCall.entries.firstOrNull {
|
||||
reference == it.reference
|
||||
} ?: return@forEachInstructionAsSequence null
|
||||
|
||||
val replacement = when (match) {
|
||||
MethodCall.AgeLower -> lowerAgeBound!!
|
||||
MethodCall.AgeUpper -> upperAgeBound!!
|
||||
MethodCall.UserStatus -> userStatus!!
|
||||
}
|
||||
|
||||
replacement.let { instructionIndex to it }
|
||||
}, transform = { method, entry ->
|
||||
val (instructionIndex, replacement) = entry
|
||||
|
||||
// Get the register which would have contained the return value.
|
||||
val register = method.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
|
||||
|
||||
// Replace the call instructions with the spoofed value.
|
||||
method.removeInstructions(instructionIndex, 2)
|
||||
method.addInstructions(
|
||||
instructionIndex,
|
||||
"""
|
||||
const v$register, $replacement
|
||||
invoke-static { v$register }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
|
||||
move-result-object v$register
|
||||
""".trimIndent(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See [AgeSignalsResult](https://developer.android.com/google/play/age-signals/reference/com/google/android/play/agesignals/AgeSignalsResult).
|
||||
*/
|
||||
private enum class MethodCall(
|
||||
val reference: MethodReference,
|
||||
) {
|
||||
AgeLower(
|
||||
ImmutableMethodReference(
|
||||
"Lcom/google/android/play/agesignals/AgeSignalsResult;",
|
||||
"ageLower",
|
||||
emptyList(),
|
||||
"Ljava/lang/Integer;",
|
||||
),
|
||||
),
|
||||
AgeUpper(
|
||||
ImmutableMethodReference(
|
||||
"Lcom/google/android/play/agesignals/AgeSignalsResult;",
|
||||
"ageUpper",
|
||||
emptyList(),
|
||||
"Ljava/lang/Integer;",
|
||||
),
|
||||
),
|
||||
UserStatus(
|
||||
ImmutableMethodReference(
|
||||
"Lcom/google/android/play/agesignals/AgeSignalsResult;",
|
||||
"userStatus",
|
||||
emptyList(),
|
||||
"Ljava/lang/Integer;",
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
/**
|
||||
* All possible user verification statuses.
|
||||
*
|
||||
* See [AgeSignalsVerificationStatus](https://developer.android.com/google/play/age-signals/reference/com/google/android/play/agesignals/model/AgeSignalsVerificationStatus).
|
||||
*/
|
||||
private enum class UserStatus(val value: Int) {
|
||||
/** The user provided their age, but it hasn't been verified yet. */
|
||||
DECLARED(5),
|
||||
|
||||
/** The user is 18+. */
|
||||
VERIFIED(0),
|
||||
|
||||
/** The user's guardian has set the age for him. */
|
||||
SUPERVISED(1),
|
||||
|
||||
/** The user's guardian hasn't approved the significant changes yet. */
|
||||
SUPERVISED_APPROVAL_PENDING(2),
|
||||
|
||||
/** The user's guardian has denied approval for one or more pending significant changes. */
|
||||
SUPERVISED_APPROVAL_DENIED(3),
|
||||
|
||||
/** The user is not verified or supervised. */
|
||||
UNKNOWN(4),
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.revanced.patches.all.misc.spoof
|
||||
|
||||
import app.revanced.patcher.extensions.replaceInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.forEachInstructionAsSequence
|
||||
|
||||
@Suppress("unused")
|
||||
val spoofKeystoreSecurityLevelPatch = bytecodePatch(
|
||||
name = "Spoof keystore security level",
|
||||
description = "Forces apps to see Keymaster and Attestation security levels as 'StrongBox' (Level 2).",
|
||||
use = false
|
||||
) {
|
||||
apply {
|
||||
forEachInstructionAsSequence(
|
||||
match = { _, method, _, _ ->
|
||||
// Match methods by comparing the current method to a reference criteria.
|
||||
val name = method.name.lowercase()
|
||||
if (name.contains("securitylevel") && method.returnType == "I") method else null
|
||||
},
|
||||
transform = { mutableMethod, _ ->
|
||||
// Ensure the method has an implementation before replacing.
|
||||
if (mutableMethod.implementation?.instructions?.iterator()?.hasNext() == true) {
|
||||
mutableMethod.replaceInstructions(0, "const/4 v0, 0x2\nreturn v0")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package app.revanced.patches.all.misc.spoof
|
||||
|
||||
import app.revanced.patcher.extensions.replaceInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.forEachInstructionAsSequence
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
@Suppress("unused")
|
||||
val spoofRootOfTrustPatch = bytecodePatch(
|
||||
name = "Spoof root of trust",
|
||||
description = "Spoofs device integrity states (Locked Bootloader, Verified OS) for apps that perform local certificate attestation.",
|
||||
use = false
|
||||
) {
|
||||
apply {
|
||||
forEachInstructionAsSequence(
|
||||
match = { _, method, _, _ ->
|
||||
MethodCall.entries.firstOrNull { MethodUtil.methodSignaturesMatch(method, it.reference) }
|
||||
},
|
||||
transform = { mutableMethod, methodCall ->
|
||||
if (mutableMethod.implementation?.instructions?.iterator()?.hasNext() == true) {
|
||||
mutableMethod.replaceInstructions(0, methodCall.replacementInstructions)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private enum class MethodCall(
|
||||
val reference: MethodReference,
|
||||
val replacementInstructions: String,
|
||||
) {
|
||||
IsDeviceLockedRootOfTrust(
|
||||
ImmutableMethodReference(
|
||||
"LRootOfTrust;",
|
||||
"isDeviceLocked",
|
||||
emptyList(),
|
||||
"Z"
|
||||
),
|
||||
"const/4 v0, 0x1\nreturn v0",
|
||||
),
|
||||
GetVerifiedBootStateRootOfTrust(
|
||||
ImmutableMethodReference(
|
||||
"LRootOfTrust;",
|
||||
"getVerifiedBootState",
|
||||
emptyList(),
|
||||
"I"
|
||||
),
|
||||
"const/4 v0, 0x0\nreturn v0",
|
||||
),
|
||||
IsDeviceLockedAttestation(
|
||||
ImmutableMethodReference(
|
||||
"LAttestation;",
|
||||
"isDeviceLocked",
|
||||
emptyList(),
|
||||
"Z"
|
||||
),
|
||||
"const/4 v0, 0x1\nreturn v0",
|
||||
),
|
||||
GetVerifiedBootStateAttestation(
|
||||
ImmutableMethodReference(
|
||||
"LAttestation;",
|
||||
"getVerifiedBootState",
|
||||
emptyList(),
|
||||
"I"
|
||||
),
|
||||
"const/4 v0, 0x0\nreturn v0",
|
||||
),
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import app.revanced.patcher.invoke
|
|||
import app.revanced.patcher.name
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
|
||||
internal val BytecodePatchContext.exploreResponseJsonParserMethodMatch by composingFirstMethod("ExploreTopicalFeedResponse") {
|
||||
name("parseFromJson")
|
||||
internal val BytecodePatchContext.exploreResponseJsonParserMethodMatch by composingFirstMethod("clusters") {
|
||||
name("unsafeParseFromJson")
|
||||
instructions("sectional_items"())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ internal val BytecodePatchContext.initializeNavigationButtonsListMethod by getti
|
|||
}
|
||||
|
||||
internal val BytecodePatchContext.navigationButtonsEnumMethod by gettingFirstImmutableMethodDeclaratively(
|
||||
"FEED",
|
||||
"fragment_clips",
|
||||
"fragment_feed",
|
||||
"SEARCH",
|
||||
"fragment_news",
|
||||
"fragment_search",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@ internal val FEED_ITEM_KEYS_TO_BE_HIDDEN = arrayOf(
|
|||
"suggested_users",
|
||||
)
|
||||
|
||||
internal val BytecodePatchContext.feedItemParseFromJsonMethodMatch by composingFirstMethod("FeedItem") {
|
||||
internal val BytecodePatchContext.feedItemParseFromJsonMethodMatch by composingFirstMethod("feed_item_type") {
|
||||
instructions(predicates = unorderedAllOf(predicates = FEED_ITEM_KEYS_TO_BE_HIDDEN.map { it() }.toTypedArray()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import app.revanced.patcher.patch.BytecodePatchContext
|
|||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val BytecodePatchContext.permalinkResponseJsonParserMethodMatch by composingFirstMethod {
|
||||
name("parseFromJson")
|
||||
name("unsafeParseFromJson")
|
||||
instructions("permalink"())
|
||||
opcodes(
|
||||
Opcode.NEW_INSTANCE,
|
||||
|
|
@ -19,16 +19,16 @@ internal val BytecodePatchContext.permalinkResponseJsonParserMethodMatch by comp
|
|||
}
|
||||
|
||||
internal val BytecodePatchContext.storyUrlResponseJsonParserMethodMatch by composingFirstMethod {
|
||||
name("parseFromJson")
|
||||
name("unsafeParseFromJson")
|
||||
instructions("story_item_to_share_url"())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.profileUrlResponseJsonParserMethodMatch by composingFirstMethod {
|
||||
name("parseFromJson")
|
||||
name("unsafeParseFromJson")
|
||||
instructions("profile_to_share_url"())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.liveUrlResponseJsonParserMethodMatch by composingFirstMethod {
|
||||
name("parseFromJson")
|
||||
name("unsafeParseFromJson")
|
||||
instructions("live_to_share_url"())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package app.revanced.patches.instagram.reels.autoscroll
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val disableReelsAutoScrollPatch = bytecodePatch(
|
||||
name = "Disable Reels auto-scroll",
|
||||
description = "Removes the auto-scroll toggle and prevents Reels from scrolling automatically.",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.instagram.android")
|
||||
|
||||
apply {
|
||||
// Prevent the auto-scroll feature from being initialized.
|
||||
// When this returns false, ClipsViewerFragment skips creating the auto-scroller.
|
||||
clipsAutoScrollFeatureCheckMethod.returnEarly()
|
||||
|
||||
// Make the toggle button handler a no-op so tapping it does nothing.
|
||||
clipsAutoScrollToggleMethod.returnEarly()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package app.revanced.patches.instagram.reels.autoscroll
|
||||
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.parameterTypes
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
|
||||
/**
|
||||
* Matches the feature availability gate that determines
|
||||
* whether auto-scroll should be available for Reels.
|
||||
*/
|
||||
internal val BytecodePatchContext.clipsAutoScrollFeatureCheckMethod by gettingFirstMethodDeclaratively("auto_scroll") {
|
||||
returnType("Z")
|
||||
parameterTypes("Lcom/instagram/common/session/UserSession;", "Z")
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the toggle handler called when the user taps
|
||||
* the auto-scroll button. Contains analytics logging strings.
|
||||
*/
|
||||
internal val BytecodePatchContext.clipsAutoScrollToggleMethod by gettingFirstMethodDeclaratively(
|
||||
"auto_scroll",
|
||||
"instagram_clips_viewer_autoplay_tap",
|
||||
)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package app.revanced.patches.instagram.story.locationsticker
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val enableLocationStickerRedesignPatch = bytecodePatch(
|
||||
name = "Enable location sticker redesign",
|
||||
description = "Unlocks the redesigned location sticker with additional style options.",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.instagram.android")
|
||||
|
||||
apply {
|
||||
// The gate method reads a MobileConfig boolean flag and returns it directly.
|
||||
// Returning early with true bypasses the flag check entirely,
|
||||
// enabling the redesigned sticker styles regardless of server configuration.
|
||||
locationStickerRedesignGateMethodMatch.method.returnEarly(true)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package app.revanced.patches.instagram.story.locationsticker
|
||||
|
||||
import app.revanced.patcher.composingFirstMethod
|
||||
import app.revanced.patcher.instructions
|
||||
import app.revanced.patcher.invoke
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
|
||||
// MobileConfig boolean key that gates the redesigned location sticker styles.
|
||||
// The method containing this constant reads the flag and returns it directly,
|
||||
// making it the sole control point for the feature. The key is stable across
|
||||
// app updates as MobileConfig keys are server-assigned constants.
|
||||
private const val LOCATION_STICKER_REDESIGN_CONFIG_KEY = 0x8105a100041e0dL
|
||||
|
||||
internal val BytecodePatchContext.locationStickerRedesignGateMethodMatch by composingFirstMethod {
|
||||
instructions(LOCATION_STICKER_REDESIGN_CONFIG_KEY())
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package app.revanced.patches.photoshopmix
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val bypassLoginPatch = bytecodePatch(
|
||||
name = "Bypass login",
|
||||
description = "Allows the use of the app after its discontinuation.",
|
||||
) {
|
||||
compatibleWith("com.adobe.photoshopmix")
|
||||
|
||||
apply {
|
||||
isLoggedInMethod.returnEarly(true)
|
||||
|
||||
// Disables these buttons that cause the app to crash while not logged in.
|
||||
ccLibButtonClickHandlerMethod.returnEarly()
|
||||
lightroomButtonClickHandlerMethod.returnEarly()
|
||||
ccButtonClickHandlerMethod.returnEarly()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package app.revanced.patches.photoshopmix
|
||||
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.name
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.returnType
|
||||
|
||||
internal val BytecodePatchContext.isLoggedInMethod by gettingFirstMethodDeclaratively {
|
||||
name("isLoggedIn")
|
||||
definingClass("CreativeCloudSource;")
|
||||
returnType("Z")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.ccLibButtonClickHandlerMethod by gettingFirstMethodDeclaratively {
|
||||
name("ccLibButtonClickHandler")
|
||||
definingClass("PSMixFragment;")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.lightroomButtonClickHandlerMethod by gettingFirstMethodDeclaratively {
|
||||
name("lightroomButtonClickHandler")
|
||||
definingClass("PSMixFragment;")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.ccButtonClickHandlerMethod by gettingFirstMethodDeclaratively {
|
||||
name("ccButtonClickHandler")
|
||||
definingClass("PSMixFragment;")
|
||||
}
|
||||
|
|
@ -1,13 +1,16 @@
|
|||
package app.revanced.patches.protonvpn.delay
|
||||
|
||||
import app.revanced.patcher.definingClass
|
||||
import app.revanced.patcher.gettingFirstMethodDeclaratively
|
||||
import app.revanced.patcher.name
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
|
||||
internal val BytecodePatchContext.longDelayMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass("AppConfigResponse;")
|
||||
name("getChangeServerLongDelayInSeconds")
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.shortDelayMethod by gettingFirstMethodDeclaratively {
|
||||
definingClass("AppConfigResponse;")
|
||||
name("getChangeServerShortDelayInSeconds")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package app.revanced.patches.shared.layout.branding
|
|||
import app.revanced.com.android.tools.smali.dexlib2.mutable.MutableMethod
|
||||
import app.revanced.patcher.extensions.addInstruction
|
||||
import app.revanced.patcher.extensions.getInstruction
|
||||
import app.revanced.patcher.firstImmutableClassDef
|
||||
import app.revanced.patcher.patch.*
|
||||
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
|
|
@ -126,14 +125,14 @@ internal fun baseCustomBrandingPatch(
|
|||
val getBuilderIndex = if (isYouTubeMusic) {
|
||||
// YT Music the field is not a plain object type.
|
||||
indexOfFirstInstructionOrThrow {
|
||||
getReference<FieldReference>()?.type == "Landroid/app/Notification\$Builder;"
|
||||
getReference<FieldReference>()?.type == $$"Landroid/app/Notification$Builder;"
|
||||
}
|
||||
} else {
|
||||
// Find the field name of the notification builder. Field is an Object type.
|
||||
val builderCastIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<TypeReference>()
|
||||
opcode == Opcode.CHECK_CAST &&
|
||||
reference?.type == "Landroid/app/Notification\$Builder;"
|
||||
reference?.type == $$"Landroid/app/Notification$Builder;"
|
||||
}
|
||||
indexOfFirstInstructionReversedOrThrow(builderCastIndex) {
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/Object;"
|
||||
|
|
@ -148,11 +147,11 @@ internal fun baseCustomBrandingPatch(
|
|||
).forEach { index ->
|
||||
addInstructionsAtControlFlowLabel(
|
||||
index,
|
||||
"""
|
||||
$$"""
|
||||
move-object/from16 v0, p0
|
||||
iget-object v0, v0, $builderFieldName
|
||||
check-cast v0, Landroid/app/Notification${'$'}Builder;
|
||||
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification${'$'}Builder;)V
|
||||
iget-object v0, v0, $$builderFieldName
|
||||
check-cast v0, Landroid/app/Notification$Builder;
|
||||
invoke-static { v0 }, $$EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification$Builder;)V
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
|
@ -162,16 +161,37 @@ internal fun baseCustomBrandingPatch(
|
|||
)
|
||||
|
||||
afterDependents {
|
||||
val useCustomName = customName != null
|
||||
val useCustomIcon = customIcon != null
|
||||
val isRootInstall = setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName
|
||||
|
||||
// Can only check if app is root installation by checking if change package name patch is in use.
|
||||
// and can only do that in the afterDependents block here.
|
||||
// The UI preferences cannot be selectively added here, because the settings afterDependents block
|
||||
// may have already run and the settings are already wrote to file.
|
||||
// Instead, show a warning if any patch option was used (A rooted device launcher ignores the manifest changes),
|
||||
// and the non-functional in-app settings are removed on app startup by extension code.
|
||||
if (customName != null || customIcon != null) {
|
||||
if (setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName) {
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"Custom branding does not work with root installation. No changes applied.",
|
||||
if (isRootInstall && (useCustomName || useCustomIcon)) {
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"Custom branding does not work with root installation. No changes applied."
|
||||
)
|
||||
}
|
||||
|
||||
if (!isRootInstall || useCustomName) {
|
||||
document("AndroidManifest.xml").use { document ->
|
||||
val application = document.getElementsByTagName("application").item(0) as Element
|
||||
application.setAttribute(
|
||||
"android:label",
|
||||
if (useCustomName) {
|
||||
// Use custom name everywhere.
|
||||
customName
|
||||
} else {
|
||||
// The YT application name can appear in some places alongside the system
|
||||
// YouTube app, such as the settings app list and in the "open with" file picker.
|
||||
// Because the YouTube app cannot be completely uninstalled and only disabled,
|
||||
// use a custom name for this situation to disambiguate which app is which.
|
||||
"@string/revanced_custom_branding_name_entry_2"
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -312,16 +332,19 @@ internal fun baseCustomBrandingPatch(
|
|||
activityAliasNameWithIntents,
|
||||
).childNodes
|
||||
|
||||
// The YT application name can appear in some places alongside the system
|
||||
// YouTube app, such as the settings app list and in the "open with" file picker.
|
||||
// Because the YouTube app cannot be completely uninstalled and only disabled,
|
||||
// use a custom name for this situation to disambiguate which app is which.
|
||||
application.setAttribute(
|
||||
"android:label",
|
||||
"@string/revanced_custom_branding_name_entry_2",
|
||||
)
|
||||
// If user provides a custom icon, then change the application icon ('static' icon)
|
||||
// which shows as the push notification for some devices, in the app settings,
|
||||
// and as the icon for the apk before installing.
|
||||
// This icon cannot be dynamically selected and this change must only be done if the
|
||||
// user provides an icon otherwise there is no way to restore the original YouTube icon.
|
||||
if (useCustomIcon) {
|
||||
application.setAttribute(
|
||||
"android:icon",
|
||||
"@mipmap/revanced_launcher_custom"
|
||||
)
|
||||
}
|
||||
|
||||
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 1 // 1 indexing.
|
||||
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 2 // 1 indexing.
|
||||
val enabledIconIndex = if (useCustomIcon) iconStyleNames.size else 0 // 0 indexing.
|
||||
|
||||
for (appNameIndex in 1..numberOfPresetAppNames) {
|
||||
|
|
@ -336,7 +359,7 @@ internal fun baseCustomBrandingPatch(
|
|||
iconMipmapName = originalLauncherIconName,
|
||||
appNameIndex = appNameIndex,
|
||||
useCustomName = useCustomNameLabel,
|
||||
enabled = (appNameIndex == 1),
|
||||
enabled = false,
|
||||
intentFilters,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ fun gmsCoreSupportResourcePatch(
|
|||
.joinToString(";") { authority ->
|
||||
APP_AUTHORITIES += authority
|
||||
|
||||
authority.replace(fromPackageName, toPackageName)
|
||||
authority.prefixOrReplace(fromPackageName, toPackageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,13 +169,13 @@ internal fun spoofVideoStreamsPatch(
|
|||
if-eqz v2, :disabled
|
||||
|
||||
# Get streaming data.
|
||||
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)Ljava/nio/ByteBuffer;
|
||||
invoke-static { v2 }, $EXTENSION_CLASS_DESCRIPTOR->getStreamingData(Ljava/lang/String;)[B
|
||||
move-result-object v3
|
||||
if-eqz v3, :disabled
|
||||
|
||||
# Parse streaming data.
|
||||
sget-object v4, $playerProtoClass->a:$playerProtoClass
|
||||
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}Ljava/nio/ByteBuffer;)$protobufClass
|
||||
invoke-static { v4, v3 }, $protobufClass->parseFrom(${protobufClass}[B)$protobufClass
|
||||
move-result-object v5
|
||||
check-cast v5, $playerProtoClass
|
||||
|
||||
|
|
@ -216,10 +216,10 @@ internal fun spoofVideoStreamsPatch(
|
|||
// A proper fix may include modifying the request body to match the platforms expected body.
|
||||
|
||||
buildMediaDataSourceMethod.apply {
|
||||
val targetIndex = instructions.count() - 1
|
||||
// Find return-void, not the last instruction, which may be a throw in an error branch
|
||||
// where p0 is overwritten by new-instance IllegalArgumentException.
|
||||
val targetIndex = indexOfFirstInstructionOrThrow(Opcode.RETURN_VOID)
|
||||
|
||||
// Instructions are added just before the method returns,
|
||||
// so there's no concern of clobbering in-use registers.
|
||||
addInstructions(
|
||||
targetIndex,
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package app.revanced.patches.spotify.misc.privacy
|
|||
|
||||
import app.revanced.patcher.*
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
|
|
@ -28,13 +27,12 @@ internal val BytecodePatchContext.formatAndroidShareSheetUrlMethod by gettingFir
|
|||
returnType("Ljava/lang/String;")
|
||||
parameterTypes("L", "Ljava/lang/String;")
|
||||
opcodes(
|
||||
Opcode.GOTO,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.RETURN_OBJECT,
|
||||
)
|
||||
literal { '\n'.code.toLong() }
|
||||
instructions('\n'.code.toLong()())
|
||||
}
|
||||
|
||||
internal val BytecodePatchContext.oldFormatAndroidShareSheetUrlMethod by gettingFirstMethodDeclaratively {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ internal val BytecodePatchContext.fullscreenButtonMethodMatch by composingFirstM
|
|||
returnType("V")
|
||||
instructions(
|
||||
ResourceType.ID("fullscreen_button"),
|
||||
Opcode.CHECK_CAST()
|
||||
Opcode.INVOKE_VIRTUAL(), // findViewById call.
|
||||
Opcode.MOVE_RESULT_OBJECT() // The actual view, not a check-cast in 21.x.
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -169,14 +169,16 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
|||
|
||||
fullscreenButtonMethodMatch.let {
|
||||
it.method.apply {
|
||||
val castIndex = it[1]
|
||||
val insertIndex = castIndex + 1
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(castIndex).registerA
|
||||
// Youtube 21.x doesn't cast the fullscreen button to ImageView anymore,
|
||||
// so match on move-result-object after findViewById instead of check-cast.
|
||||
val moveResultIndex = it[2]
|
||||
val insertIndex = moveResultIndex + 1
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { v$insertRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->hideFullscreenButton(Landroid/widget/ImageView;)Landroid/widget/ImageView;
|
||||
invoke-static { v$insertRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->hideFullscreenButton(Landroid/view/View;)Landroid/view/View;
|
||||
move-result-object v$insertRegister
|
||||
if-nez v$insertRegister, :show
|
||||
return-void
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue