Compare commits

..

4 commits

Author SHA1 Message Date
drobotk
5d0ac9ea63
refactor: unused imports 2026-03-06 12:41:26 +01:00
drobotk
3c154e1119
fix logic error from refactor 2026-03-06 12:19:27 +01:00
drobotk
2ca6f943e4
refactor: more wip 2026-03-06 11:24:39 +01:00
drobotk
1f8a6f2040
wip: clean up deprecated parts 2026-03-05 23:53:57 +01:00
211 changed files with 17545 additions and 31898 deletions

View file

@ -19,7 +19,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
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@v7
uses: actions/upload-artifact@v5
with:
name: revanced-patches
path: patches/build/libs

View file

@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Open pull request
uses: repo-sync/pull-request@v2

View file

@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
with:
ref: dev
persist-credentials: true
@ -32,7 +32,6 @@ jobs:
- name: Process strings
run: |
chmod -R 777 patches/src/main/resources
./gradlew processStringsFromCrowdin
env:
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ github.actor }}

View file

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Process strings
env:

View file

@ -15,11 +15,10 @@ jobs:
packages: write
id-token: write
attestations: write
artifact-metadata: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Setup Java
uses: actions/setup-java@v5
@ -62,7 +61,7 @@ jobs:
- name: Attest
if: steps.release.outputs.new_release_published == 'true'
uses: actions/attest@v4
uses: actions/attest-build-provenance@v3
with:
subject-name: 'ReVanced Patches ${{ steps.release.outputs.new_release_git_tag }}'
subject-path: patches/build/libs/patches-*.rvp

View file

@ -10,9 +10,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v5
- name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@v2
uses: gradle-update/update-gradle-wrapper-action@v1
with:
target-branch: dev

View file

@ -1,302 +1,3 @@
# [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)

View file

@ -1,7 +1,14 @@
android {
namespace = "app.revanced.extension"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {

View file

@ -1,6 +1,9 @@
android {
defaultConfig {
minSdk = 23
namespace = "app.revanced.extension"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View file

@ -1,7 +1,14 @@
android {
namespace = "app.revanced.extension"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {

View file

@ -31,10 +31,7 @@ public class InternalDataDocumentsProvider extends DocumentsProvider {
private static final String[] directoryColumns =
{"document_id", "mime_type", "_display_name", "last_modified", "flags",
"_size", "full_path", "lstat_info"};
@SuppressWarnings("OctalInteger")
private static final int S_IFMT = 0170000;
@SuppressWarnings("OctalInteger")
private static final int S_IFLNK = 0120000;
private static final int S_IFLNK = 0x8000;
private String packageName;
private File dataDirectory;
@ -50,7 +47,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_IFMT) != S_IFLNK) {
if ((Os.lstat(root.getPath()).st_mode & S_IFLNK) != S_IFLNK) {
File[] files = root.listFiles();
if (files != null) {
for (File file : files) {
@ -327,7 +324,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_IFMT) == S_IFLNK) {
if ((lstat.st_mode & S_IFLNK) == S_IFLNK) {
sb.append(";");
sb.append(Os.readlink(path));
}

View file

@ -1,8 +1,15 @@
android {
namespace = "app.revanced.extension"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
aidl = true
}

View file

@ -1,4 +1,4 @@
package app.revanced.extension.play;
package app.revanced.extension.playintegrity;
import android.content.Context;
import android.content.Intent;

View file

@ -1,7 +1,14 @@
android {
namespace = "app.revanced.extension"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {

View file

@ -1,7 +1,14 @@
android {
namespace = "app.revanced.extension"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {

View file

@ -3,9 +3,3 @@ dependencies {
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
}
android {
defaultConfig {
minSdk = 22
}
}

View file

@ -4,9 +4,3 @@ dependencies {
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
}
android {
defaultConfig {
minSdk = 21
}
}

View file

@ -1,10 +1,4 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:cricbuzz:stub"))
}
android {
defaultConfig {
minSdk = 21
}
}
}

View file

@ -1,9 +1,3 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
}
android {
defaultConfig {
minSdk = 26
}
}

View file

@ -1,9 +1,3 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
}
android {
defaultConfig {
minSdk = 24
}
}

View file

@ -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_REEL;
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.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
);

View file

@ -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_REEL, true, parent(SPOOF_VIDEO_STREAMS));
ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS));
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", TRUE, true);
}

View file

@ -5,6 +5,11 @@ dependencies {
android {
defaultConfig {
minSdk = 23
minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View file

@ -2,9 +2,3 @@ dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:nunl:stub"))
}
android {
defaultConfig {
minSdk = 26
}
}

View file

@ -2,9 +2,3 @@ dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:primevideo:stub"))
}
android {
defaultConfig {
minSdk = 21
}
}

View file

@ -1,9 +1,3 @@
dependencies {
compileOnly(project(":extensions:reddit:stub"))
}
android {
defaultConfig {
minSdk = 28
}
}

View file

@ -2,9 +2,3 @@ dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:samsung:radio:stub"))
}
android {
defaultConfig {
minSdk = 26
}
}

View file

@ -5,6 +5,6 @@ dependencies {
android {
defaultConfig {
minSdk = 23
minSdk = 26
}
}

View file

@ -1,5 +1,5 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.android.library)
}
android {
@ -19,6 +19,4 @@ android {
dependencies {
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
compileOnly(libs.protobuf.javalite)
implementation(project(":extensions:shared:protobuf", configuration = "shadowRuntimeElements"))
}

View file

@ -32,7 +32,11 @@ 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;
@ -61,6 +65,7 @@ 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")
@ -134,7 +139,6 @@ public class Utils {
return versionName;
}
@SuppressWarnings("unused")
public static String getApplicationName() {
if (applicationLabel == null) {
try {
@ -181,13 +185,24 @@ public class Utils {
* @param view The view to hide.
*/
public static void hideViewBy0dp(View view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params == null)
params = new ViewGroup.LayoutParams(0, 0);
params.width = 0;
params.height = 0;
view.setLayoutParams(params);
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);
}
}
/**
@ -464,7 +479,6 @@ public class Utils {
return str != null && !str.isEmpty();
}
@SuppressWarnings("unused")
public static boolean isTablet() {
return context.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
@ -504,7 +518,6 @@ public class Utils {
return getTextDirectionString(isRightToLeftLocale());
}
@SuppressWarnings("unused")
public static String getTextDirectionString(Locale locale) {
return getTextDirectionString(isRightToLeftLocale(locale));
}

View file

@ -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,7 +19,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.util.Collection;
@ -29,7 +28,6 @@ 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;
@ -78,6 +76,7 @@ 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();

View file

@ -10,8 +10,6 @@ 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;
@ -31,7 +29,6 @@ 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();
@ -124,7 +121,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("HardwareIds")
@SuppressLint({"NewApi", "HardwareIds"})
@Override
protected Boolean check() {
if (PATCH_BOARD.isEmpty()) {
@ -198,7 +195,7 @@ public final class CheckEnvironmentPatch {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
// Duration since initial install or last update, whichever is sooner.
// Duration since initial install or last update, which ever is sooner.
durationBetweenPatchingAndInstallation = packageInfo.lastUpdateTime - PatchInfo.PATCH_TIME;
Logger.printInfo(() -> "App was installed/updated: "
+ (durationBetweenPatchingAndInstallation / (60 * 1000) + " minutes after patching"));

View file

@ -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()
: 2;
: 1;
}
public static BrandingTheme getDefaultIconStyle() {
return userProvidedCustomIcon()
? BrandingTheme.CUSTOM
: BrandingTheme.ROUNDED;
: BrandingTheme.ORIGINAL;
}
/**

View file

@ -49,7 +49,7 @@ public final class LithoFilterPatch {
}
builder.append(" Path: ");
builder.append(path);
if (YouTubeAndMusicSettings.DEBUG_PROTOCOLBUFFER.get()) {
if (YouTubeAndMusicSettings.DEBUG_PROTOBUFFER.get()) {
builder.append(" BufferStrings: ");
findAsciiStrings(builder, buffer);
}

View file

@ -6,15 +6,12 @@ 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;
@ -25,8 +22,7 @@ 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")
@RequiresApi(api = Build.VERSION_CODES.O)
@SuppressWarnings({"deprecation", "NewApi"})
public abstract class BaseActivityHook extends Activity {
private static final int ID_REVANCED_SETTINGS_FRAGMENTS =

View file

@ -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_video_streams_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
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 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);

View file

@ -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_PROTOCOLBUFFER = new BooleanSetting("revanced_debug_protocolbuffer", FALSE, false,
"revanced_debug_protocolbuffer_user_dialog_message", parent(BaseSettings.DEBUG));
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, false,
"revanced_debug_protobuffer_user_dialog_message", parent(BaseSettings.DEBUG));
}

View file

@ -15,16 +15,15 @@ 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")
@RequiresApi(api = Build.VERSION_CODES.O)
@SuppressWarnings({"deprecation", "NewApi"})
public class ToolbarPreferenceFragment extends AbstractPreferenceFragment {
/**

View file

@ -9,34 +9,9 @@ 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.
@ -53,10 +28,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"
),
/**
@ -73,12 +48,39 @@ 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>
@ -93,10 +95,10 @@ public enum ClientType {
"15",
"35",
"AP3A.241005.015.A2",
"132.0.6779.0",
"23.47.101",
true,
false,
true,
"Android Studio"
),
/**
@ -112,8 +114,32 @@ 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,
true,
"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"
);
/**
@ -169,6 +195,13 @@ 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.
*/
@ -184,11 +217,6 @@ 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.
*/
@ -206,10 +234,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;
@ -220,20 +248,21 @@ 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)",
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)",
packageName,
clientVersion,
osVersion,
defaultLocale,
deviceModel,
buildId
Objects.requireNonNull(buildId),
Objects.requireNonNull(cronetVersion)
);
Logger.printDebug(() -> "userAgent: " + this.userAgent);
}
@ -249,7 +278,6 @@ public enum ClientType {
String userAgent,
boolean useAuth,
boolean supportsMultiAudioTracks,
boolean usePlayerEndpoint,
String friendlyName) {
this.id = id;
this.clientName = clientName;
@ -261,10 +289,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;
}
}

View file

@ -5,6 +5,7 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -38,7 +39,7 @@ public class SpoofVideoStreamsPatch {
@Nullable
private static volatile AppLanguage languageOverride;
private static volatile ClientType preferredClient = ClientType.ANDROID_REEL;
private static volatile ClientType preferredClient = ClientType.ANDROID_VR_1_43_32;
/**
* @return If this patch was included during patching.
@ -249,7 +250,7 @@ public class SpoofVideoStreamsPatch {
* Called after {@link #fetchStreams(String, Map)}.
*/
@Nullable
public static byte[] getStreamingData(String videoId) {
public static ByteBuffer getStreamingData(String videoId) {
if (SPOOF_STREAMING_DATA) {
try {
StreamingDataRequest request = StreamingDataRequest.getRequestForVideoId(videoId);

View file

@ -15,20 +15,13 @@ import app.revanced.extension.shared.spoof.ClientType;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
final class PlayerRoutes {
static final Route.CompiledRoute GET_PLAYER_STREAMING_DATA = new Route(
static final Route.CompiledRoute GET_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/";
/**
@ -54,7 +47,6 @@ 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);
@ -69,19 +61,9 @@ final class PlayerRoutes {
context.put("client", client);
innerTubeBody.put("context", context);
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);
}
innerTubeBody.put("contentCheckOk", true);
innerTubeBody.put("racyCheckOk", true);
innerTubeBody.put("videoId", videoId);
} catch (JSONException e) {
Logger.printException(() -> "Failed to create innerTubeBody", e);
}

View file

@ -1,17 +1,18 @@
package app.revanced.extension.shared.spoof.requests;
import static app.revanced.extension.shared.ByteTrieSearch.convertStringsToBytes;
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 static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_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;
@ -26,11 +27,6 @@ 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;
@ -45,7 +41,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);
@ -115,7 +111,7 @@ public class StreamingDataRequest {
private final String videoId;
private final Future<byte[]> future;
private final Future<ByteBuffer> future;
private StreamingDataRequest(String videoId, Map<String, String> playerHeaders) {
Objects.requireNonNull(playerHeaders);
@ -138,12 +134,6 @@ 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,
@ -156,10 +146,7 @@ public class StreamingDataRequest {
final long startTime = System.currentTimeMillis();
try {
Route.CompiledRoute route = clientType.usePlayerEndpoint ?
GET_PLAYER_STREAMING_DATA : GET_REEL_STREAMING_DATA;
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(route, clientType);
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
connection.setConnectTimeout(HTTP_TIMEOUT_MILLISECONDS);
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
@ -216,7 +203,7 @@ public class StreamingDataRequest {
return null;
}
private static byte[] fetch(String videoId, Map<String, String> playerHeaders) {
private static ByteBuffer fetch(String videoId, Map<String, String> playerHeaders) {
final boolean debugEnabled = BaseSettings.DEBUG.get();
// Retry with different client if empty response body is received.
@ -227,11 +214,33 @@ public class StreamingDataRequest {
HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast);
if (connection != null) {
byte[] playerResponseBuffer = buildPlayerResponseBuffer(clientType, connection);
if (playerResponseBuffer != null) {
lastSpoofedClientType = clientType;
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()) {
return playerResponseBuffer;
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);
}
}
}
@ -241,61 +250,12 @@ 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 byte[] getStream() {
public ByteBuffer getStream() {
try {
return future.get(MAX_MILLISECONDS_TO_WAIT_FOR_FETCH, TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {

View file

@ -1,55 +0,0 @@
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) } }

View file

@ -1,42 +0,0 @@
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;
}

View file

@ -1,14 +0,0 @@
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;
}
}

View file

@ -6,6 +6,11 @@ dependencies {
android {
defaultConfig {
minSdk = 24
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

View file

@ -7,11 +7,11 @@ android {
compileSdk = 34
defaultConfig {
minSdk = 24
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}

View file

@ -3,9 +3,3 @@ dependencies {
compileOnly(project(":extensions:strava:stub"))
compileOnly(libs.okhttp)
}
android {
defaultConfig {
minSdk = 26
}
}

View file

@ -1,7 +1,9 @@
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;
@ -26,6 +28,7 @@ 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;
@ -82,7 +85,7 @@ public final class AddMediaDownloadPatch {
} finally {
values.clear();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
resolver.update(row, values, null, null);
resolver.update(row, values, null);
}
showInfoToast("yis_2024_local_save_image_success", "✔️");
} catch (IOException e) {
@ -148,7 +151,7 @@ public final class AddMediaDownloadPatch {
} finally {
values.clear();
values.put(MediaStore.Video.Media.IS_PENDING, 0);
resolver.update(row, values, null, null);
resolver.update(row, values, null);
}
showInfoToast("yis_2024_local_save_video_success", "✔️");
} catch (IOException e) {

View file

@ -1,5 +1,7 @@
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;
@ -19,6 +21,7 @@ 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;

View file

@ -7,6 +7,6 @@ android {
compileSdk = 34
defaultConfig {
minSdk = 26
minSdk = 21
}
}

View file

@ -4,9 +4,3 @@ dependencies {
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
}
android {
defaultConfig {
minSdk = 23
}
}

View file

@ -8,4 +8,9 @@ android {
defaultConfig {
minSdk = 22
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View file

@ -20,7 +20,7 @@ import java.lang.reflect.InvocationTargetException;
/**
* Hooks AdPersonalizationActivity to inject a custom {@link TikTokPreferenceFragment}.
*/
@SuppressWarnings({"deprecation", "unused"})
@SuppressWarnings({"deprecation", "NewApi", "unused"})
public class TikTokActivityHook {
public static Object createSettingsEntry(String entryClazzName, String entryInfoClazzName) {
try {

View file

@ -1,9 +1,3 @@
dependencies {
compileOnly(libs.appcompat)
}
android {
defaultConfig {
minSdk = 22
}
}

View file

@ -11,4 +11,9 @@ android {
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View file

@ -22,7 +22,7 @@ import tv.twitch.android.settings.SettingsActivity;
/**
* Hooks AppCompatActivity to inject a custom {@link TwitchPreferenceFragment}.
*/
@SuppressWarnings({"deprecation", "unused"})
@SuppressWarnings({"deprecation", "NewApi", "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";

View file

@ -1,9 +1,3 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
}
android {
defaultConfig {
minSdk = 26
}
}

View file

@ -18,7 +18,6 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@ -238,8 +237,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();
}

View file

@ -1,6 +1,6 @@
package app.revanced.extension.youtube.patches;
import android.view.View;
import android.widget.ImageView;
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(() -> {
View button = PlayerControlsPatch.fullscreenButtonRef.get();
ImageView button = PlayerControlsPatch.fullscreenButtonRef.get();
if (button == null) {
Logger.printDebug(() -> "Fullscreen button is null, cannot click");
} else {

View file

@ -109,13 +109,13 @@ public final class HidePlayerOverlayButtonsPatch {
/**
* Injection point.
*/
public static View hideFullscreenButton(View view) {
public static ImageView hideFullscreenButton(ImageView imageView) {
if (!Settings.HIDE_FULLSCREEN_BUTTON.get()) {
return view;
return imageView;
}
if (view != null) {
view.setVisibility(View.GONE);
if (imageView != null) {
imageView.setVisibility(View.GONE);
}
return null;

View file

@ -2,11 +2,9 @@ package app.revanced.extension.youtube.patches;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.*;
import static app.revanced.extension.youtube.patches.VersionCheckPatch.*;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@ -21,7 +19,7 @@ import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings({"unused", "SpellCheckingInspection"})
@SuppressWarnings("unused")
public final class MiniplayerPatch {
/**
@ -120,15 +118,6 @@ public final class MiniplayerPatch {
private static final MiniplayerType CURRENT_TYPE = Settings.MINIPLAYER_TYPE.get();
/**
* Cannot turn off double tap with modern 2 or 3 with later targets,
* as forcing it off breakings tapping the miniplayer.
*/
private static final boolean DOUBLE_TAP_ACTION_ENABLED =
// 19.29+ is very broken if double tap is not enabled.
IS_19_29_OR_GREATER ||
(CURRENT_TYPE.isModern() && Settings.MINIPLAYER_DOUBLE_TAP_ACTION.get());
private static final boolean DRAG_AND_DROP_ENABLED =
CURRENT_TYPE.isModern() && !Settings.MINIPLAYER_DISABLE_DRAG_AND_DROP.get();
@ -142,8 +131,7 @@ public final class MiniplayerPatch {
// 19.25 is last version that uses forward/back buttons for phones,
// but buttons still show for tablets/foldable devices, and they don't work well so always hide.
private static final boolean HIDE_REWIND_FORWARD_ENABLED = CURRENT_TYPE == MODERN_1
&& (VersionCheckPatch.IS_19_34_OR_GREATER || Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get());
private static final boolean HIDE_REWIND_FORWARD_ENABLED = CURRENT_TYPE == MODERN_1;
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
CURRENT_TYPE.isModern() && !Settings.MINIPLAYER_DISABLE_ROUNDED_CORNERS.get();
@ -151,13 +139,6 @@ public final class MiniplayerPatch {
private static final boolean MINIPLAYER_HORIZONTAL_DRAG_ENABLED =
DRAG_AND_DROP_ENABLED && !Settings.MINIPLAYER_DISABLE_HORIZONTAL_DRAG.get();
/**
* Remove a broken and always present subtitle text that is only
* present with {@link MiniplayerType#MODERN_2}. Bug was fixed in 19.21.
*/
private static final boolean HIDE_BROKEN_MODERN_2_SUBTITLE =
CURRENT_TYPE == MODERN_2 && !IS_19_21_OR_GREATER;
private static final int OPACITY_LEVEL;
static {
@ -190,11 +171,7 @@ public final class MiniplayerPatch {
@Override
public boolean isAvailable() {
MiniplayerType type = Settings.MINIPLAYER_TYPE.get();
return type == MODERN_4
|| (!IS_19_20_OR_GREATER && (type == MODERN_1 || type == MODERN_3))
|| (!IS_19_26_OR_GREATER && type == MODERN_1
&& !Settings.MINIPLAYER_DOUBLE_TAP_ACTION.get() && Settings.MINIPLAYER_DISABLE_DRAG_AND_DROP.get())
|| (IS_19_29_OR_GREATER && type == MODERN_3);
return type == MODERN_4 || type == MODERN_3;
}
@Override
@ -325,7 +302,7 @@ public final class MiniplayerPatch {
return original;
}
return DOUBLE_TAP_ACTION_ENABLED;
return true;
}
/**
@ -435,25 +412,4 @@ public final class MiniplayerPatch {
Logger.printException(() -> "hideMiniplayerSubTexts failure", ex);
}
}
/**
* Injection point.
*/
public static void playerOverlayGroupCreated(View group) {
try {
if (HIDE_BROKEN_MODERN_2_SUBTITLE && MODERN_OVERLAY_SUBTITLE_TEXT != 0) {
if (group instanceof ViewGroup) {
View subtitleText = Utils.getChildView((ViewGroup) group, true,
view -> view.getId() == MODERN_OVERLAY_SUBTITLE_TEXT);
if (subtitleText != null) {
subtitleText.setVisibility(View.GONE);
Logger.printDebug(() -> "Modern overlay subtitle view set to hidden");
}
}
}
} catch (Exception ex) {
Logger.printException(() -> "playerOverlayGroupCreated failure", ex);
}
}
}

View file

@ -2,6 +2,7 @@ package app.revanced.extension.youtube.patches;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
@ -10,7 +11,7 @@ import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class PlayerControlsPatch {
public static WeakReference<View> fullscreenButtonRef = new WeakReference<>(null);
public static WeakReference<ImageView> fullscreenButtonRef = new WeakReference<>(null);
private static boolean fullscreenButtonVisibilityCallbacksExist() {
return false; // Modified during patching if needed.
@ -19,8 +20,8 @@ public class PlayerControlsPatch {
/**
* Injection point.
*/
public static void setFullscreenCloseButton(View button) {
fullscreenButtonRef = new WeakReference<>(button);
public static void setFullscreenCloseButton(ImageView imageButton) {
fullscreenButtonRef = new WeakReference<>(imageButton);
Logger.printDebug(() -> "Fullscreen button set");
if (!fullscreenButtonVisibilityCallbacksExist()) {
@ -29,13 +30,13 @@ public class PlayerControlsPatch {
// Add a global listener, since the protected method
// View#onVisibilityChanged() does not have any call backs.
button.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
imageButton.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
int lastVisibility = View.VISIBLE;
@Override
public void onGlobalLayout() {
try {
final int visibility = button.getVisibility();
final int visibility = imageButton.getVisibility();
if (lastVisibility != visibility) {
lastVisibility = visibility;

View file

@ -81,13 +81,6 @@ public class ShortsAutoplayPatch {
final boolean autoplay;
if (isAppInBackgroundPiPMode()) {
if (!VersionCheckPatch.IS_19_34_OR_GREATER) {
// 19.34+ is required to set background play behavior.
Logger.printDebug(() -> "PiP Shorts not supported, using original repeat behavior");
return original;
}
autoplay = Settings.SHORTS_AUTOPLAY_BACKGROUND.get();
} else {
autoplay = Settings.SHORTS_AUTOPLAY.get();

View file

@ -7,19 +7,6 @@ public class VersionCheckPatch {
return Utils.getAppVersionName().compareTo(version) >= 0;
}
@Deprecated
public static final boolean IS_19_17_OR_GREATER = isVersionOrGreater("19.17.00");
@Deprecated
public static final boolean IS_19_20_OR_GREATER = isVersionOrGreater("19.20.00");
@Deprecated
public static final boolean IS_19_21_OR_GREATER = isVersionOrGreater("19.21.00");
@Deprecated
public static final boolean IS_19_26_OR_GREATER = isVersionOrGreater("19.26.00");
@Deprecated
public static final boolean IS_19_29_OR_GREATER = isVersionOrGreater("19.29.00");
@Deprecated
public static final boolean IS_19_34_OR_GREATER = isVersionOrGreater("19.34.00");
public static final boolean IS_20_21_OR_GREATER = isVersionOrGreater("20.21.00");
public static final boolean IS_20_22_OR_GREATER = isVersionOrGreater("20.22.00");

View file

@ -14,9 +14,7 @@ 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;
@ -31,20 +29,9 @@ 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);
@ -69,30 +56,19 @@ public final class AnnouncementsPatch {
var jsonString = Requester.parseStringAndDisconnect(connection);
var id = -1;
// Parse the ID. Fall-back to raw string if it fails.
int id = Settings.ANNOUNCEMENT_LAST_ID.defaultValue;
try {
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");
final var announcementIDs = new JSONArray(jsonString);
if (announcementIDs.length() == 0) return true;
id = announcementIDs.getJSONObject(0).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().equals(id);
return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
}
public static void showAnnouncement(final Activity context) {
@ -119,22 +95,7 @@ public final class AnnouncementsPatch {
LocalDateTime archivedAt = LocalDateTime.MAX;
Level level = Level.INFO;
try {
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");
final var announcement = new JSONArray(jsonString).getJSONObject(0);
id = announcement.getInt("id");
title = announcement.getString("title");

View file

@ -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/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 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 AnnouncementsRoutes() {
}

View file

@ -1,9 +1,6 @@
package app.revanced.extension.youtube.patches.litho;
import static app.revanced.extension.shared.StringRef.str;
import android.app.Dialog;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

View file

@ -7,7 +7,6 @@ import app.revanced.extension.shared.patches.litho.FilterGroupList.ByteArrayFilt
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.ShortsPlayerState;

View file

@ -1,9 +1,10 @@
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.ANDROID_REEL;
import static app.revanced.extension.shared.spoof.ClientType.IPADOS;
import static app.revanced.extension.shared.spoof.ClientType.VISIONOS;
import java.util.List;
@ -43,11 +44,11 @@ public class SpoofVideoStreamsPatch {
}
List<ClientType> availableClients = List.of(
ANDROID_REEL,
ANDROID_VR_1_43_32,
VISIONOS,
ANDROID_CREATOR
);
ANDROID_CREATOR,
ANDROID_VR_1_43_32,
ANDROID_NO_SDK,
IPADOS);
app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse(
availableClients, client);

View file

@ -1,9 +1,7 @@
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;
@ -83,19 +81,4 @@ 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);
}
}

View file

@ -16,7 +16,6 @@ import java.util.Arrays;
import java.util.Scanner;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.settings.Settings;

View file

@ -11,7 +11,6 @@ import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseActivityHook;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
import app.revanced.extension.youtube.settings.preference.YouTubePreferenceFragment;
import app.revanced.extension.youtube.settings.search.YouTubeSearchViewController;
@ -19,7 +18,7 @@ import app.revanced.extension.youtube.settings.search.YouTubeSearchViewControlle
* Hooks LicenseActivity to inject a custom {@link YouTubePreferenceFragment}
* with a toolbar and search functionality.
*/
@SuppressWarnings("deprecation")
public class YouTubeActivityHook extends BaseActivityHook {
/**
@ -121,17 +120,9 @@ public class YouTubeActivityHook extends BaseActivityHook {
*/
@SuppressWarnings("unused")
public static boolean useCairoSettingsFragment(boolean original) {
// Early targets have layout issues and it's better to always force off.
if (!VersionCheckPatch.IS_19_34_OR_GREATER) {
return false;
}
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
return false;
}
// Spoofing can cause half broken settings menus of old and new settings.
if (SpoofAppVersionPatch.isSpoofingToLessThan("19.35.36")) {
return false;
}
// On the first launch of a clean install, forcing the cairo menu can give a
// half broken appearance because all the preference icons may not be available yet.

View file

@ -19,7 +19,7 @@ import app.revanced.extension.shared.spoof.ClientType;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings({"deprecation", "unused"})
public class SpoofVideoStreamsSideEffectsPreference extends Preference {
public class SpoofStreamingDataSideEffectsPreference extends Preference {
@Nullable
private ClientType currentClientType;
@ -33,19 +33,19 @@ public class SpoofVideoStreamsSideEffectsPreference extends Preference {
Utils.runOnMainThread(this::updateUI);
};
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SpoofVideoStreamsSideEffectsPreference(Context context, AttributeSet attrs) {
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SpoofVideoStreamsSideEffectsPreference(Context context) {
public SpoofStreamingDataSideEffectsPreference(Context context) {
super(context);
}
@ -88,23 +88,27 @@ public class SpoofVideoStreamsSideEffectsPreference 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_playback_failure")
+ '\n' + str("revanced_spoof_video_streams_about_no_audio_tracks")
summary = str("revanced_spoof_video_streams_about_no_audio_tracks")
+ '\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");
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");
default -> Logger.printException(() -> "Unknown client: " + clientType);
}
// 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");
// 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");
}
// Use better formatting for bullet points.

View file

@ -3,6 +3,7 @@ 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;
@ -50,6 +51,7 @@ 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 {
/**

View file

@ -26,6 +26,7 @@ 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.

View file

@ -33,7 +33,6 @@ import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.preference.CustomDialogListPreference;
import app.revanced.extension.shared.settings.preference.ResettableEditTextPreference;
import app.revanced.extension.shared.ui.CustomDialog;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;

View file

@ -4,4 +4,4 @@ org.gradle.parallel = true
android.useAndroidX = true
android.uniquePackageNames = false
kotlin.code.style = official
version = 6.1.0
version = 6.0.0-dev.16

View file

@ -10,10 +10,6 @@ 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" }
@ -22,10 +18,6 @@ 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" }

View file

@ -89,14 +89,10 @@ 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/play/DisablePlayIntegrityKt {
public final class app/revanced/patches/all/misc/playintegrity/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
@ -129,14 +125,6 @@ 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;
}
@ -369,18 +357,10 @@ 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;
}
@ -647,10 +627,6 @@ 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;
}
@ -908,7 +884,6 @@ 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 {
@ -1888,7 +1863,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 ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getGENERAL_LAYOUT ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun getMISC ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;
public final fun 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;

View file

@ -6,7 +6,7 @@ patches {
description = "Patches for ReVanced"
source = "git@github.com:revanced/revanced-patches.git"
author = "ReVanced"
contact = "patches@revanced.app"
contact = "contact@revanced.app"
website = "https://revanced.app"
license = "GNU General Public License v3.0"
}

View file

@ -6,8 +6,7 @@ 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.util.forEachInstructionAsSequence
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
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
@ -34,8 +33,7 @@ val spoofSIMProviderPatch = bytecodePatch(
validator = { it: String? -> it == null || it.uppercase() in countries.values },
)
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
fun isMccMncValid(it: Int?): Boolean = it == null || (it >= 10000 && it <= 999999)
val networkCountryIso by isoCountryPatchOption("Network ISO country code")
@ -63,119 +61,46 @@ 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(
bytecodePatch {
apply {
fun generateRandomNumeric(length: Int) = (1..length).map { ('0'..'9').random() }.joinToString("")
transformInstructionsPatch(
filterMap = { _, _, instruction, instructionIndex ->
if (instruction !is ReferenceInstruction) return@transformInstructionsPatch null
fun String?.computeSpoof(randomizer: () -> String): String? {
if (this.isNullOrBlank()) return null
if (this.equals("random", ignoreCase = true)) return randomizer()
return this
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
}
// 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
)
}
},
replacement?.let { instructionIndex to it }
},
transform = ::transformMethodCall,
),
)
}
private fun transformMethodCall(mutableMethod: MutableMethod, entry: Pair<Int, String>) {
val (index, value) = entry
val nextInstr = mutableMethod.getInstruction<Instruction>(index + 1)
private fun transformMethodCall(
mutableMethod: MutableMethod,
entry: Pair<Int, String>,
) {
val (instructionIndex, methodCallValue) = entry
if (nextInstr.opcode.name != "move-result-object") {
mutableMethod.replaceInstruction(index, "nop")
return
}
// Get the register which would have contained the return value
val register = mutableMethod.getInstruction<OneRegisterInstruction>(instructionIndex + 1).registerA
val register = (nextInstr as OneRegisterInstruction).registerA
mutableMethod.replaceInstruction(index, "const-string v$register, \"$value\"")
mutableMethod.replaceInstruction(index + 1, "nop")
// Replace the move-result instruction with our fake value
mutableMethod.replaceInstruction(
instructionIndex + 1,
"const-string v$register, \"$methodCallValue\"",
)
}
private enum class MethodCall(
@ -229,100 +154,4 @@ 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;"
)
)
}

View file

@ -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.util.forEachInstructionAsSequence
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
@ -19,32 +19,29 @@ val spoofWiFiConnectionPatch = bytecodePatch(
extendWith("extensions/all/misc/connectivity/wifi/spoof/spoof-wifi.rve")
dependsOn(
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,
)
})
}
},
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,
)
},
),
)
}
// 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,
@ -92,13 +89,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;",
@ -135,7 +132,7 @@ private enum class MethodCall(
"registerBestMatchingNetworkCallback",
arrayOf(
"Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;",
),
"V",
@ -143,19 +140,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(
@ -169,7 +166,7 @@ private enum class MethodCall(
"registerNetworkCallback",
arrayOf(
"Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;",
),
"V",
@ -177,13 +174,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(
@ -191,7 +188,7 @@ private enum class MethodCall(
"requestNetwork",
arrayOf(
"Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;",
),
"V",
@ -207,7 +204,7 @@ private enum class MethodCall(
"requestNetwork",
arrayOf(
"Landroid/net/NetworkRequest;",
$$"Landroid/net/ConnectivityManager$NetworkCallback;",
"Landroid/net/ConnectivityManager\$NetworkCallback;",
"Landroid/os/Handler;",
"I",
),
@ -216,7 +213,7 @@ private enum class MethodCall(
UnregisterNetworkCallback1(
"Landroid/net/ConnectivityManager;",
"unregisterNetworkCallback",
arrayOf($$"Landroid/net/ConnectivityManager$NetworkCallback;"),
arrayOf("Landroid/net/ConnectivityManager\$NetworkCallback;"),
"V",
),
UnregisterNetworkCallback2(

View file

@ -8,7 +8,6 @@ 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,
) {

View file

@ -1,138 +0,0 @@
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),
}

View file

@ -1,4 +1,4 @@
package app.revanced.patches.all.misc.play
package app.revanced.patches.all.misc.playintegrity
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/play/DisablePlayIntegrityPatch;"
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/playintegrity/DisablePlayIntegrityPatch;"
private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/content/Context;",

View file

@ -1,28 +0,0 @@
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")
}
}
)
}
}

View file

@ -1,70 +0,0 @@
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",
),
}

View file

@ -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("clusters") {
name("unsafeParseFromJson")
internal val BytecodePatchContext.exploreResponseJsonParserMethodMatch by composingFirstMethod("ExploreTopicalFeedResponse") {
name("parseFromJson")
instructions("sectional_items"())
}

View file

@ -11,8 +11,8 @@ internal val BytecodePatchContext.initializeNavigationButtonsListMethod by getti
}
internal val BytecodePatchContext.navigationButtonsEnumMethod by gettingFirstImmutableMethodDeclaratively(
"fragment_clips",
"FEED",
"fragment_feed",
"fragment_news",
"SEARCH",
"fragment_search",
)

View file

@ -16,6 +16,6 @@ internal val FEED_ITEM_KEYS_TO_BE_HIDDEN = arrayOf(
"suggested_users",
)
internal val BytecodePatchContext.feedItemParseFromJsonMethodMatch by composingFirstMethod("feed_item_type") {
internal val BytecodePatchContext.feedItemParseFromJsonMethodMatch by composingFirstMethod("FeedItem") {
instructions(predicates = unorderedAllOf(predicates = FEED_ITEM_KEYS_TO_BE_HIDDEN.map { it() }.toTypedArray()))
}

View file

@ -9,7 +9,7 @@ import app.revanced.patcher.patch.BytecodePatchContext
import com.android.tools.smali.dexlib2.Opcode
internal val BytecodePatchContext.permalinkResponseJsonParserMethodMatch by composingFirstMethod {
name("unsafeParseFromJson")
name("parseFromJson")
instructions("permalink"())
opcodes(
Opcode.NEW_INSTANCE,
@ -19,16 +19,16 @@ internal val BytecodePatchContext.permalinkResponseJsonParserMethodMatch by comp
}
internal val BytecodePatchContext.storyUrlResponseJsonParserMethodMatch by composingFirstMethod {
name("unsafeParseFromJson")
name("parseFromJson")
instructions("story_item_to_share_url"())
}
internal val BytecodePatchContext.profileUrlResponseJsonParserMethodMatch by composingFirstMethod {
name("unsafeParseFromJson")
name("parseFromJson")
instructions("profile_to_share_url"())
}
internal val BytecodePatchContext.liveUrlResponseJsonParserMethodMatch by composingFirstMethod {
name("unsafeParseFromJson")
name("parseFromJson")
instructions("live_to_share_url"())
}

View file

@ -1,22 +0,0 @@
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()
}
}

View file

@ -1,24 +0,0 @@
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",
)

View file

@ -1,20 +0,0 @@
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)
}
}

View file

@ -1,16 +0,0 @@
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())
}

View file

@ -1,21 +0,0 @@
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()
}
}

View file

@ -1,28 +0,0 @@
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;")
}

View file

@ -1,16 +1,13 @@
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")
}

View file

@ -3,6 +3,7 @@ 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
@ -125,14 +126,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;"
@ -147,11 +148,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
""",
)
}
@ -161,37 +162,16 @@ 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 (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"
}
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.",
)
}
}
@ -332,19 +312,16 @@ internal fun baseCustomBrandingPatch(
activityAliasNameWithIntents,
).childNodes
// 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"
)
}
// 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",
)
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 2 // 1 indexing.
val enabledNameIndex = if (useCustomName) numberOfPresetAppNames else 1 // 1 indexing.
val enabledIconIndex = if (useCustomIcon) iconStyleNames.size else 0 // 0 indexing.
for (appNameIndex in 1..numberOfPresetAppNames) {
@ -359,7 +336,7 @@ internal fun baseCustomBrandingPatch(
iconMipmapName = originalLauncherIconName,
appNameIndex = appNameIndex,
useCustomName = useCustomNameLabel,
enabled = false,
enabled = (appNameIndex == 1),
intentFilters,
),
)

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