Compare commits

..

90 Commits

Author SHA1 Message Date
semantic-release-bot
e2a7e25c66 chore: Release v5.21.0-dev.6 [skip ci]
# [5.21.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.5...v5.21.0-dev.6) (2025-04-19)

### Bug Fixes

* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([77ea5c4](77ea5c4033))
2025-04-19 16:36:29 +00:00
LisoUseInAIKyrios
77ea5c4033 fix(YouTube - Wide search bar): Do not force phone layout for tablet devices (#4827) 2025-04-19 18:33:12 +02:00
github-actions[bot]
6eea2354f5 chore: Sync translations (#4828) 2025-04-19 12:00:28 +02:00
semantic-release-bot
cce21c4d4a chore: Release v5.21.0-dev.5 [skip ci]
# [5.21.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.4...v5.21.0-dev.5) (2025-04-18)

### Bug Fixes

* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([5e069bd](5e069bde90))
2025-04-18 09:18:56 +00:00
1fexd
5e069bde90 fix: Hide ADB status patch (#4814) 2025-04-18 11:15:05 +02:00
github-actions[bot]
6a49208982 chore: Sync translations (#4822) 2025-04-18 09:55:15 +02:00
Snow
404bb2e86e chore(YouTube - Return YouTube Dislike): Clarify settings text for estimated likes (#4818) 2025-04-17 19:10:37 +02:00
github-actions[bot]
bc869fe359 chore: Sync translations (#4820) 2025-04-17 19:09:35 +02:00
semantic-release-bot
7d166cf82c chore: Release v5.21.0-dev.4 [skip ci]
# [5.21.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.3...v5.21.0-dev.4) (2025-04-17)

### Bug Fixes

* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([8efbaae](8efbaae65c))
2025-04-17 17:06:19 +00:00
LisoUseInAIKyrios
8efbaae65c fix(YouTube - Disable auto captions): Correctly hide captions with YT 20.12 2025-04-17 19:02:52 +02:00
LisoUseInAIKyrios
e27ab23279 chore(YouTube): Add debug text to toast text if user forgot they enabled debugging 2025-04-16 14:56:31 +02:00
semantic-release-bot
ce42604083 chore: Release v5.21.0-dev.3 [skip ci]
# [5.21.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.2...v5.21.0-dev.3) (2025-04-16)

### Features

* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([fc6282d](fc6282d0cb))
2025-04-16 11:09:43 +00:00
cyberboh
fc6282d0cb feat(X / Twitter): Support version 10.86.0-release.0 (#4805) 2025-04-16 13:06:30 +02:00
semantic-release-bot
0559fc7fd0 chore: Release v5.21.0-dev.2 [skip ci]
# [5.21.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.1...v5.21.0-dev.2) (2025-04-16)

### Features

* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([7cc6995](7cc6995682))
2025-04-16 08:58:30 +00:00
1fexd
7cc6995682 feat: Add Hide ADB status patch (#4585)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-04-16 10:55:07 +02:00
semantic-release-bot
476f13bf98 chore: Release v5.21.0-dev.1 [skip ci]
# [5.21.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0-dev.1) (2025-04-16)

### Features

* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([f216e16](f216e16c0b))
2025-04-16 08:28:15 +00:00
LisoUseInAIKyrios
f216e16c0b feat(YouTube): Support version 20.12.46 (#4779) 2025-04-16 10:24:35 +02:00
semantic-release-bot
f2a8789649 chore: Release v5.20.1 [skip ci]
## [5.20.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1) (2025-04-15)

### Bug Fixes

* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([2393d0a](2393d0a8f5))
2025-04-15 16:41:52 +00:00
LisoUseInAIKyrios
5973b64f52 chore: Merge branch dev to main (#4801) 2025-04-15 18:38:29 +02:00
semantic-release-bot
102036706e chore: Release v5.20.1-dev.1 [skip ci]
## [5.20.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1-dev.1) (2025-04-15)

### Bug Fixes

* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([2393d0a](2393d0a8f5))
2025-04-15 16:34:50 +00:00
LisoUseInAIKyrios
2393d0a8f5 fix(Spotify - Custom theme): Support latest app target (#4800) 2025-04-15 18:30:55 +02:00
LisoUseInAIKyrios
aea29b9522 refactor(Spotify - Spoof package info): Replace installer package name of legacy target (#4797) 2025-04-15 17:38:47 +02:00
semantic-release-bot
4db8ef7079 chore: Release v5.20.0 [skip ci]
# [5.20.0](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0) (2025-04-15)

### Bug Fixes

* **Duolingo - Hide ads:**  Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([3d6958f](3d6958f157))
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](50f5b1ac54))
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([0f687ec](0f687ecfd3))
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([b4e8540](b4e8540bbc))

### Features

* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([9db67a6](9db67a6eb2))
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([c510931](c510931eb0))
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([5ebd449](5ebd449f1f))
2025-04-15 11:02:58 +00:00
oSumAtrIX
7fbd26ccad chore: Merge branch dev to main (#4781) 2025-04-15 12:59:44 +02:00
github-actions[bot]
91995ea01d chore: Sync translations (#4795)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-04-15 12:57:13 +02:00
semantic-release-bot
86f867fe97 chore: Release v5.20.0-dev.7 [skip ci]
# [5.20.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.6...v5.20.0-dev.7) (2025-04-15)

### Bug Fixes

* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([0f687ec](0f687ecfd3))
2025-04-15 10:56:12 +00:00
oSumAtrIX
0f687ecfd3 fix(Spotify): Fix login by replacing Spoof signature patch with new Spoof package info patch (#4794)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-04-15 12:53:26 +02:00
semantic-release-bot
6c8b7d09c1 chore: Release v5.20.0-dev.6 [skip ci]
# [5.20.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.5...v5.20.0-dev.6) (2025-04-15)

### Bug Fixes

* **Duolingo - Hide ads:**  Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([3d6958f](3d6958f157))
2025-04-15 07:27:06 +00:00
hoodles
3d6958f157 fix(Duolingo - Hide ads): Support lastest app release (#4790) 2025-04-15 09:24:29 +02:00
semantic-release-bot
43d7cc7374 chore: Release v5.20.0-dev.5 [skip ci]
# [5.20.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.4...v5.20.0-dev.5) (2025-04-14)

### Features

* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([5ebd449](5ebd449f1f))
2025-04-14 08:46:51 +00:00
Kamil Kras
5ebd449f1f feat(YouTube - Swipe controls): Add option to change volume swipe sensitivity (step size) (#4557)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-04-14 10:43:21 +02:00
semantic-release-bot
346a061df8 chore: Release v5.20.0-dev.4 [skip ci]
# [5.20.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.3...v5.20.0-dev.4) (2025-04-14)

### Bug Fixes

* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](50f5b1ac54))
2025-04-14 08:10:14 +00:00
semantic-release-bot
13e490a422 chore: Release v5.20.0-dev.3 [skip ci]
# [5.20.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.2...v5.20.0-dev.3) (2025-04-13)

### Bug Fixes

* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([b4e8540](b4e8540bbc))
2025-04-13 22:11:10 +00:00
LisoUseInAIKyrios
b4e8540bbc fix(YouTube - Remove background playback restrictions): Restore PiP button functionality after screen is unlocked 2025-04-14 00:07:35 +02:00
github-actions[bot]
775c1baec2 chore: Sync translations (#4784) 2025-04-14 00:05:17 +02:00
semantic-release-bot
9419fb8ec4 chore: Release v5.20.0-dev.2 [skip ci]
# [5.20.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.1...v5.20.0-dev.2) (2025-04-13)

### Features

* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([c510931](c510931eb0))
2025-04-13 19:29:33 +00:00
Nuckyz
c510931eb0 feat(Spotify - Custom theme): Add option to use unmodified player background gradient (#4741)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-04-13 21:26:59 +02:00
semantic-release-bot
7160699384 chore: Release v5.20.0-dev.1 [skip ci]
# [5.20.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0-dev.1) (2025-04-13)

### Features

* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([9db67a6](9db67a6eb2))
2025-04-13 16:26:18 +00:00
LisoUseInAIKyrios
9db67a6eb2 feat: Add Set target SDK version 34 patch (Disable edge-to-edge display) (#4780)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-04-13 18:23:45 +02:00
semantic-release-bot
e684d87dd3 chore: Release v5.19.1 [skip ci]
## [5.19.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1) (2025-04-12)

### Bug Fixes

* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([d451bc6](d451bc6d6d))
* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](89d44da171))
2025-04-12 19:30:34 +00:00
LisoUseInAIKyrios
2d1752a1eb chore: Merge branch dev to main (#4772) 2025-04-12 21:27:49 +02:00
semantic-release-bot
c9ff7092fe chore: Release v5.19.1-dev.2 [skip ci]
## [5.19.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.1-dev.1...v5.19.1-dev.2) (2025-04-12)

### Bug Fixes

* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([d451bc6](d451bc6d6d))
2025-04-12 19:23:37 +00:00
LisoUseInAIKyrios
d451bc6d6d fix(Google Photos): Restore patching with ReVanced Manager (#4773) 2025-04-12 21:20:36 +02:00
semantic-release-bot
741fd36872 chore: Release v5.19.1-dev.1 [skip ci]
## [5.19.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1-dev.1) (2025-04-12)

### Bug Fixes

* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](89d44da171))
2025-04-12 19:04:28 +00:00
semantic-release-bot
517f8cf59a chore: Release v5.19.0 [skip ci]
# [5.19.0](https://github.com/ReVanced/revanced-patches/compare/v5.18.0...v5.19.0) (2025-04-12)

### Bug Fixes

* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([e6ae55f](e6ae55fa99))
* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([f68d06d](f68d06dbf3))
* **Spotify - Custom theme:** Override more color resources ([#4690](https://github.com/ReVanced/revanced-patches/issues/4690)) ([49c8499](49c849979f))
* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([d573386](d573386e0f))
* **Spotify:** Remove ads sections from home ([#4722](https://github.com/ReVanced/revanced-patches/issues/4722)) ([628d184](628d18489c))
* **Twitter - Hide recommended users:** Make hiding work again by filtering for new entryId prefix ([#4456](https://github.com/ReVanced/revanced-patches/issues/4456)) ([3d68c06](3d68c06146))
* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([6005c97](6005c97bf5))
* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([1abed31](1abed31968))
* **YouTube - Remove background playback restrictions:** Do not show media controls when playing Shorts from the feed ([318b55b](318b55b8fe))
* **YouTube - Return YouTube Dislike:** Correctly update label after disliking a Short with 20.07 ([0bff207](0bff207efc))
* **YouTube - Return YouTube Dislike:** Fix inconsistent label after disliking a Short ([3d67d90](3d67d90473))
* **YouTube - Seekbar:** Correctly hide the feed seekbar with target 20.07 ([2035c9e](2035c9e2e9))
* **YouTube:** Combine multiple seekbar patches into a single patch ([#4705](https://github.com/ReVanced/revanced-patches/issues/4705)) ([37984b8](37984b8b99))

### Features

* **Angulus:** Add `Hide ads` patch ([#4604](https://github.com/ReVanced/revanced-patches/issues/4604)) ([2d7b1b0](2d7b1b09af))
* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([de0d11f](de0d11fcfb))
* **Photomath:** Support latest version ([#4672](https://github.com/ReVanced/revanced-patches/issues/4672)) ([ef3d5ba](ef3d5bafd5))
* **Proton Mail:** Add `Remove 'Sent from' signature` patch ([#4514](https://github.com/ReVanced/revanced-patches/issues/4514)) ([8ed9d5b](8ed9d5bf08))
* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([854a18f](854a18ff72))
* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([e30f593](e30f593af0))
* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([654587a](654587a75e))
* **YouTube - Settings:** Add icons to the ReVanced settings ([#4496](https://github.com/ReVanced/revanced-patches/issues/4496)) ([fdefb67](fdefb67d02))
2025-04-12 13:27:56 +00:00
oSumAtrIX
b78fb24435 chore: Merge branch dev to main (#4699)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
Co-authored-by: Denis Kerner <11692788+dtricks@users.noreply.github.com>
Co-authored-by: Denis Kerner <denis.kerner@vodafone.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: Dawid Krajcarz <80264606+drobotk@users.noreply.github.com>
Co-authored-by: Jakub Blažej <trogper@gmail.com>
Co-authored-by: MarcaD <152095496+MarcaDian@users.noreply.github.com>
Co-authored-by: Aoife McCullough <92225779+AoifeMcCull@users.noreply.github.com>
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
Co-authored-by: user5095 <140261471+user5095@users.noreply.github.com>
Co-authored-by: Brosssh <44944126+Brosssh@users.noreply.github.com>
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: oSumAtrIX <github@osumatrix.me>
2025-04-12 15:24:43 +02:00
github-actions[bot]
a3faccb21b chore: Sync translations (#4766)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-04-12 15:19:43 +02:00
semantic-release-bot
5f0fddc122 chore: Release v5.19.0-dev.17 [skip ci]
# [5.19.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.16...v5.19.0-dev.17) (2025-04-12)

### Features

* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([854a18f](854a18ff72))
2025-04-12 12:56:01 +00:00
oSumAtrIX
854a18ff72 feat(Spotify): Add Check environment patch (#4765) 2025-04-12 14:53:27 +02:00
semantic-release-bot
b994a16bdc chore: Release v5.19.0-dev.16 [skip ci]
# [5.19.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.15...v5.19.0-dev.16) (2025-04-11)

### Bug Fixes

* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([f68d06d](f68d06dbf3))
2025-04-11 20:15:09 +00:00
LisoUseInAIKyrios
f68d06dbf3 fix(Google Photos): Remove obsolete non functional patch Restore hidden 'Back up while charging' toggle (#4764) 2025-04-11 22:12:13 +02:00
semantic-release-bot
04c6a2e5f4 chore: Release v5.19.0-dev.15 [skip ci]
# [5.19.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.14...v5.19.0-dev.15) (2025-04-11)

### Bug Fixes

* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([e6ae55f](e6ae55fa99))
2025-04-11 19:42:49 +00:00
LisoUseInAIKyrios
e6ae55fa99 fix(Google Photos - Restore hidden 'Back up while charging' toggle): Constrain to last working app target (#4761) 2025-04-11 21:40:17 +02:00
github-actions[bot]
fb62474ff4 chore: Sync translations (#4763)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-04-11 21:39:34 +02:00
semantic-release-bot
e084f01fd0 chore: Release v5.19.0-dev.14 [skip ci]
# [5.19.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.13...v5.19.0-dev.14) (2025-04-11)

### Bug Fixes

* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([d573386](d573386e0f))
2025-04-11 19:36:38 +00:00
Brosssh
d573386e0f fix(Spotify - Unlock Spotify Premium): Remove restrictions for Google voice assistant (#4702)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-04-11 21:33:59 +02:00
semantic-release-bot
0f3aeb35e5 chore: Release v5.19.0-dev.13 [skip ci]
# [5.19.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.12...v5.19.0-dev.13) (2025-04-11)

### Features

* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([e30f593](e30f593af0))
2025-04-11 19:33:53 +00:00
LisoUseInAIKyrios
e30f593af0 feat(Spotify): Add limited support for version 8.6.98.900 (last version that supports Kenwood and Pioneer car stereos) (#4750) 2025-04-11 21:30:47 +02:00
semantic-release-bot
df965b8a9b chore: Release v5.19.0-dev.12 [skip ci]
# [5.19.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.11...v5.19.0-dev.12) (2025-04-11)

### Features

* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([654587a](654587a75e))
2025-04-11 18:40:46 +00:00
user5095
654587a75e feat(Strava - Disable subscription suggestions): Make compatible with latest version (#4739) 2025-04-11 20:38:07 +02:00
LisoUseInAIKyrios
9956833781 chore(YouTube): Remove obsolete targets overlooked in last version bump 2025-04-11 20:05:45 +02:00
semantic-release-bot
c585b26188 chore: Release v5.19.0-dev.11 [skip ci]
# [5.19.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.10...v5.19.0-dev.11) (2025-04-10)

### Features

* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([de0d11f](de0d11fcfb))
2025-04-10 21:11:47 +00:00
Dawid Krajcarz
de0d11fcfb feat(Messenger): Add Remove Meta AI tab patch (#4726) 2025-04-10 23:08:43 +02:00
semantic-release-bot
d321504fcf chore: Release v5.19.0-dev.10 [skip ci]
# [5.19.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.9...v5.19.0-dev.10) (2025-04-10)

### Bug Fixes

* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([6005c97](6005c97bf5))
2025-04-10 17:13:49 +00:00
LisoUseInAIKyrios
6005c97bf5 fix(YouTube - Hide layout components): Do not hide video description music/game links if hide horizontal shelves is enabled 2025-04-10 19:11:11 +02:00
semantic-release-bot
e404d84c83 chore: Release v5.19.0-dev.9 [skip ci]
# [5.19.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.8...v5.19.0-dev.9) (2025-04-10)

### Bug Fixes

* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([1abed31](1abed31968))
2025-04-10 14:08:34 +00:00
LisoUseInAIKyrios
1abed31968 fix(YouTube - Hide player flyout menu items): Show more detailed summary text for 'Hide Audio track' if using Android spoof client (#4756) 2025-04-10 16:05:42 +02:00
semantic-release-bot
a75a88d3c6 chore: Release v5.19.0-dev.8 [skip ci]
# [5.19.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.7...v5.19.0-dev.8) (2025-04-09)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Fix inconsistent label after disliking a Short ([3d67d90](3d67d90473))
2025-04-09 18:38:26 +00:00
LisoUseInAIKyrios
3d67d90473 fix(YouTube - Return YouTube Dislike): Fix inconsistent label after disliking a Short 2025-04-09 20:32:27 +02:00
github-actions[bot]
fa1e137a43 chore: Sync translations (#4753) 2025-04-09 20:31:46 +02:00
semantic-release-bot
ac71a53c73 chore: Release v5.19.0-dev.7 [skip ci]
# [5.19.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.6...v5.19.0-dev.7) (2025-04-07)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Correctly update label after disliking a Short with 20.07 ([0bff207](0bff207efc))
2025-04-07 14:05:29 +00:00
LisoUseInAIKyrios
0bff207efc fix(YouTube - Return YouTube Dislike): Correctly update label after disliking a Short with 20.07 2025-04-07 16:02:44 +02:00
semantic-release-bot
e1a8b388a5 chore: Release v5.19.0-dev.6 [skip ci]
# [5.19.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.5...v5.19.0-dev.6) (2025-04-04)

### Bug Fixes

* **Spotify:** Remove ads sections from home ([#4722](https://github.com/ReVanced/revanced-patches/issues/4722)) ([628d184](628d18489c))
2025-04-04 12:35:54 +00:00
Nuckyz
628d18489c fix(Spotify): Remove ads sections from home (#4722)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-04-04 14:32:40 +02:00
semantic-release-bot
36772b8b2e chore: Release v5.19.0-dev.5 [skip ci]
# [5.19.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.4...v5.19.0-dev.5) (2025-04-02)

### Bug Fixes

* **Spotify - Custom theme:** Override more color resources ([#4690](https://github.com/ReVanced/revanced-patches/issues/4690)) ([49c8499](49c849979f))
2025-04-02 17:17:59 +00:00
Nuckyz
49c849979f fix(Spotify - Custom theme): Override more color resources (#4690) 2025-04-02 19:14:17 +02:00
semantic-release-bot
0bdb8cdf2b chore: Release v5.19.0-dev.4 [skip ci]
# [5.19.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.3...v5.19.0-dev.4) (2025-04-02)

### Bug Fixes

* **YouTube - Seekbar:** Correctly hide the feed seekbar with target 20.07 ([2035c9e](2035c9e2e9))
2025-04-02 15:58:10 +00:00
LisoUseInAIKyrios
2035c9e2e9 fix(YouTube - Seekbar): Correctly hide the feed seekbar with target 20.07 2025-04-02 17:54:17 +02:00
semantic-release-bot
7cb38fd3fc chore: Release v5.19.0-dev.3 [skip ci]
# [5.19.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.2...v5.19.0-dev.3) (2025-04-02)

### Features

* **Proton Mail:** Add `Remove 'Sent from' signature` patch ([#4514](https://github.com/ReVanced/revanced-patches/issues/4514)) ([8ed9d5b](8ed9d5bf08))
2025-04-02 10:00:45 +00:00
Aoife McCullough
8ed9d5bf08 feat(Proton Mail): Add Remove 'Sent from' signature patch (#4514) 2025-04-02 11:58:10 +02:00
semantic-release-bot
cd467d6244 chore: Release v5.19.0-dev.2 [skip ci]
# [5.19.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.1...v5.19.0-dev.2) (2025-04-02)

### Features

* **YouTube - Settings:** Add icons to the ReVanced settings ([#4496](https://github.com/ReVanced/revanced-patches/issues/4496)) ([fdefb67](fdefb67d02))
2025-04-02 09:42:46 +00:00
MarcaD
fdefb67d02 feat(YouTube - Settings): Add icons to the ReVanced settings (#4496) 2025-04-02 11:39:53 +02:00
semantic-release-bot
5274cd18f0 chore: Release v5.19.0-dev.1 [skip ci]
# [5.19.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.18.1-dev.2...v5.19.0-dev.1) (2025-04-01)

### Bug Fixes

* **Twitter - Hide recommended users:** Make hiding work again by filtering for new entryId prefix ([#4456](https://github.com/ReVanced/revanced-patches/issues/4456)) ([3d68c06](3d68c06146))

### Features

* **Angulus:** Add `Hide ads` patch ([#4604](https://github.com/ReVanced/revanced-patches/issues/4604)) ([2d7b1b0](2d7b1b09af))
* **Photomath:** Support latest version ([#4672](https://github.com/ReVanced/revanced-patches/issues/4672)) ([ef3d5ba](ef3d5bafd5))
2025-04-01 22:33:38 +00:00
Jakub Blažej
3d68c06146 fix(Twitter - Hide recommended users): Make hiding work again by filtering for new entryId prefix (#4456) 2025-04-02 00:31:02 +02:00
Dawid Krajcarz
ef3d5bafd5 feat(Photomath): Support latest version (#4672) 2025-04-02 00:28:57 +02:00
Denis Kerner
2d7b1b09af feat(Angulus): Add Hide ads patch (#4604)
Co-authored-by: Denis Kerner <denis.kerner@vodafone.com>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-04-02 00:27:59 +02:00
semantic-release-bot
0572d48fde chore: Release v5.18.1-dev.2 [skip ci]
## [5.18.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.18.1-dev.1...v5.18.1-dev.2) (2025-04-01)

### Bug Fixes

* **YouTube:** Combine multiple seekbar patches into a single patch ([#4705](https://github.com/ReVanced/revanced-patches/issues/4705)) ([37984b8](37984b8b99))
2025-04-01 19:00:31 +00:00
LisoUseInAIKyrios
37984b8b99 fix(YouTube): Combine multiple seekbar patches into a single patch (#4705) 2025-04-01 20:57:03 +02:00
github-actions[bot]
6e63193f06 chore: Sync translations (#4713) 2025-04-01 20:51:16 +02:00
LisoUseInAIKyrios
b2384b22a5 refactor: Add more opcodes to findFreeRegister 2025-04-01 20:17:00 +02:00
270 changed files with 3633 additions and 1357 deletions

View File

@@ -1,3 +1,310 @@
# [5.21.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.5...v5.21.0-dev.6) (2025-04-19)
### Bug Fixes
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
# [5.21.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.4...v5.21.0-dev.5) (2025-04-18)
### Bug Fixes
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
# [5.21.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.3...v5.21.0-dev.4) (2025-04-17)
### Bug Fixes
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
# [5.21.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.2...v5.21.0-dev.3) (2025-04-16)
### Features
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([655b390](https://github.com/ReVanced/revanced-patches/commit/655b39043ad77efcb4380de67c3f603666e7bc49))
# [5.21.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.1...v5.21.0-dev.2) (2025-04-16)
### Features
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
# [5.21.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0-dev.1) (2025-04-16)
### Features
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
## [5.20.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1) (2025-04-15)
### Bug Fixes
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
## [5.20.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1-dev.1) (2025-04-15)
### Bug Fixes
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
# [5.20.0](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0) (2025-04-15)
### Bug Fixes
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
### Features
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
# [5.20.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.6...v5.20.0-dev.7) (2025-04-15)
### Bug Fixes
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
# [5.20.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.5...v5.20.0-dev.6) (2025-04-15)
### Bug Fixes
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
# [5.20.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.4...v5.20.0-dev.5) (2025-04-14)
### Features
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
# [5.20.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.3...v5.20.0-dev.4) (2025-04-14)
### Bug Fixes
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
# [5.20.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.2...v5.20.0-dev.3) (2025-04-13)
### Bug Fixes
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
# [5.20.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.1...v5.20.0-dev.2) (2025-04-13)
### Features
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
# [5.20.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0-dev.1) (2025-04-13)
### Features
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
## [5.19.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1) (2025-04-12)
### Bug Fixes
* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([3e18e86](https://github.com/ReVanced/revanced-patches/commit/3e18e868bbd9fd0600fe81a7fe8767b4bd89a00e))
* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](https://github.com/ReVanced/revanced-patches/commit/89d44da171c3f56f13112d1d82bc4ea4a56c7c06))
## [5.19.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.1-dev.1...v5.19.1-dev.2) (2025-04-12)
### Bug Fixes
* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([3e18e86](https://github.com/ReVanced/revanced-patches/commit/3e18e868bbd9fd0600fe81a7fe8767b4bd89a00e))
## [5.19.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1-dev.1) (2025-04-12)
### Bug Fixes
* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](https://github.com/ReVanced/revanced-patches/commit/89d44da171c3f56f13112d1d82bc4ea4a56c7c06))
# [5.19.0](https://github.com/ReVanced/revanced-patches/compare/v5.18.0...v5.19.0) (2025-04-12)
### Bug Fixes
* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([152bb7c](https://github.com/ReVanced/revanced-patches/commit/152bb7c3ee7cf36bc07460e7a3444631ec540441))
* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([56e48f4](https://github.com/ReVanced/revanced-patches/commit/56e48f4c89da51f81ff11a79a164eaa5b440690e))
* **Spotify - Custom theme:** Override more color resources ([#4690](https://github.com/ReVanced/revanced-patches/issues/4690)) ([d7a7a0b](https://github.com/ReVanced/revanced-patches/commit/d7a7a0b982dbafa181b04f984a5f7618fb067c2a))
* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([106202f](https://github.com/ReVanced/revanced-patches/commit/106202f9ebb7699c4ba4ae46b82133e35f1ac6b9))
* **Spotify:** Remove ads sections from home ([#4722](https://github.com/ReVanced/revanced-patches/issues/4722)) ([0b9a5e7](https://github.com/ReVanced/revanced-patches/commit/0b9a5e7f89a89d971762b3539166d4f145111481))
* **Twitter - Hide recommended users:** Make hiding work again by filtering for new entryId prefix ([#4456](https://github.com/ReVanced/revanced-patches/issues/4456)) ([ff846b0](https://github.com/ReVanced/revanced-patches/commit/ff846b0b7ef5060caaffedb08c1f901172f5b2d1))
* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([3864f35](https://github.com/ReVanced/revanced-patches/commit/3864f3550153617e23ad9979fb543d8a7fb4dc0a))
* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([b67bbb2](https://github.com/ReVanced/revanced-patches/commit/b67bbb299669336addb68cf52a8ce5b39c68cec0))
* **YouTube - Remove background playback restrictions:** Do not show media controls when playing Shorts from the feed ([2ed675c](https://github.com/ReVanced/revanced-patches/commit/2ed675cdd058fb5876381a9d30dee5263f6b2e26))
* **YouTube - Return YouTube Dislike:** Correctly update label after disliking a Short with 20.07 ([0bb3e32](https://github.com/ReVanced/revanced-patches/commit/0bb3e32244fa10809aee5c4e549f77ed4054537e))
* **YouTube - Return YouTube Dislike:** Fix inconsistent label after disliking a Short ([ea92a2e](https://github.com/ReVanced/revanced-patches/commit/ea92a2e36c7aab3bd115f7d0ec40467179485b32))
* **YouTube - Seekbar:** Correctly hide the feed seekbar with target 20.07 ([ddc6e4c](https://github.com/ReVanced/revanced-patches/commit/ddc6e4c34fe35fa34bd859bf34e25645a23dbdc9))
* **YouTube:** Combine multiple seekbar patches into a single patch ([#4705](https://github.com/ReVanced/revanced-patches/issues/4705)) ([503b7eb](https://github.com/ReVanced/revanced-patches/commit/503b7eb8d413ef7f248394f128f3b2a6f3192ba6))
### Features
* **Angulus:** Add `Hide ads` patch ([#4604](https://github.com/ReVanced/revanced-patches/issues/4604)) ([87c86b5](https://github.com/ReVanced/revanced-patches/commit/87c86b53a91b0054ac892a3f02bbe7bf83bbf813))
* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([e3fad97](https://github.com/ReVanced/revanced-patches/commit/e3fad97484d7eb962aeb53d44a0047b34a881071))
* **Photomath:** Support latest version ([#4672](https://github.com/ReVanced/revanced-patches/issues/4672)) ([8e16483](https://github.com/ReVanced/revanced-patches/commit/8e1648322948151e4565fb0d86e0f37d0a02d73f))
* **Proton Mail:** Add `Remove 'Sent from' signature` patch ([#4514](https://github.com/ReVanced/revanced-patches/issues/4514)) ([34c14c9](https://github.com/ReVanced/revanced-patches/commit/34c14c9b443092824d035afd77adb678c6f89e3e))
* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([6d7101c](https://github.com/ReVanced/revanced-patches/commit/6d7101cb2e546e01a934eff9cad1264367aeafe3))
* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([a3fde87](https://github.com/ReVanced/revanced-patches/commit/a3fde874af993125ba7a741820e7bd48e3641b84))
* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([649a2c0](https://github.com/ReVanced/revanced-patches/commit/649a2c06161c72a2040b179dbed5b415847d7527))
* **YouTube - Settings:** Add icons to the ReVanced settings ([#4496](https://github.com/ReVanced/revanced-patches/issues/4496)) ([d0c85f0](https://github.com/ReVanced/revanced-patches/commit/d0c85f044083d720c63a8ea4ff15d42eefeb9db7))
# [5.19.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.16...v5.19.0-dev.17) (2025-04-12)
### Features
* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([6d7101c](https://github.com/ReVanced/revanced-patches/commit/6d7101cb2e546e01a934eff9cad1264367aeafe3))
# [5.19.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.15...v5.19.0-dev.16) (2025-04-11)
### Bug Fixes
* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([56e48f4](https://github.com/ReVanced/revanced-patches/commit/56e48f4c89da51f81ff11a79a164eaa5b440690e))
# [5.19.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.14...v5.19.0-dev.15) (2025-04-11)
### Bug Fixes
* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([152bb7c](https://github.com/ReVanced/revanced-patches/commit/152bb7c3ee7cf36bc07460e7a3444631ec540441))
# [5.19.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.13...v5.19.0-dev.14) (2025-04-11)
### Bug Fixes
* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([106202f](https://github.com/ReVanced/revanced-patches/commit/106202f9ebb7699c4ba4ae46b82133e35f1ac6b9))
# [5.19.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.12...v5.19.0-dev.13) (2025-04-11)
### Features
* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([a3fde87](https://github.com/ReVanced/revanced-patches/commit/a3fde874af993125ba7a741820e7bd48e3641b84))
# [5.19.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.11...v5.19.0-dev.12) (2025-04-11)
### Features
* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([649a2c0](https://github.com/ReVanced/revanced-patches/commit/649a2c06161c72a2040b179dbed5b415847d7527))
# [5.19.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.10...v5.19.0-dev.11) (2025-04-10)
### Features
* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([e3fad97](https://github.com/ReVanced/revanced-patches/commit/e3fad97484d7eb962aeb53d44a0047b34a881071))
# [5.19.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.9...v5.19.0-dev.10) (2025-04-10)
### Bug Fixes
* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([3864f35](https://github.com/ReVanced/revanced-patches/commit/3864f3550153617e23ad9979fb543d8a7fb4dc0a))
# [5.19.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.8...v5.19.0-dev.9) (2025-04-10)
### Bug Fixes
* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([b67bbb2](https://github.com/ReVanced/revanced-patches/commit/b67bbb299669336addb68cf52a8ce5b39c68cec0))
# [5.19.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.7...v5.19.0-dev.8) (2025-04-09)
### Bug Fixes
* **YouTube - Return YouTube Dislike:** Fix inconsistent label after disliking a Short ([ea92a2e](https://github.com/ReVanced/revanced-patches/commit/ea92a2e36c7aab3bd115f7d0ec40467179485b32))
# [5.19.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.6...v5.19.0-dev.7) (2025-04-07)
### Bug Fixes
* **YouTube - Return YouTube Dislike:** Correctly update label after disliking a Short with 20.07 ([0bb3e32](https://github.com/ReVanced/revanced-patches/commit/0bb3e32244fa10809aee5c4e549f77ed4054537e))
# [5.19.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.5...v5.19.0-dev.6) (2025-04-04)
### Bug Fixes
* **Spotify:** Remove ads sections from home ([#4722](https://github.com/ReVanced/revanced-patches/issues/4722)) ([0b9a5e7](https://github.com/ReVanced/revanced-patches/commit/0b9a5e7f89a89d971762b3539166d4f145111481))
# [5.19.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.4...v5.19.0-dev.5) (2025-04-02)
### Bug Fixes
* **Spotify - Custom theme:** Override more color resources ([#4690](https://github.com/ReVanced/revanced-patches/issues/4690)) ([d7a7a0b](https://github.com/ReVanced/revanced-patches/commit/d7a7a0b982dbafa181b04f984a5f7618fb067c2a))
# [5.19.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.3...v5.19.0-dev.4) (2025-04-02)
### Bug Fixes
* **YouTube - Seekbar:** Correctly hide the feed seekbar with target 20.07 ([ddc6e4c](https://github.com/ReVanced/revanced-patches/commit/ddc6e4c34fe35fa34bd859bf34e25645a23dbdc9))
# [5.19.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.2...v5.19.0-dev.3) (2025-04-02)
### Features
* **Proton Mail:** Add `Remove 'Sent from' signature` patch ([#4514](https://github.com/ReVanced/revanced-patches/issues/4514)) ([34c14c9](https://github.com/ReVanced/revanced-patches/commit/34c14c9b443092824d035afd77adb678c6f89e3e))
# [5.19.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.1...v5.19.0-dev.2) (2025-04-02)
### Features
* **YouTube - Settings:** Add icons to the ReVanced settings ([#4496](https://github.com/ReVanced/revanced-patches/issues/4496)) ([d0c85f0](https://github.com/ReVanced/revanced-patches/commit/d0c85f044083d720c63a8ea4ff15d42eefeb9db7))
# [5.19.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.18.1-dev.2...v5.19.0-dev.1) (2025-04-01)
### Bug Fixes
* **Twitter - Hide recommended users:** Make hiding work again by filtering for new entryId prefix ([#4456](https://github.com/ReVanced/revanced-patches/issues/4456)) ([ff846b0](https://github.com/ReVanced/revanced-patches/commit/ff846b0b7ef5060caaffedb08c1f901172f5b2d1))
### Features
* **Angulus:** Add `Hide ads` patch ([#4604](https://github.com/ReVanced/revanced-patches/issues/4604)) ([87c86b5](https://github.com/ReVanced/revanced-patches/commit/87c86b53a91b0054ac892a3f02bbe7bf83bbf813))
* **Photomath:** Support latest version ([#4672](https://github.com/ReVanced/revanced-patches/issues/4672)) ([8e16483](https://github.com/ReVanced/revanced-patches/commit/8e1648322948151e4565fb0d86e0f37d0a02d73f))
## [5.18.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.18.1-dev.1...v5.18.1-dev.2) (2025-04-01)
### Bug Fixes
* **YouTube:** Combine multiple seekbar patches into a single patch ([#4705](https://github.com/ReVanced/revanced-patches/issues/4705)) ([503b7eb](https://github.com/ReVanced/revanced-patches/commit/503b7eb8d413ef7f248394f128f3b2a6f3192ba6))
## [5.18.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.18.0...v5.18.1-dev.1) (2025-03-31) ## [5.18.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.18.0...v5.18.1-dev.1) (2025-03-31)

View File

@@ -0,0 +1,16 @@
android {
namespace = "app.revanced.extension"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
compileOnly(libs.annotation)
}

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,28 @@
package app.revanced.extension.all.misc.hide.adb;
import android.content.ContentResolver;
import android.provider.Settings;
import java.util.Arrays;
import java.util.List;
@SuppressWarnings("unused")
public final class HideAdbPatch {
private static final List<String> SPOOF_SETTINGS = Arrays.asList("adb_enabled", "adb_wifi_enabled", "development_settings_enabled");
public static int getInt(ContentResolver cr, String name) throws Settings.SettingNotFoundException {
if (SPOOF_SETTINGS.contains(name)) {
return 0;
}
return Settings.Global.getInt(cr, name);
}
public static int getInt(ContentResolver cr, String name, int def) {
if (SPOOF_SETTINGS.contains(name)) {
return 0;
}
return Settings.Global.getInt(cr, name, def);
}
}

View File

@@ -1,4 +1,4 @@
package app.revanced.extension.all.connectivity.wifi.spoof; package app.revanced.extension.all.misc.connectivity.wifi.spoof;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;

View File

@@ -1,4 +1,4 @@
package app.revanced.extension.all.screencapture.removerestriction; package app.revanced.extension.all.misc.screencapture.removerestriction;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.os.Build; import android.os.Build;

View File

@@ -1,4 +1,4 @@
package app.revanced.extension.all.screenshot.removerestriction; package app.revanced.extension.all.misc.screenshot.removerestriction;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;

View File

@@ -9,6 +9,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Color;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@@ -799,4 +800,14 @@ public class Utils {
builder.getContext().setTheme(editTextDialogStyle); builder.getContext().setTheme(editTextDialogStyle);
} }
} }
/**
* Parse a color resource or hex code to an int representation of the color.
*/
public static int getColorFromString(String colorString) throws IllegalArgumentException, Resources.NotFoundException {
if (colorString.startsWith("#")) {
return Color.parseColor(colorString);
}
return getResourceColor(colorString);
}
} }

View File

@@ -23,6 +23,11 @@ public class BaseSettings {
public static final EnumSetting<AppLanguage> REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true, "revanced_language_user_dialog_message"); public static final EnumSetting<AppLanguage> REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true, "revanced_language_user_dialog_message");
/**
* Use the icons declared in the preferences created during patching. If no icons or styles are declared then this setting does nothing.
*/
public static final BooleanSetting SHOW_MENU_ICONS = new BooleanSetting("revanced_show_menu_icons", TRUE, true);
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_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
public static final EnumSetting<AppLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability()); public static final EnumSetting<AppLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS)); public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));

View File

@@ -86,7 +86,6 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
} }
}; };
/** /**
* Initialize this instance, and do any custom behavior. * Initialize this instance, and do any custom behavior.
* <p> * <p>
@@ -95,7 +94,10 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
* so all app specific {@link Setting} instances are loaded before this method returns. * so all app specific {@link Setting} instances are loaded before this method returns.
*/ */
protected void initialize() { protected void initialize() {
final var identifier = Utils.getResourceIdentifier("revanced_prefs", "xml"); String preferenceResourceName = BaseSettings.SHOW_MENU_ICONS.get()
? "revanced_prefs_icons"
: "revanced_prefs";
final var identifier = Utils.getResourceIdentifier(preferenceResourceName, "xml");
if (identifier == 0) return; if (identifier == 0) return;
addPreferencesFromResource(identifier); addPreferencesFromResource(identifier);

View File

@@ -204,7 +204,7 @@ public class StreamingDataRequest {
// but empty response body does. // but empty response body does.
if (connection.getContentLength() == 0) { if (connection.getContentLength() == 0) {
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) { if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
Utils.showToastShort("Ignoring empty spoof stream client: " + clientType); Utils.showToastShort("Debug: Ignoring empty spoof stream client " + clientType);
} }
} else { } else {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream()); try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());

View File

@@ -0,0 +1,22 @@
package app.revanced.extension.spotify.layout.theme;
import android.graphics.Color;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
@SuppressWarnings("unused")
public final class CustomThemePatch {
/**
* Injection point.
*/
public static long getThemeColor(String colorString) {
try {
return Utils.getColorFromString(colorString);
} catch (Exception ex) {
Logger.printException(() -> "Invalid custom color: " + colorString, ex);
return Color.BLACK;
}
}
}

View File

@@ -0,0 +1,7 @@
package com.spotify.home.evopage.homeapi.proto;
public final class Section {
public static final int VIDEO_BRAND_AD_FIELD_NUMBER = 20;
public static final int IMAGE_BRAND_AD_FIELD_NUMBER = 21;
public int featureTypeCase_;
}

View File

@@ -0,0 +1,8 @@
package com.spotify.useraccount.v1;
/**
* Used for target 8.6.98.900. Class is still present in newer app targets.
*/
public class AccountAttribute {
public Object value_;
}

View File

@@ -9,7 +9,6 @@ import app.revanced.extension.tiktok.settings.preference.categories.DownloadsPre
import app.revanced.extension.tiktok.settings.preference.categories.FeedFilterPreferenceCategory; import app.revanced.extension.tiktok.settings.preference.categories.FeedFilterPreferenceCategory;
import app.revanced.extension.tiktok.settings.preference.categories.ExtensionPreferenceCategory; import app.revanced.extension.tiktok.settings.preference.categories.ExtensionPreferenceCategory;
import app.revanced.extension.tiktok.settings.preference.categories.SimSpoofPreferenceCategory; import app.revanced.extension.tiktok.settings.preference.categories.SimSpoofPreferenceCategory;
import org.jetbrains.annotations.NotNull;
/** /**
* Preference fragment for ReVanced settings * Preference fragment for ReVanced settings

View File

@@ -163,7 +163,7 @@ internal object TwiFucker {
private fun JSONObject.entryIsWhoToFollow(): Boolean = private fun JSONObject.entryIsWhoToFollow(): Boolean =
optString("entryId").let { optString("entryId").let {
it.startsWith("whoToFollow-") || it.startsWith("who-to-follow-") || it.startsWith("connect-module-") it.startsWith("whoToFollow-") || it.startsWith("who-to-follow-") || it.startsWith("connect-module-") || it.startsWith("who-to-subscribe-")
} }
private fun JSONObject.itemContainsPromotedUser(): Boolean = private fun JSONObject.itemContainsPromotedUser(): Boolean =

View File

@@ -45,13 +45,24 @@ public class ThemeHelper {
return "@color/yt_black3"; return "@color/yt_black3";
} }
private static int getThemeColor(String resourceName, int defaultColor) {
try {
return Utils.getColorFromString(resourceName);
} catch (Exception ex) {
// User entered an invalid custom theme color.
// Normally this should never be reached, and no localized strings are needed.
Utils.showToastLong("Invalid custom theme color: " + resourceName);
return defaultColor;
}
}
/** /**
* @return The dark theme color as specified by the Theme patch (if included), * @return The dark theme color as specified by the Theme patch (if included),
* or the dark mode background color unpatched YT uses. * or the dark mode background color unpatched YT uses.
*/ */
public static int getDarkThemeColor() { public static int getDarkThemeColor() {
if (darkThemeColor == null) { if (darkThemeColor == null) {
darkThemeColor = getColorInt(darkThemeResourceName()); darkThemeColor = getThemeColor(darkThemeResourceName(), Color.BLACK);
} }
return darkThemeColor; return darkThemeColor;
} }
@@ -71,18 +82,11 @@ public class ThemeHelper {
*/ */
public static int getLightThemeColor() { public static int getLightThemeColor() {
if (lightThemeColor == null) { if (lightThemeColor == null) {
lightThemeColor = getColorInt(lightThemeResourceName()); lightThemeColor = getThemeColor(lightThemeResourceName(), Color.WHITE);
} }
return lightThemeColor; return lightThemeColor;
} }
private static int getColorInt(String colorString) {
if (colorString.startsWith("#")) {
return Color.parseColor(colorString);
}
return Utils.getResourceColor(colorString);
}
public static int getBackgroundColor() { public static int getBackgroundColor() {
return isDarkTheme() ? getDarkThemeColor() : getLightThemeColor(); return isDarkTheme() ? getDarkThemeColor() : getLightThemeColor();
} }
@@ -96,6 +100,6 @@ public class ThemeHelper {
? "yt_black3" ? "yt_black3"
: "yt_white1"; : "yt_white1";
return getColorInt(colorName); return Utils.getColorFromString(colorName);
} }
} }

View File

@@ -1,20 +1,23 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DisableAutoCaptionsPatch { public class DisableAutoCaptionsPatch {
/** private static volatile boolean captionsButtonStatus;
* Used by injected code. Do not delete.
*/
public static boolean captionsButtonDisabled;
public static boolean autoCaptionsEnabled() { /**
return Settings.AUTO_CAPTIONS.get() * Injection point.
// Do not use auto captions for Shorts. */
&& ShortsPlayerState.isOpen(); public static boolean disableAutoCaptions() {
return Settings.DISABLE_AUTO_CAPTIONS.get() && !captionsButtonStatus;
} }
/**
* Injection point.
*/
public static void setCaptionsButtonStatus(boolean status) {
captionsButtonStatus = status;
}
} }

View File

@@ -2,20 +2,16 @@ package app.revanced.extension.youtube.patches;
import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike.Vote; import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike.Vote;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable;
import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
@@ -60,12 +56,12 @@ public class ReturnYouTubeDislikePatch {
private static volatile ReturnYouTubeDislike lastLithoShortsVideoData; private static volatile ReturnYouTubeDislike lastLithoShortsVideoData;
/** /**
* Because the litho Shorts spans are created after {@link ReturnYouTubeDislikeFilterPatch} * Because litho Shorts spans are created offscreen after {@link ReturnYouTubeDislikeFilterPatch}
* detects the video ids, after the user votes the litho will update * detects the video ids, but the current Short can arbitrarily reload the same span,
* but {@link #lastLithoShortsVideoData} is not the correct data to use. * then use the {@link #lastLithoShortsVideoData} if this value is greater than zero.
* If this is true, then instead use {@link #currentVideoData}.
*/ */
private static volatile boolean lithoShortsShouldUseCurrentData; @GuardedBy("ReturnYouTubeDislikePatch.class")
private static int useLithoShortsVideoDataCount;
/** /**
* Last video id prefetched. Field is to prevent prefetching the same video id multiple times in a row. * Last video id prefetched. Field is to prevent prefetching the same video id multiple times in a row.
@@ -83,12 +79,28 @@ public class ReturnYouTubeDislikePatch {
private static void clearData() { private static void clearData() {
currentVideoData = null; currentVideoData = null;
lastLithoShortsVideoData = null; lastLithoShortsVideoData = null;
lithoShortsShouldUseCurrentData = false; synchronized (ReturnYouTubeDislike.class) {
useLithoShortsVideoDataCount = 0;
}
// Rolling number text should not be cleared, // Rolling number text should not be cleared,
// as it's used if incognito Short is opened/closed // as it's used if incognito Short is opened/closed
// while a regular video is on screen. // while a regular video is on screen.
} }
/**
* @return If {@link #useLithoShortsVideoDataCount} was greater than zero.
*/
private static boolean decrementUseLithoDataIfNeeded() {
synchronized (ReturnYouTubeDislikePatch.class) {
if (useLithoShortsVideoDataCount > 0) {
useLithoShortsVideoDataCount--;
return true;
}
return false;
}
}
// //
// Litho player for both regular videos and Shorts. // Litho player for both regular videos and Shorts.
@@ -152,10 +164,13 @@ public class ReturnYouTubeDislikePatch {
return getShortsSpan(original, true); return getShortsSpan(original, true);
} }
if (conversionContextString.contains("|shorts_like_button.eml") if (conversionContextString.contains("|shorts_like_button.eml")) {
&& !Utils.containsNumber(original)) { if (!Utils.containsNumber(original)) {
Logger.printDebug(() -> "Replacing hidden likes count"); Logger.printDebug(() -> "Replacing hidden likes count");
return getShortsSpan(original, false); return getShortsSpan(original, false);
} else {
decrementUseLithoDataIfNeeded();
}
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onLithoTextLoaded failure", ex); Logger.printException(() -> "onLithoTextLoaded failure", ex);
@@ -170,7 +185,14 @@ public class ReturnYouTubeDislikePatch {
return original; return original;
} }
ReturnYouTubeDislike videoData = lastLithoShortsVideoData; final ReturnYouTubeDislike videoData;
if (decrementUseLithoDataIfNeeded()) {
// New Short is loading off screen.
videoData = lastLithoShortsVideoData;
} else {
videoData = currentVideoData;
}
if (videoData == null) { if (videoData == null) {
// The Shorts litho video id filter did not detect the video id. // The Shorts litho video id filter did not detect the video id.
// This is normal in incognito mode, but otherwise is abnormal. // This is normal in incognito mode, but otherwise is abnormal.
@@ -178,19 +200,6 @@ public class ReturnYouTubeDislikePatch {
return original; return original;
} }
// Use the correct dislikes data after voting.
if (lithoShortsShouldUseCurrentData) {
if (isDislikesSpan) {
lithoShortsShouldUseCurrentData = false;
}
videoData = currentVideoData;
if (videoData == null) {
Logger.printException(() -> "currentVideoData is null"); // Should never happen
return original;
}
Logger.printDebug(() -> "Using current video data for litho span");
}
return isDislikesSpan return isDislikesSpan
? videoData.getDislikeSpanForShort((Spanned) original) ? videoData.getDislikeSpanForShort((Spanned) original)
: videoData.getLikeSpanForShort((Spanned) original); : videoData.getLikeSpanForShort((Spanned) original);
@@ -445,7 +454,10 @@ public class ReturnYouTubeDislikePatch {
ReturnYouTubeDislike videoData = ReturnYouTubeDislike.getFetchForVideoId(videoId); ReturnYouTubeDislike videoData = ReturnYouTubeDislike.getFetchForVideoId(videoId);
videoData.setVideoIdIsShort(true); videoData.setVideoIdIsShort(true);
lastLithoShortsVideoData = videoData; lastLithoShortsVideoData = videoData;
lithoShortsShouldUseCurrentData = false; synchronized (ReturnYouTubeDislikePatch.class) {
// Use litho Shorts data for the next like and dislike spans.
useLithoShortsVideoDataCount = 2;
}
} }
private static boolean videoIdIsSame(@Nullable ReturnYouTubeDislike fetch, @Nullable String videoId) { private static boolean videoIdIsSame(@Nullable ReturnYouTubeDislike fetch, @Nullable String videoId) {
@@ -480,13 +492,6 @@ public class ReturnYouTubeDislikePatch {
for (Vote v : Vote.values()) { for (Vote v : Vote.values()) {
if (v.value == vote) { if (v.value == vote) {
videoData.sendVote(v); videoData.sendVote(v);
if (isNoneHiddenOrMinimized) {
if (lastLithoShortsVideoData != null) {
lithoShortsShouldUseCurrentData = true;
}
}
return; return;
} }
} }

View File

@@ -1,11 +1,48 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import android.content.res.Resources;
import android.util.TypedValue;
import android.view.View;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class WideSearchbarPatch { public final class WideSearchbarPatch {
private static final Boolean WIDE_SEARCHBAR_ENABLED = Settings.WIDE_SEARCHBAR.get();
/**
* Injection point.
*/
public static boolean enableWideSearchbar(boolean original) { public static boolean enableWideSearchbar(boolean original) {
return Settings.WIDE_SEARCHBAR.get() || original; return WIDE_SEARCHBAR_ENABLED || original;
}
/**
* Injection point.
*/
public static void setActionBar(View view) {
try {
if (!WIDE_SEARCHBAR_ENABLED) return;
View searchBarView = Utils.getChildViewByResourceName(view, "search_bar");
final int paddingLeft = searchBarView.getPaddingLeft();
final int paddingRight = searchBarView.getPaddingRight();
final int paddingTop = searchBarView.getPaddingTop();
final int paddingBottom = searchBarView.getPaddingBottom();
final int paddingStart = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
8, Resources.getSystem().getDisplayMetrics());
if (Utils.isRightToLeftTextLayout()) {
searchBarView.setPadding(paddingLeft, paddingTop, paddingStart, paddingBottom);
} else {
searchBarView.setPadding(paddingStart, paddingTop, paddingRight, paddingBottom);
}
} catch (Exception ex) {
Logger.printException(() -> "setActionBar failure", ex);
}
} }
} }

View File

@@ -454,16 +454,20 @@ public final class LayoutComponentsFilter extends Filter {
} }
private static boolean hideShelves() { private static boolean hideShelves() {
// If the player is opened while library is selected, // Horizontal shelves are used for music/game links in video descriptions,
// then filter any recommendations below the player. // such as https://youtube.com/watch?v=W8kI1na3S2M
if (PlayerType.getCurrent().isMaximizedOrFullscreen() if (PlayerType.getCurrent().isMaximizedOrFullscreen()) {
// Or if the search is active while library is selected, then also filter. return false;
|| NavigationBar.isSearchBarActive()) { }
// Must check search bar after player type, since search results
// can be in the background behind an open player.
if (NavigationBar.isSearchBarActive()) {
return true; return true;
} }
// Do not hide if the navigation back button is visible, // Do not hide if the navigation back button is visible,
// otherwise the content shelves in the YouTube Movie/Courses pages is hidden. // otherwise the content shelves in the explore/music/courses pages are hidde.
if (NavigationBar.isBackButtonVisible()) { if (NavigationBar.isBackButtonVisible()) {
return false; return false;
} }

View File

@@ -2,12 +2,25 @@ package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class PlayerFlyoutMenuItemsFilter extends Filter { public class PlayerFlyoutMenuItemsFilter extends Filter {
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
private static final boolean AVAILABLE_ON_LAUNCH = SpoofVideoStreamsPatch.notSpoofingToAndroid();
@Override
public boolean isAvailable() {
// Check conditions of launch and now. Otherwise if spoofing is changed
// without a restart the setting will show as available when it's not.
return AVAILABLE_ON_LAUNCH && SpoofVideoStreamsPatch.notSpoofingToAndroid();
}
}
private final ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList(); private final ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList();
private final ByteArrayFilterGroup exception; private final ByteArrayFilterGroup exception;

View File

@@ -226,6 +226,7 @@ public final class SeekbarColorPatch {
} }
private static String loadRawResourceAsString(int resourceId) { private static String loadRawResourceAsString(int resourceId) {
//noinspection CharsetObjectCanBeUsed
try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId); try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId);
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) { Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
return scanner.next(); return scanner.next();
@@ -281,6 +282,20 @@ public final class SeekbarColorPatch {
/** /**
* Injection point. * Injection point.
* 19.49+
*/
public static int[] getPlayerLinearGradient(int[] original, int x0, int y1) {
// This hook is used for both the player and the feed.
// Feed usage always has x0 and y1 value of zero, and the player is always non zero.
if (HIDE_SEEKBAR_THUMBNAIL_ENABLED && x0 == 0 && y1 == 0) {
return HIDDEN_SEEKBAR_GRADIENT_COLORS;
}
return getPlayerLinearGradient(original);
}
/**
* Injection point.
* Pre 19.49
*/ */
public static int[] getPlayerLinearGradient(int[] original) { public static int[] getPlayerLinearGradient(int[] original) {
return SEEKBAR_CUSTOM_COLOR_ENABLED return SEEKBAR_CUSTOM_COLOR_ENABLED

View File

@@ -19,6 +19,7 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4; import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType; import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability; import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
import static app.revanced.extension.youtube.patches.components.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
@@ -123,6 +124,7 @@ public class Settings extends BaseSettings {
// Player // Player
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE); public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE); public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
public static final BooleanSetting DISABLE_AUTO_CAPTIONS = new BooleanSetting("revanced_disable_auto_captions", FALSE, true);
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true); public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE); public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED); public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
@@ -198,7 +200,7 @@ public class Settings extends BaseSettings {
// Player flyout menu items // Player flyout menu items
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE); public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE); public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE); public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE, new HideAudioFlyoutMenuAvailability());
public static final BooleanSetting HIDE_PLAYER_FLYOUT_CAPTIONS = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE); public static final BooleanSetting HIDE_PLAYER_FLYOUT_CAPTIONS = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
public static final BooleanSetting HIDE_PLAYER_FLYOUT_HELP = new BooleanSetting("revanced_hide_player_flyout_help", TRUE); public static final BooleanSetting HIDE_PLAYER_FLYOUT_HELP = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE); public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
@@ -293,7 +295,6 @@ public class Settings extends BaseSettings {
// Misc // Misc
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE); public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false); public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
public static final BooleanSetting AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE); public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE); public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false); public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
@@ -318,6 +319,7 @@ public class Settings extends BaseSettings {
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true, public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true, public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true, public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
@@ -401,6 +403,7 @@ public class Settings extends BaseSettings {
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033"); private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE); private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE); private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
private static final BooleanSetting DEPRECATED_AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
static { static {
// region Migration // region Migration
@@ -454,6 +457,11 @@ public class Settings extends BaseSettings {
SPOOF_APP_VERSION_TARGET.resetToDefault(); SPOOF_APP_VERSION_TARGET.resetToDefault();
} }
if (!DEPRECATED_AUTO_CAPTIONS.isSetToDefault()) {
DISABLE_AUTO_CAPTIONS.save(true);
DEPRECATED_AUTO_CAPTIONS.resetToDefault();
}
// endregion // endregion
// region SB import/export callbacks // region SB import/export callbacks

View File

@@ -0,0 +1,36 @@
package app.revanced.extension.youtube.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import android.content.Context;
import android.preference.SwitchPreference;
import android.util.AttributeSet;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
@SuppressWarnings({"deprecation", "unused"})
public class HideAudioFlyoutMenuPreference extends SwitchPreference {
{
// Audio menu is not available if spoofing to Android client type.
if (!SpoofVideoStreamsPatch.notSpoofingToAndroid()) {
String summary = str("revanced_hide_player_flyout_audio_track_not_available");
setSummary(summary);
setSummaryOn(summary);
setSummaryOff(summary);
}
}
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HideAudioFlyoutMenuPreference(Context context) {
super(context);
}
}

View File

@@ -1,6 +1,5 @@
package app.revanced.extension.youtube.swipecontrols package app.revanced.extension.youtube.swipecontrols
import android.content.Context
import android.graphics.Color import android.graphics.Color
import app.revanced.extension.shared.StringRef.str import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils import app.revanced.extension.shared.Utils
@@ -9,12 +8,8 @@ import app.revanced.extension.youtube.shared.PlayerType
/** /**
* provider for configuration for volume and brightness swipe controls * provider for configuration for volume and brightness swipe controls
*
* @param context the context to create in
*/ */
class SwipeControlsConfigurationProvider( class SwipeControlsConfigurationProvider {
private val context: Context,
) {
//region swipe enable //region swipe enable
/** /**
* should swipe controls be enabled? (global setting) * should swipe controls be enabled? (global setting)
@@ -60,6 +55,23 @@ class SwipeControlsConfigurationProvider(
*/ */
val swipeMagnitudeThreshold: Int val swipeMagnitudeThreshold: Int
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get() get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
/**
* How much volume will change by single swipe.
* If it is set to 0, it will reset to the default value because 0 would disable swiping.
* */
val volumeSwipeSensitivity: Int
get() {
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
if (sensitivity < 1) {
Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
return Settings.SWIPE_VOLUME_SENSITIVITY.get()
}
return sensitivity
}
//endregion //endregion
//region overlay adjustments //region overlay adjustments

View File

@@ -127,7 +127,7 @@ class SwipeControlsHostActivity : Activity() {
private fun initialize() { private fun initialize() {
// create controllers // create controllers
printDebug { "initializing swipe controls controllers" } printDebug { "initializing swipe controls controllers" }
config = SwipeControlsConfigurationProvider(this) config = SwipeControlsConfigurationProvider()
keys = VolumeKeysController(this) keys = VolumeKeysController(this)
audio = createAudioController() audio = createAudioController()
screen = createScreenController() screen = createScreenController()

View File

@@ -41,7 +41,7 @@ class VolumeKeysController(
private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean { private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean {
if (event.action == KeyEvent.ACTION_DOWN) { if (event.action == KeyEvent.ACTION_DOWN) {
controller.audio?.apply { controller.audio?.apply {
volume += if (volumeUp) 1 else -1 volume += controller.config.volumeSwipeSensitivity * if (volumeUp) 1 else -1
controller.overlay.onVolumeChanged(volume, maxVolume) controller.overlay.onVolumeChanged(volume, maxVolume)
} }
} }

View File

@@ -24,6 +24,7 @@ abstract class BaseGestureController(
controller.overlay, controller.overlay,
10, 10,
1, 1,
controller.config.volumeSwipeSensitivity,
) { ) {
/** /**

View File

@@ -41,6 +41,7 @@ interface VolumeAndBrightnessScroller {
* @param overlayController overlay controller instance * @param overlayController overlay controller instance
* @param volumeDistance unit distance for volume scrolling, in dp * @param volumeDistance unit distance for volume scrolling, in dp
* @param brightnessDistance unit distance for brightness scrolling, in dp * @param brightnessDistance unit distance for brightness scrolling, in dp
* @param volumeSwipeSensitivity how much volume will change by single swipe
*/ */
class VolumeAndBrightnessScrollerImpl( class VolumeAndBrightnessScrollerImpl(
context: Context, context: Context,
@@ -49,6 +50,7 @@ class VolumeAndBrightnessScrollerImpl(
private val overlayController: SwipeControlsOverlay, private val overlayController: SwipeControlsOverlay,
volumeDistance: Int = 10, volumeDistance: Int = 10,
brightnessDistance: Int = 1, brightnessDistance: Int = 1,
private val volumeSwipeSensitivity: Int,
) : VolumeAndBrightnessScroller { ) : VolumeAndBrightnessScroller {
// region volume // region volume
@@ -60,7 +62,7 @@ class VolumeAndBrightnessScrollerImpl(
), ),
) { _, _, direction -> ) { _, _, direction ->
volumeController?.run { volumeController?.run {
volume += direction volume += direction * volumeSwipeSensitivity
overlayController.onVolumeChanged(volume, maxVolume) overlayController.onVolumeChanged(volume, maxVolume)
} }
} }

View File

@@ -25,7 +25,7 @@ class SwipeControlsOverlayLayout(
private val config: SwipeControlsConfigurationProvider, private val config: SwipeControlsConfigurationProvider,
) : RelativeLayout(context), SwipeControlsOverlay { ) : RelativeLayout(context), SwipeControlsOverlay {
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context)) constructor(context: Context) : this(context, SwipeControlsConfigurationProvider())
// Drawable icons for brightness and volume // Drawable icons for brightness and volume
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto") private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true org.gradle.parallel = true
android.useAndroidX = true android.useAndroidX = true
kotlin.code.style = official kotlin.code.style = official
version = 5.18.1-dev.1 version = 5.21.0-dev.6

View File

@@ -2,6 +2,10 @@ public final class app/revanced/patches/all/misc/activity/exportall/ExportAllAct
public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch; public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
} }
public final class app/revanced/patches/all/misc/adb/HideAdbPatchKt {
public static final fun getHideAdbStatusPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt { public final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt {
public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch; public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -108,6 +112,10 @@ public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveSha
public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch; public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
} }
public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt {
public static final fun getSetTargetSdkVersion34 ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall { public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall {
public abstract fun getDefinedClassName ()Ljava/lang/String; public abstract fun getDefinedClassName ()Ljava/lang/String;
public abstract fun getMethodName ()Ljava/lang/String; public abstract fun getMethodName ()Ljava/lang/String;
@@ -132,6 +140,10 @@ public final class app/revanced/patches/amazon/DeepLinkingPatchKt {
public static final fun getDeepLinkingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getDeepLinkingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/angulus/ads/RemoveAdsPatchKt {
public static final fun getAngulusPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatchKt { public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatchKt {
public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -268,6 +280,10 @@ public final class app/revanced/patches/messenger/inputfield/DisableTypingIndica
public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/messenger/navbar/RemoveMetaAITabPatchKt {
public static final fun getRemoveMetaAITabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt { public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt {
public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -396,6 +412,10 @@ public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatchKt {
public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatchKt { public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatchKt {
public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -556,7 +576,9 @@ public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
} }
public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt { public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt {
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook; public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
public static final fun sharedExtensionPatch (Ljava/lang/String;[Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch; public static final fun sharedExtensionPatch (Ljava/lang/String;[Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch; public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
@@ -824,6 +846,10 @@ public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatchKt {
public static final fun getSpoofPackageInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt { public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -1096,6 +1122,14 @@ public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideT
public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatchKt {
public static final fun getHideSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarPatchKt {
public static final fun getSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatchKt { public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatchKt {
public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@@ -1386,6 +1420,8 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
public static final fun is_20_07_or_greater ()Z public static final fun is_20_07_or_greater ()Z
public static final fun is_20_09_or_greater ()Z public static final fun is_20_09_or_greater ()Z
public static final fun is_20_10_or_greater ()Z public static final fun is_20_10_or_greater ()Z
public static final fun is_20_14_or_greater ()Z
public static final fun is_20_15_or_greater ()Z
} }
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt { public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
@@ -1499,7 +1535,11 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
} }
public final class app/revanced/util/BytecodeUtilsKt { public final class app/revanced/util/BytecodeUtilsKt {
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
public static final fun findFreeRegister (Lcom/android/tools/smali/dexlib2/iface/Method;I[I)I
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List; public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List; public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List; public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
@@ -1526,9 +1566,17 @@ public final class app/revanced/util/BytecodeUtilsKt {
public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)I public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstResourceId (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I public static final fun indexOfFirstResourceId (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I

View File

@@ -0,0 +1,75 @@
package app.revanced.patches.all.misc.adb
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/all/misc/hide/adb/HideAdbPatch;"
private val SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/provider/Settings\$Global;",
"getInt",
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;"),
"I"
)
private val SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE = ImmutableMethodReference(
"Landroid/provider/Settings\$Global;",
"getInt",
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;", "I"),
"I"
)
private fun MethodReference.anyMethodSignatureMatches(vararg anyOf: MethodReference): Boolean {
return anyOf.any {
MethodUtil.methodSignaturesMatch(it, this)
}
}
@Suppress("unused")
val hideAdbStatusPatch = bytecodePatch(
name = "Hide ADB status",
description = "Hides enabled development settings and/or ADB.",
use = false,
) {
extendWith("extensions/all/misc/adb/hide-adb.rve")
dependsOn(
transformInstructionsPatch(
filterMap = filterMap@{ classDef, method, instruction, instructionIndex ->
val reference = instruction
.takeIf { it.opcode == Opcode.INVOKE_STATIC }
?.getReference<MethodReference>()
?.takeIf {
it.anyMethodSignatureMatches(it,
SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE,
SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE
)
}
?: return@filterMap null
Triple(instruction as Instruction35c, instructionIndex, reference.parameterTypes)
},
transform = { method, entry ->
val (instruction, index, parameterTypes) = entry
val parameterString = parameterTypes.joinToString(separator = "")
val registerString = when (parameterTypes.size) {
2 -> "v${instruction.registerC}, v${instruction.registerD}"
else -> "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}"
}
method.replaceInstruction(
index,
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->getInt($parameterString)I"
)
}
)
)
}

View File

@@ -6,7 +6,7 @@ import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch" "Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"

View File

@@ -25,7 +25,7 @@ private val removeCaptureRestrictionResourcePatch = resourcePatch(
} }
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch" "Lapp/revanced/extension/all/misc/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused") @Suppress("unused")

View File

@@ -10,7 +10,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
"Lapp/revanced/extension/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch" "Lapp/revanced/extension/all/misc/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;" private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused") @Suppress("unused")

View File

@@ -0,0 +1,48 @@
package app.revanced.patches.all.misc.targetSdk
import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.getNode
import org.w3c.dom.Element
import java.util.logging.Logger
@Suppress("unused")
val setTargetSdkVersion34 = resourcePatch(
name = "Set target SDK version 34",
description = "Changes the target SDK to version 34 (Android 14). " +
"For devices running Android 15+, this will disable edge-to-edge display.",
use = false,
) {
execute {
val targetSdkOverride = 34 // Android 14.
document("AndroidManifest.xml").use { document ->
fun getLogger() = Logger.getLogger(this::class.java.name)
// Ideally, the override should only be applied if the existing target is higher.
// But since ApkTool does not add targetSdkVersion to the decompiled AndroidManifest,
// there is no way to check targetSdkVersion. Instead, check compileSdkVersion and print a warning.
try {
val manifestElement = document.getNode("manifest") as Element
val compileSdkVersion = Integer.parseInt(
manifestElement.getAttribute("android:compileSdkVersion")
)
if (compileSdkVersion <= targetSdkOverride) {
getLogger().warning(
"This app does not appear to use a target SDK above $targetSdkOverride: " +
"(compileSdkVersion: $compileSdkVersion)"
)
}
} catch (_: Exception) {
getLogger().warning("Could not check compileSdkVersion")
}
// Change targetSdkVersion to override value.
document.getElementsByTagName("manifest").item(0).let {
var element = it.ownerDocument.createElement("uses-sdk")
element.setAttribute("android:targetSdkVersion", targetSdkOverride.toString())
it.appendChild(element)
}
}
}
}

View File

@@ -0,0 +1,18 @@
package app.revanced.patches.angulus.ads
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
// Keywords to search for in case the method name changes:
// dailyMeasurementCount
// lastMeasurementDate
// dailyAdResetCount
// MeasurementPrefs
// This fingerprint targets a method that returns the daily measurement count.
// This method is used to determine if the user has reached the daily limit of measurements.
internal val getDailyMeasurementCountFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE)
returns("I")
strings("dailyMeasurementCount")
}

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.angulus.ads
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val angulusPatch = bytecodePatch(name = "Hide ads") {
compatibleWith("com.drinkplusplus.angulus")
execute {
// Always return 0 as the daily measurement count.
getDailyMeasurementCountFingerprint.method.returnEarly()
}
}

View File

@@ -12,7 +12,8 @@ internal val initializeMonetizationDebugSettingsFingerprint = fingerprint {
"Z", // useDebugBilling "Z", // useDebugBilling
"Z", // showManageSubscriptions "Z", // showManageSubscriptions
"Z", // alwaysShowSuperAds "Z", // alwaysShowSuperAds
"Lcom/duolingo/debug/FamilyQuestOverride;", // matches "Lcom/duolingo/debug/FamilyQuestOverride;" or "Lcom/duolingo/data/debug/monetization/FamilyQuestOverride;"
"Lcom/duolingo/",
) )
opcodes(Opcode.IPUT_BOOLEAN) opcodes(Opcode.IPUT_BOOLEAN)
} }

View File

@@ -27,4 +27,5 @@ private fun gmsCoreSupportResourcePatch(
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME, toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666", spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption, gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
addStringResources = false,
) )

View File

@@ -22,6 +22,7 @@ private fun gmsCoreSupportResourcePatch(
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch( ) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
fromPackageName = PHOTOS_PACKAGE_NAME, fromPackageName = PHOTOS_PACKAGE_NAME,
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME, toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
addStringResources = false,
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600", spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption, gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
) )

View File

@@ -3,23 +3,28 @@ package app.revanced.patches.googlephotos.misc.preferences
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Deprecated("This patch no longer works and this code will soon be deleted")
@Suppress("unused") @Suppress("unused")
val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch( val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch(
name = "Restore hidden 'Back up while charging' toggle", description = "Restores a hidden toggle to only run backups when the device is charging."
description = "Restores a hidden toggle to only run backups when the device is charging.",
) { ) {
compatibleWith("com.google.android.apps.photos") compatibleWith("com.google.android.apps.photos"("7.11.0.705590205"))
execute { execute {
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true. // Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
val chargingPrefStringIndex = backupPreferencesFingerprint.stringMatches!!.first().index backupPreferencesFingerprint.let {
backupPreferencesFingerprint.method.apply { it.method.apply {
// Get the register of move-result. val index = indexOfFirstInstructionOrThrow(
val resultRegister = getInstruction<OneRegisterInstruction>(chargingPrefStringIndex + 2).registerA it.stringMatches!!.first().index,
// Insert const after move-result to override register as true. Opcode.MOVE_RESULT
addInstruction(chargingPrefStringIndex + 3, "const/4 v$resultRegister, 0x1") )
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstruction(index + 1, "const/4 v$register, 0x1")
}
} }
} }
} }

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.messenger.navbar
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.Opcode
internal val createTabConfigurationFingerprint = fingerprint {
strings("MessengerTabConfigurationCreator.createTabConfiguration")
opcodes(
Opcode.INVOKE_DIRECT,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.INVOKE_DIRECT,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
)
}

View File

@@ -0,0 +1,25 @@
package app.revanced.patches.messenger.navbar
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val removeMetaAITabPatch = bytecodePatch(
name = "Remove Meta AI tab",
description = "Removes the 'Meta AI' tab from the navbar.",
) {
compatibleWith("com.facebook.orca")
execute {
createTabConfigurationFingerprint.let {
val moveResultIndex = it.patternMatch!!.startIndex + 1
val enabledRegister = it.method.getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
it.method.replaceInstruction(
moveResultIndex,
"const/4 v$enabledRegister, 0x0"
)
}
}
}

View File

@@ -12,7 +12,7 @@ val getDeviceIdPatch = bytecodePatch(
) { ) {
dependsOn(signatureDetectionPatch) dependsOn(signatureDetectionPatch)
compatibleWith("com.microblink.photomath"("8.37.0")) compatibleWith("com.microblink.photomath")
execute { execute {
getDeviceIdFingerprint.method.replaceInstructions( getDeviceIdFingerprint.method.replaceInstructions(

View File

@@ -14,5 +14,5 @@ internal val checkSignatureFingerprint = fingerprint {
Opcode.INVOKE_STATIC, Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT, Opcode.MOVE_RESULT,
) )
strings("signatures") strings("SHA")
} }

View File

@@ -11,7 +11,7 @@ val hideUpdatePopupPatch = bytecodePatch(
) { ) {
dependsOn(signatureDetectionPatch) dependsOn(signatureDetectionPatch)
compatibleWith("com.microblink.photomath"("8.32.0")) compatibleWith("com.microblink.photomath")
execute { execute {
hideUpdatePopupFingerprint.method.addInstructions( hideUpdatePopupFingerprint.method.addInstructions(

View File

@@ -11,7 +11,7 @@ val unlockPlusPatch = bytecodePatch(
) { ) {
dependsOn(signatureDetectionPatch, enableBookpointPatch) dependsOn(signatureDetectionPatch, enableBookpointPatch)
compatibleWith("com.microblink.photomath"("8.37.0")) compatibleWith("com.microblink.photomath")
execute { execute {
isPlusUnlockedFingerprint.method.addInstructions( isPlusUnlockedFingerprint.method.addInstructions(

View File

@@ -0,0 +1,42 @@
package app.revanced.patches.protonmail.signature
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.findElementByAttributeValue
import java.io.File
@Suppress("unused")
val removeSentFromSignaturePatch = resourcePatch(
name = "Remove 'Sent from' signature",
description = "Removes the 'Sent from Proton Mail mobile' signature from emails.",
) {
compatibleWith("ch.protonmail.android")
execute {
val stringResourceFiles = mutableListOf<File>()
get("res").walk().forEach { file ->
if (file.isFile && file.name.equals("strings.xml", ignoreCase = true)) {
stringResourceFiles.add(file)
}
}
var foundString = false
stringResourceFiles.forEach { filePath ->
document(filePath.absolutePath).use { document ->
var node = document.documentElement.childNodes.findElementByAttributeValue(
"name",
"mail_settings_identity_mobile_footer_default_free"
)
// String is not localized in all languages.
if (node != null) {
node.textContent = ""
foundString = true
}
}
}
if (!foundString) throw PatchException("Could not find 'sent from' string in resources")
}
}

View File

@@ -41,11 +41,12 @@ fun sharedExtensionPatch(
execute { execute {
if (classes.none { EXTENSION_CLASS_DESCRIPTOR == it.type }) { if (classes.none { EXTENSION_CLASS_DESCRIPTOR == it.type }) {
throw PatchException( throw PatchException("Shared extension is not available. This patch can not succeed without it.")
"Shared extension has not been merged yet. This patch can not succeed without merging it.",
)
} }
}
finalize {
// The hooks are made in finalize to ensure that the context is hooked before any other patches.
hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) } hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) }
// Modify Utils method to include the patches release version. // Modify Utils method to include the patches release version.
@@ -92,7 +93,7 @@ fun sharedExtensionPatch(
} }
class ExtensionHook internal constructor( class ExtensionHook internal constructor(
private val fingerprint: Fingerprint, internal val fingerprint: Fingerprint,
private val insertIndexResolver: ((Method) -> Int), private val insertIndexResolver: ((Method) -> Int),
private val contextRegisterResolver: (Method) -> String, private val contextRegisterResolver: (Method) -> String,
) { ) {
@@ -109,8 +110,14 @@ class ExtensionHook internal constructor(
} }
} }
fun extensionHook(
insertIndexResolver: ((Method) -> Int) = { 0 },
contextRegisterResolver: (Method) -> String = { "p0" },
fingerprint: Fingerprint,
) = ExtensionHook(fingerprint, insertIndexResolver, contextRegisterResolver)
fun extensionHook( fun extensionHook(
insertIndexResolver: ((Method) -> Int) = { 0 }, insertIndexResolver: ((Method) -> Int) = { 0 },
contextRegisterResolver: (Method) -> String = { "p0" }, contextRegisterResolver: (Method) -> String = { "p0" },
fingerprintBuilderBlock: FingerprintBuilder.() -> Unit, fingerprintBuilderBlock: FingerprintBuilder.() -> Unit,
) = ExtensionHook(fingerprint(block = fingerprintBuilderBlock), insertIndexResolver, contextRegisterResolver) ) = extensionHook(insertIndexResolver, contextRegisterResolver, fingerprint(block = fingerprintBuilderBlock))

View File

@@ -17,7 +17,6 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
import com.android.tools.smali.dexlib2.util.MethodUtil import com.android.tools.smali.dexlib2.util.MethodUtil
@@ -110,19 +109,18 @@ fun gmsCoreSupportPatch(
// region Collection of transformations that are applied to all strings. // region Collection of transformations that are applied to all strings.
fun commonTransform(referencedString: String): String? = fun commonTransform(referencedString: String): String? = when (referencedString) {
when (referencedString) { "com.google",
"com.google", "com.google.android.gms",
"com.google.android.gms", in PERMISSIONS,
in PERMISSIONS, in ACTIONS,
in ACTIONS, in AUTHORITIES,
in AUTHORITIES, -> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
// No vendor prefix for whatever reason... // No vendor prefix for whatever reason...
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds" "subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
else -> null else -> null
} }
fun contentUrisTransform(str: String): String? { fun contentUrisTransform(str: String): String? {
// only when content:// uri // only when content:// uri
@@ -205,16 +203,8 @@ fun gmsCoreSupportPatch(
// Verify GmsCore is installed and whitelisted for power optimizations and background usage. // Verify GmsCore is installed and whitelisted for power optimizations and background usage.
mainActivityOnCreateFingerprint.method.apply { mainActivityOnCreateFingerprint.method.apply {
// Temporary fix for patches with an extension patch that hook the onCreate method as well.
val setContextIndex = indexOfFirstInstruction {
val reference = getReference<MethodReference>() ?: return@indexOfFirstInstruction false
reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V"
}
// Add after setContext call, because this patch needs the context.
addInstructions( addInstructions(
if (setContextIndex < 0) 0 else setContextIndex + 1, 0,
"invoke-static/range { p0 .. p0 }, Lapp/revanced/extension/shared/GmsCoreSupport;->" + "invoke-static/range { p0 .. p0 }, Lapp/revanced/extension/shared/GmsCoreSupport;->" +
"checkGmsCore(Landroid/app/Activity;)V", "checkGmsCore(Landroid/app/Activity;)V",
) )
@@ -510,13 +500,44 @@ private object Constants {
* @param executeBlock The additional execution block of the patch. * @param executeBlock The additional execution block of the patch.
* @param block The additional block to build the patch. * @param block The additional block to build the patch.
*/ */
fun gmsCoreSupportResourcePatch( fun gmsCoreSupportResourcePatch( // This is here only for binary compatibility.
fromPackageName: String, fromPackageName: String,
toPackageName: String, toPackageName: String,
spoofedPackageSignature: String, spoofedPackageSignature: String,
gmsCoreVendorGroupIdOption: Option<String>, gmsCoreVendorGroupIdOption: Option<String>,
executeBlock: ResourcePatchContext.() -> Unit = {}, executeBlock: ResourcePatchContext.() -> Unit = {},
block: ResourcePatchBuilder.() -> Unit = {}, block: ResourcePatchBuilder.() -> Unit = {},
) = gmsCoreSupportResourcePatch(
fromPackageName,
toPackageName,
spoofedPackageSignature,
gmsCoreVendorGroupIdOption,
true,
executeBlock,
block
)
/**
* Abstract resource patch that allows Google apps to run without root and under a different package name
* by using GmsCore instead of Google Play Services.
*
* @param fromPackageName The package name of the original app.
* @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
* @param spoofedPackageSignature The signature of the package to spoof to.
* @param gmsCoreVendorGroupIdOption The option to get the vendor group ID of GmsCore.
* @param addStringResources If the GmsCore shared strings should be added to the patched app.
* @param executeBlock The additional execution block of the patch.
* @param block The additional block to build the patch.
*/
// TODO: On the next major release make this public and delete the public overloaded constructor.
internal fun gmsCoreSupportResourcePatch(
fromPackageName: String,
toPackageName: String,
spoofedPackageSignature: String,
gmsCoreVendorGroupIdOption: Option<String>,
addStringResources: Boolean = true,
executeBlock: ResourcePatchContext.() -> Unit = {},
block: ResourcePatchBuilder.() -> Unit = {},
) = resourcePatch { ) = resourcePatch {
dependsOn( dependsOn(
changePackageNamePatch, changePackageNamePatch,
@@ -526,7 +547,10 @@ fun gmsCoreSupportResourcePatch(
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
execute { execute {
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch") // Some patches don't use shared String resources so there's no need to add them.
if (addStringResources) {
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
}
/** /**
* Add metadata to manifest to support spoofing the package name and signature of GmsCore. * Add metadata to manifest to support spoofing the package name and signature of GmsCore.

View File

@@ -7,6 +7,8 @@ import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.BasePreference import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.IntentPreference import app.revanced.patches.shared.misc.settings.preference.IntentPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.util.ResourceGroup import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources import app.revanced.util.copyResources
import app.revanced.util.getNode import app.revanced.util.getNode
@@ -36,14 +38,14 @@ fun settingsPatch (
execute { execute {
copyResources( copyResources(
"settings", "settings",
ResourceGroup("xml", "revanced_prefs.xml"), ResourceGroup("xml", "revanced_prefs.xml", "revanced_prefs_icons.xml"),
) )
addResources("shared", "misc.settings.settingsResourcePatch") addResources("shared", "misc.settings.settingsResourcePatch")
} }
finalize { finalize {
fun Node.addPreference(preference: BasePreference, prepend: Boolean = false) { fun Node.addPreference(preference: BasePreference) {
preference.serialize(ownerDocument) { resource -> preference.serialize(ownerDocument) { resource ->
// TODO: Currently, resources can only be added to "values", which may not be the correct place. // TODO: Currently, resources can only be added to "values", which may not be the correct place.
// It may be necessary to ask for the desired resourceValue in the future. // It may be necessary to ask for the desired resourceValue in the future.
@@ -61,7 +63,7 @@ fun settingsPatch (
val preferenceFileName = "res/xml/$fileName.xml" val preferenceFileName = "res/xml/$fileName.xml"
if (get(preferenceFileName).exists()) { if (get(preferenceFileName).exists()) {
document(preferenceFileName).use { document -> document(preferenceFileName).use { document ->
document.getNode("PreferenceScreen").addPreference(intent, true) document.getNode("PreferenceScreen").addPreference(intent)
} }
modified = true modified = true
} }
@@ -71,6 +73,30 @@ fun settingsPatch (
} }
// Add all preferences to the ReVanced fragment. // Add all preferences to the ReVanced fragment.
document("res/xml/revanced_prefs_icons.xml").use { document ->
val revancedPreferenceScreenNode = document.getNode("PreferenceScreen")
preferences.forEach { revancedPreferenceScreenNode.addPreference(it) }
}
// Because the icon preferences require declaring a layout resource,
// there is no easy way to change to the Android default preference layout
// after the preference is inflated.
// Using two different preference files is the simplest and most robust solution.
fun removeIconsAndLayout(preferences: Collection<BasePreference>) {
preferences.forEach { preference ->
preference.icon = null
preference.layout = null
if (preference is PreferenceCategory) {
removeIconsAndLayout(preference.preferences)
}
if (preference is PreferenceScreenPreference) {
removeIconsAndLayout(preference.preferences)
}
}
}
removeIconsAndLayout(preferences)
document("res/xml/revanced_prefs.xml").use { document -> document("res/xml/revanced_prefs.xml").use { document ->
val revancedPreferenceScreenNode = document.getNode("PreferenceScreen") val revancedPreferenceScreenNode = document.getNode("PreferenceScreen")
preferences.forEach { revancedPreferenceScreenNode.addPreference(it) } preferences.forEach { revancedPreferenceScreenNode.addPreference(it) }

View File

@@ -19,10 +19,17 @@ abstract class BasePreference(
val key: String? = null, val key: String? = null,
val titleKey: String? = "${key}_title", val titleKey: String? = "${key}_title",
val summaryKey: String? = "${key}_summary", val summaryKey: String? = "${key}_summary",
val icon: String? = null, icon: String? = null,
val layout: String? = null, layout: String? = null,
val tag: String val tag: String
) { ) {
var icon: String? = icon
internal set
var layout: String? = layout
internal set
/** /**
* Serialize preference element to XML. * Serialize preference element to XML.
* Overriding methods should invoke super and operate on its return value. * Overriding methods should invoke super and operate on its return value.

View File

@@ -14,7 +14,7 @@ import app.revanced.util.findFreeRegister
import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertFeatureFlagBooleanOverride import app.revanced.util.insertLiteralOverride
import app.revanced.util.returnEarly import app.revanced.util.returnEarly
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@@ -235,7 +235,7 @@ fun spoofVideoStreamsPatch(
// region Fix iOS livestream current time. // region Fix iOS livestream current time.
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride( hlsCurrentTimeFingerprint.method.insertLiteralOverride(
HLS_CURRENT_TIME_FEATURE_FLAG, HLS_CURRENT_TIME_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z" "$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
) )
@@ -245,21 +245,21 @@ fun spoofVideoStreamsPatch(
// region turn off stream config replacement feature flag. // region turn off stream config replacement feature flag.
if (fixMediaFetchHotConfigChanges()) { if (fixMediaFetchHotConfigChanges()) {
mediaFetchHotConfigFingerprint.method.insertFeatureFlagBooleanOverride( mediaFetchHotConfigFingerprint.method.insertLiteralOverride(
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG, MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
) )
} }
if (fixMediaFetchHotConfigAlternativeChanges()) { if (fixMediaFetchHotConfigAlternativeChanges()) {
mediaFetchHotConfigAlternativeFingerprint.method.insertFeatureFlagBooleanOverride( mediaFetchHotConfigAlternativeFingerprint.method.insertLiteralOverride(
MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG, MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
) )
} }
if (fixParsePlaybackResponseFeatureFlag()) { if (fixParsePlaybackResponseFeatureFlag()) {
playbackStartDescriptorFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride( playbackStartDescriptorFeatureFlagFingerprint.method.insertLiteralOverride(
PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG, PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z" "$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z"
) )

View File

@@ -1,81 +1,205 @@
@file:Suppress("NAME_SHADOWING")
package app.revanced.patches.spotify.layout.theme package app.revanced.patches.spotify.layout.theme
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.fingerprint
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import org.w3c.dom.Element import org.w3c.dom.Element
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
internal val spotifyBackgroundColor = stringOption(
key = "backgroundColor",
default = "@android:color/black",
title = "Primary background color",
description = "The background color. Can be a hex color or a resource reference.",
required = true,
)
internal val overridePlayerGradientColor = booleanOption(
key = "overridePlayerGradientColor",
default = false,
title = "Override player gradient color",
description = "Apply primary background color to the player gradient color, which changes dynamically with the song.",
required = false
)
internal val spotifyBackgroundColorSecondary = stringOption(
key = "backgroundColorSecondary",
default = "#FF121212",
title = "Secondary background color",
description =
"The secondary background color. (e.g. playlist list in home, player artist, song credits). Can be a hex color or a resource reference.",
required = true,
)
internal val spotifyAccentColor = stringOption(
key = "accentColor",
default = "#FF1ED760",
title = "Accent color",
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
required = true,
)
internal val spotifyAccentColorPressed = stringOption(
key = "accentColorPressed",
default = "#FF169C46",
title = "Pressed dark theme accent color",
description =
"The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
required = true,
)
private val customThemeBytecodePatch = bytecodePatch {
dependsOn(sharedExtensionPatch)
execute {
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
// Bytecode changes are not needed for legacy app target.
// Player background color is changed with existing resource patch.
return@execute
}
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
val index = indexOfFirstLiteralInstructionOrThrow(literal)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
const-string v$register, "$colorString"
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getThemeColor(Ljava/lang/String;)J
move-result-wide v$register
"""
)
}
val encoreColorsClassName = with(encoreThemeFingerprint.originalMethod) {
// "Encore" colors are referenced right before the value of POSITIVE_INFINITY is returned.
// Begin the instruction find using the index of where POSITIVE_INFINITY is set into the register.
val positiveInfinityIndex = indexOfFirstLiteralInstructionOrThrow(
Float.POSITIVE_INFINITY
)
val encoreColorsFieldReferenceIndex = indexOfFirstInstructionReversedOrThrow(
positiveInfinityIndex,
Opcode.SGET_OBJECT
)
getInstruction(encoreColorsFieldReferenceIndex)
.getReference<FieldReference>()!!.definingClass
}
val encoreColorsConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
custom { method, classDef ->
classDef.type == encoreColorsClassName &&
method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL)
}
}
val backgroundColor by spotifyBackgroundColor
val backgroundColorSecondary by spotifyBackgroundColorSecondary
encoreColorsConstructorFingerprint.method.apply {
addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL, backgroundColor!!)
addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL, backgroundColorSecondary!!)
}
homeCategoryPillColorsFingerprint.method.addColorChangeInstructions(
HOME_CATEGORY_PILL_COLOR_LITERAL,
backgroundColorSecondary!!
)
settingsHeaderColorFingerprint.method.addColorChangeInstructions(
SETTINGS_HEADER_COLOR_LITERAL,
backgroundColorSecondary!!
)
}
}
@Suppress("unused") @Suppress("unused")
val customThemePatch = resourcePatch( val customThemePatch = resourcePatch(
name = "Custom theme", name = "Custom theme",
description = "Applies a custom theme.", description = "Applies a custom theme (defaults to amoled black)",
use = false, use = false,
) { ) {
compatibleWith("com.spotify.music") compatibleWith("com.spotify.music")
val backgroundColor by stringOption( dependsOn(customThemeBytecodePatch)
key = "backgroundColor",
default = "@android:color/black",
title = "Primary background color",
description = "The background color. Can be a hex color or a resource reference.",
required = true,
)
val backgroundColorSecondary by stringOption( val backgroundColor by spotifyBackgroundColor()
key = "backgroundColorSecondary", val overridePlayerGradientColor by overridePlayerGradientColor()
default = "#ff282828", val backgroundColorSecondary by spotifyBackgroundColorSecondary()
title = "Secondary background color", val accentColor by spotifyAccentColor()
description = "The secondary background color. (e.g. search box, artist & podcast). Can be a hex color or a resource reference.", val accentColorPressed by spotifyAccentColorPressed()
required = true,
)
val accentColor by stringOption(
key = "accentColor",
default = "#ff1ed760",
title = "Accent color",
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
required = true,
)
val accentColorPressed by stringOption(
key = "accentColorPressed",
default = "#ff169c46",
title = "Pressed dark theme accent color",
description =
"The color when accented buttons are pressed, by default slightly darker than accent. " +
"Can be a hex color or a resource reference.",
required = true,
)
execute { execute {
val backgroundColor = backgroundColor!!
val backgroundColorSecondary = backgroundColorSecondary!!
val accentColor = accentColor!!
val accentColorPressed = accentColorPressed!!
document("res/values/colors.xml").use { document -> document("res/values/colors.xml").use { document ->
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
val childNodes = resourcesNode.childNodes val childNodes = resourcesNode.childNodes
for (i in 0 until childNodes.length) { for (i in 0 until childNodes.length) {
val node = childNodes.item(i) as? Element ?: continue val node = childNodes.item(i) as? Element ?: continue
val name = node.getAttribute("name")
node.textContent = // Skip overriding song/player gradient start color if the option is disabled.
when (node.getAttribute("name")) { // Gradient end color should be themed regardless to allow the gradient to connect with
"dark_base_background_elevated_base", "design_dark_default_color_background", // our primary background color.
"design_dark_default_color_surface", "gray_7", "gray_background", "gray_layer", if (name == "bg_gradient_start_color" && !overridePlayerGradientColor!!) {
"sthlm_blk", continue
}
node.textContent = when (name) {
// Gradient next to user photo and "All" in home page.
"dark_base_background_base",
// Main background.
"gray_7",
// Left sidebar background in tablet mode.
"gray_10",
// "Add account", "Settings and privacy", "View Profile" left sidebar background.
"dark_base_background_elevated_base",
// Song/player gradient start/end color.
"bg_gradient_start_color", "bg_gradient_end_color",
// Login screen background and gradient start.
"sthlm_blk", "sthlm_blk_grad_start",
// Misc.
"image_placeholder_color",
-> backgroundColor -> backgroundColor
"gray_15" -> backgroundColorSecondary // Track credits, merch background in song player.
"track_credits_card_bg", "benefit_list_default_color", "merch_card_background",
// Playlist list background in home page.
"opacity_white_10",
// "About the artist" background in song player.
"gray_15",
// "What's New" pills background.
"dark_base_background_tinted_highlight"
-> backgroundColorSecondary
"dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light" -> accentColor "dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light" -> accentColor
"dark_brightaccent_background_press" -> accentColorPressed
"dark_brightaccent_background_press" -> accentColorPressed else -> continue
else -> continue }
}
} }
} }
// Login screen gradient.
document("res/drawable/start_screen_gradient.xml").use { document ->
val gradientNode = document.getElementsByTagName("gradient").item(0) as Element
gradientNode.setAttribute("android:startColor", backgroundColor)
gradientNode.setAttribute("android:endColor", backgroundColor)
}
} }
} }

View File

@@ -0,0 +1,33 @@
package app.revanced.patches.spotify.layout.theme
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import com.android.tools.smali.dexlib2.AccessFlags
internal val encoreThemeFingerprint = fingerprint {
strings("Encore theme was not provided.") // Partial string match.
custom { method, _ ->
method.name == "invoke"
}
}
internal const val PLAYLIST_BACKGROUND_COLOR_LITERAL = 0xFF121212
internal const val SHARE_MENU_BACKGROUND_COLOR_LITERAL = 0xFF1F1F1F
internal const val HOME_CATEGORY_PILL_COLOR_LITERAL = 0xFF333333
internal const val SETTINGS_HEADER_COLOR_LITERAL = 0xFF282828
internal val homeCategoryPillColorsFingerprint = fingerprint{
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
custom { method, _ ->
method.containsLiteralInstruction(HOME_CATEGORY_PILL_COLOR_LITERAL) &&
method.containsLiteralInstruction(0x33000000)
}
}
internal val settingsHeaderColorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
custom { method, _ ->
method.containsLiteralInstruction(SETTINGS_HEADER_COLOR_LITERAL) &&
method.containsLiteralInstruction(0)
}
}

View File

@@ -1,15 +1,28 @@
package app.revanced.patches.spotify.misc package app.revanced.patches.spotify.misc
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val accountAttributeFingerprint = fingerprint { internal val accountAttributeFingerprint = fingerprint {
custom { _, c -> c.endsWith("internal/AccountAttribute;") } custom { _, classDef ->
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
"Lcom/spotify/useraccount/v1/AccountAttribute;"
} else {
"Lcom/spotify/remoteconfig/internal/AccountAttribute;"
}
}
} }
internal val productStateProtoFingerprint = fingerprint { internal val productStateProtoFingerprint = fingerprint {
returns("Ljava/util/Map;") returns("Ljava/util/Map;")
custom { _, classDef -> custom { _, classDef ->
classDef.endsWith("ProductStateProto;") classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
"Lcom/spotify/ucs/proto/v0/UcsResponseWrapper${'$'}AccountAttributesResponse;"
} else {
"Lcom/spotify/remoteconfig/internal/ProductStateProto;"
}
} }
} }
@@ -21,3 +34,38 @@ internal val contextMenuExperimentsFingerprint = fingerprint {
parameters("L") parameters("L")
strings("remove_ads_upsell_enabled") strings("remove_ads_upsell_enabled")
} }
internal val contextFromJsonFingerprint = fingerprint {
opcodes(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC
)
custom { methodDef, classDef ->
methodDef.name == "fromJson" &&
classDef.endsWith("voiceassistants/playermodels/ContextJsonAdapter;")
}
}
internal val readPlayerOptionOverridesFingerprint = fingerprint {
custom { methodDef, classDef ->
methodDef.name == "readPlayerOptionOverrides" &&
classDef.endsWith("voiceassistants/playermodels/PreparePlayOptionsJsonAdapter;")
}
}
internal val homeSectionFingerprint = fingerprint {
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
}
internal val protobufListsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
custom { method, _ -> method.name == "emptyProtobufList" }
}
internal val homeStructureFingerprint = fingerprint {
opcodes(Opcode.IGET_OBJECT, Opcode.RETURN_OBJECT)
custom { _, classDef -> classDef.endsWith("homeapi/proto/HomeStructure;") }
}

View File

@@ -0,0 +1,11 @@
package app.revanced.patches.spotify.misc.check
import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch
import app.revanced.patches.spotify.shared.mainActivityOnCreateFingerprint
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
internal val checkEnvironmentPatch = checkEnvironmentPatch(
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
extensionPatch = sharedExtensionPatch,
"com.spotify.music",
)

View File

@@ -1,5 +1,21 @@
package app.revanced.patches.spotify.misc.extension package app.revanced.patches.spotify.misc.extension
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
import app.revanced.patches.spotify.shared.SPOTIFY_MAIN_ACTIVITY_LEGACY
val sharedExtensionPatch = sharedExtensionPatch("spotify", spotifyMainActivityOnCreate) /**
* If patching a legacy 8.x target. This may also be set if patching slightly older/newer app targets,
* but the only legacy target of interest is 8.6.98.900 as it's the last version that
* supports Spotify integration on Kenwood/Pioneer car stereos.
*/
internal var IS_SPOTIFY_LEGACY_APP_TARGET = false
val sharedExtensionPatch = bytecodePatch {
dependsOn(sharedExtensionPatch("spotify", mainActivityOnCreateHook))
execute {
IS_SPOTIFY_LEGACY_APP_TARGET = mainActivityOnCreateHook.fingerprint
.originalClassDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY
}
}

View File

@@ -1,10 +1,6 @@
package app.revanced.patches.spotify.misc.extension package app.revanced.patches.spotify.misc.extension
import app.revanced.patches.shared.misc.extension.extensionHook import app.revanced.patches.shared.misc.extension.extensionHook
import app.revanced.patches.spotify.shared.mainActivityOnCreateFingerprint
internal val spotifyMainActivityOnCreate = extensionHook { internal val mainActivityOnCreateHook = extensionHook(fingerprint = mainActivityOnCreateFingerprint)
custom { method, classDef ->
classDef.type == "Lcom/spotify/music/SpotifyMainActivity;" &&
method.name == "onCreate"
}
}

View File

@@ -2,4 +2,8 @@ package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
internal val getAppSignatureFingerprint = fingerprint { strings("Failed to get the application signatures") } internal val getPackageInfoFingerprint = fingerprint {
strings(
"Failed to get the application signatures"
)
}

View File

@@ -0,0 +1,63 @@
package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Suppress("unused")
val spoofPackageInfoPatch = bytecodePatch(
name = "Spoof package info",
description = "Spoofs the package info of the app to fix various functions of the app.",
) {
compatibleWith("com.spotify.music")
execute {
getPackageInfoFingerprint.method.apply {
// region Spoof signature.
val failedToGetSignaturesStringIndex =
getPackageInfoFingerprint.stringMatches!!.first().index
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
failedToGetSignaturesStringIndex,
Opcode.MOVE_RESULT_OBJECT,
)
val signatureRegister = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
replaceInstruction(concatSignaturesIndex, "const-string v$signatureRegister, \"$expectedSignature\"")
// endregion
// region Spoof installer name.
val expectedInstallerName = "com.android.vending"
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.name == "getInstallerPackageName" || reference?.name == "getInstallingPackageName"
}.forEach { index ->
val returnObjectIndex = index + 1
val installerPackageNameRegister = getInstruction<OneRegisterInstruction>(
returnObjectIndex
).registerA
addInstruction(
returnObjectIndex + 1,
"const-string v$installerPackageNameRegister, \"$expectedInstallerName\""
)
}
// endregion
}
}
}

View File

@@ -1,33 +1,13 @@
package app.revanced.patches.spotify.misc.fix package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Deprecated("Superseded by spoofPackageInfoPatch", ReplaceWith("spoofPackageInfoPatch"))
@Suppress("unused") @Suppress("unused")
val spoofSignaturePatch = bytecodePatch( val spoofSignaturePatch = bytecodePatch(
name = "Spoof signature", description = "Spoofs the signature of the app fix various functions of the app.",
description = "Spoofs the signature of the app to fix various functions of the app.",
) { ) {
compatibleWith("com.spotify.music") compatibleWith("com.spotify.music")
execute { dependsOn(spoofPackageInfoPatch)
getAppSignatureFingerprint.method.apply {
val failedToGetSignaturesStringMatch = getAppSignatureFingerprint.stringMatches!!.first()
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
failedToGetSignaturesStringMatch.index,
Opcode.MOVE_RESULT_OBJECT,
)
val register = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
replaceInstruction(concatSignaturesIndex, "const-string v$register, \"$expectedSignature\"")
}
}
} }

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.spotify.shared
import app.revanced.patcher.fingerprint
private const val SPOTIFY_MAIN_ACTIVITY = "Lcom/spotify/music/SpotifyMainActivity;"
/**
* Main activity of target 8.6.98.900.
*/
internal const val SPOTIFY_MAIN_ACTIVITY_LEGACY = "Lcom/spotify/music/MainActivity;"
internal val mainActivityOnCreateFingerprint = fingerprint {
custom { method, classDef ->
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
}
}

View File

@@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
val disableSubscriptionSuggestionsPatch = bytecodePatch( val disableSubscriptionSuggestionsPatch = bytecodePatch(
name = "Disable subscription suggestions", name = "Disable subscription suggestions",
) { ) {
compatibleWith("com.strava"("320.12")) compatibleWith("com.strava")
execute { execute {
val helperMethodName = "getModulesIfNotUpselling" val helperMethodName = "getModulesIfNotUpselling"

View File

@@ -12,7 +12,7 @@ val dynamicColorPatch = resourcePatch(
) { ) {
compatibleWith( compatibleWith(
"com.twitter.android"( "com.twitter.android"(
"10.84.0-release.0", "10.86.0-release.0",
"10.60.0-release.0", "10.60.0-release.0",
"10.48.0-release.0" "10.48.0-release.0"
) )

View File

@@ -13,8 +13,8 @@ fun hookPatch(
compatibleWith( compatibleWith(
"com.twitter.android"( "com.twitter.android"(
// 10.85+ uses Pairip and requires additional changes to work. // Only v10.85 uses Pairip and requires additional changes to work.
"10.84.0-release.0", "10.86.0-release.0",
// Confirmed to not show reply ads. Slightly newer versions may also work. // Confirmed to not show reply ads. Slightly newer versions may also work.
"10.60.0-release.0", "10.60.0-release.0",
"10.48.0-release.0" "10.48.0-release.0"

View File

@@ -39,7 +39,7 @@ val changeLinkSharingDomainPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.twitter.android"( "com.twitter.android"(
"10.84.0-release.0", "10.86.0-release.0",
"10.60.0-release.0", "10.60.0-release.0",
"10.48.0-release.0" "10.48.0-release.0"
) )

View File

@@ -10,7 +10,7 @@ val sanitizeSharingLinksPatch = bytecodePatch(
) { ) {
compatibleWith( compatibleWith(
"com.twitter.android"( "com.twitter.android"(
"10.84.0-release.0", "10.86.0-release.0",
"10.60.0-release.0", "10.60.0-release.0",
"10.48.0-release.0" "10.48.0-release.0"
) )

View File

@@ -84,7 +84,8 @@ val hideAdsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -31,7 +31,8 @@ val hideGetPremiumPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -29,7 +29,8 @@ val videoAdsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -59,7 +59,8 @@ val copyVideoUrlPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -30,7 +30,8 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
val extensionMethodDescriptor = val extensionMethodDescriptor =

View File

@@ -74,7 +74,8 @@ val downloadsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -11,8 +11,10 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisablePreciseSeekingGesturePatch;"
val disablePreciseSeekingGesturePatch = bytecodePatch( val disablePreciseSeekingGesturePatch = bytecodePatch(
name = "Disable precise seeking gesture",
description = "Adds an option to disable precise seeking when swiping up on the seekbar.", description = "Adds an option to disable precise seeking when swiping up on the seekbar.",
) { ) {
dependsOn( dependsOn(
@@ -21,25 +23,12 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
addResourcesPatch, addResourcesPatch,
) )
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
),
)
execute { execute {
addResources("youtube", "interaction.seekbar.disablePreciseSeekingGesturePatch") addResources("youtube", "interaction.seekbar.disablePreciseSeekingGesturePatch")
PreferenceScreen.SEEKBAR.addPreferences( PreferenceScreen.SEEKBAR.addPreferences(
SwitchPreference("revanced_disable_precise_seeking_gesture"), SwitchPreference("revanced_disable_precise_seeking_gesture"),
) )
val extensionMethodDescriptor =
"Lapp/revanced/extension/youtube/patches/DisablePreciseSeekingGesturePatch;"
allowSwipingUpGestureFingerprint.match( allowSwipingUpGestureFingerprint.match(
swipingUpGestureParentFingerprint.originalClassDef, swipingUpGestureParentFingerprint.originalClassDef,
@@ -47,7 +36,7 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
addInstructionsWithLabels( addInstructionsWithLabels(
0, 0,
""" """
invoke-static { }, $extensionMethodDescriptor->isGestureDisabled()Z invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isGestureDisabled()Z
move-result v0 move-result v0
if-eqz v0, :disabled if-eqz v0, :disabled
return-void return-void
@@ -62,7 +51,7 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
addInstructionsWithLabels( addInstructionsWithLabels(
0, 0,
""" """
invoke-static { }, $extensionMethodDescriptor->isGestureDisabled()Z invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->isGestureDisabled()Z
move-result v0 move-result v0
if-eqz v0, :disabled if-eqz v0, :disabled
const/4 v0, 0x0 const/4 v0, 0x0

View File

@@ -10,12 +10,16 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findFreeRegister
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;"
val enableSeekbarTappingPatch = bytecodePatch( val enableSeekbarTappingPatch = bytecodePatch(
name = "Enable tap to seek",
description = "Adds an option to enable tap to seek on the seekbar of the video player.", description = "Adds an option to enable tap to seek on the seekbar of the video player.",
) { ) {
dependsOn( dependsOn(
@@ -24,17 +28,6 @@ val enableSeekbarTappingPatch = bytecodePatch(
addResourcesPatch, addResourcesPatch,
) )
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
),
)
execute { execute {
addResources("youtube", "interaction.seekbar.enableSeekbarTappingPatch") addResources("youtube", "interaction.seekbar.enableSeekbarTappingPatch")
@@ -43,39 +36,37 @@ val enableSeekbarTappingPatch = bytecodePatch(
) )
// Find the required methods to tap the seekbar. // Find the required methods to tap the seekbar.
val patternMatch = onTouchEventHandlerFingerprint.patternMatch!! val seekbarTappingMethods = onTouchEventHandlerFingerprint.let {
fun getMethodReference(index: Int) = it.method.getInstruction<ReferenceInstruction>(index)
.reference as MethodReference
fun getReference(index: Int) = onTouchEventHandlerFingerprint.method.getInstruction<ReferenceInstruction>(index) listOf(
.reference as MethodReference getMethodReference(it.patternMatch!!.startIndex),
getMethodReference(it.patternMatch!!.endIndex)
val seekbarTappingMethods = buildMap { )
put("N", getReference(patternMatch.startIndex))
put("O", getReference(patternMatch.endIndex))
} }
val insertIndex = seekbarTappingFingerprint.patternMatch!!.endIndex - 1
seekbarTappingFingerprint.method.apply { seekbarTappingFingerprint.method.apply {
val thisInstanceRegister = getInstruction<Instruction35c>(insertIndex - 1).registerC val pointIndex = indexOfNewPointInstruction(this)
val invokeIndex = indexOfFirstInstructionOrThrow(pointIndex, Opcode.INVOKE_VIRTUAL)
val insertIndex = invokeIndex + 1
val freeRegister = 0 val thisInstanceRegister = getInstruction<FiveRegisterInstruction>(invokeIndex).registerC
val xAxisRegister = 2 val xAxisRegister = this.getInstruction<FiveRegisterInstruction>(pointIndex).registerD
val freeRegister = findFreeRegister(insertIndex, thisInstanceRegister, xAxisRegister)
val oMethod = seekbarTappingMethods["O"]!! val oMethod = seekbarTappingMethods[0]
val nMethod = seekbarTappingMethods["N"]!! val nMethod = seekbarTappingMethods[1]
fun MethodReference.toInvokeInstructionString() =
"invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $this"
addInstructionsWithLabels( addInstructionsWithLabels(
insertIndex, insertIndex,
""" """
invoke-static { }, Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;->seekbarTappingEnabled()Z invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->seekbarTappingEnabled()Z
move-result v$freeRegister move-result v$freeRegister
if-eqz v$freeRegister, :disabled if-eqz v$freeRegister, :disabled
${oMethod.toInvokeInstructionString()} invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $oMethod
${nMethod.toInvokeInstructionString()} invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $nMethod
""", """,
ExternalLabel("disabled", getInstruction(insertIndex)), ExternalLabel("disabled", getInstruction(insertIndex)),
) )
} }

View File

@@ -18,11 +18,9 @@ import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal const val EXTENSION_METHOD_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SlideToSeekPatch;"
"Lapp/revanced/extension/youtube/patches/SlideToSeekPatch;->isSlideToSeekDisabled(Z)Z"
val enableSlideToSeekPatch = bytecodePatch( val enableSlideToSeekPatch = bytecodePatch(
name = "Enable slide to seek",
description = "Adds an option to enable slide to seek " + description = "Adds an option to enable slide to seek " +
"instead of playing at 2x speed when pressing and holding in the video player." "instead of playing at 2x speed when pressing and holding in the video player."
) { ) {
@@ -33,17 +31,6 @@ val enableSlideToSeekPatch = bytecodePatch(
versionCheckPatch, versionCheckPatch,
) )
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
),
)
execute { execute {
addResources("youtube", "interaction.seekbar.enableSlideToSeekPatch") addResources("youtube", "interaction.seekbar.enableSlideToSeekPatch")
@@ -59,6 +46,8 @@ val enableSlideToSeekPatch = bytecodePatch(
val checkReference = slideToSeekFingerprint.method.getInstruction(checkIndex) val checkReference = slideToSeekFingerprint.method.getInstruction(checkIndex)
.getReference<MethodReference>()!! .getReference<MethodReference>()!!
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->isSlideToSeekDisabled(Z)Z"
// A/B check method was only called on this class. // A/B check method was only called on this class.
slideToSeekFingerprint.classDef.methods.forEach { method -> slideToSeekFingerprint.classDef.methods.forEach { method ->
method.findInstructionIndicesReversed { method.findInstructionIndicesReversed {
@@ -70,7 +59,7 @@ val enableSlideToSeekPatch = bytecodePatch(
addInstructions( addInstructions(
index + 2, index + 2,
""" """
invoke-static { v$register }, $EXTENSION_METHOD_DESCRIPTOR invoke-static { v$register }, $extensionMethodDescriptor
move-result v$register move-result v$register
""", """,
) )
@@ -95,7 +84,7 @@ val enableSlideToSeekPatch = bytecodePatch(
addInstructions( addInstructions(
targetIndex + 1, targetIndex + 1,
""" """
invoke-static { v$targetRegister }, $EXTENSION_METHOD_DESCRIPTOR invoke-static { v$targetRegister }, $extensionMethodDescriptor
move-result v$targetRegister move-result v$targetRegister
""", """,
) )
@@ -109,7 +98,7 @@ val enableSlideToSeekPatch = bytecodePatch(
addInstructions( addInstructions(
insertIndex, insertIndex,
""" """
invoke-static { v$targetRegister }, $EXTENSION_METHOD_DESCRIPTOR invoke-static { v$targetRegister }, $extensionMethodDescriptor
move-result v$targetRegister move-result v$targetRegister
""", """,
) )

View File

@@ -1,11 +1,15 @@
package app.revanced.patches.youtube.interaction.seekbar package app.revanced.patches.youtube.interaction.seekbar
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.indexOfFirstInstructionReversed
import app.revanced.util.literal import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference import com.android.tools.smali.dexlib2.iface.reference.StringReference
internal val swipingUpGestureParentFingerprint = fingerprint { internal val swipingUpGestureParentFingerprint = fingerprint {
@@ -101,14 +105,17 @@ internal val seekbarTappingFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z") returns("Z")
parameters("L") parameters("L")
opcodes( custom { method, _ ->
Opcode.IPUT_OBJECT, method.name == "onTouchEvent"
Opcode.INVOKE_VIRTUAL, && method.containsLiteralInstruction(Integer.MAX_VALUE.toLong())
// Insert seekbar tapping instructions here. && indexOfNewPointInstruction(method) >= 0
Opcode.RETURN, }
Opcode.INVOKE_VIRTUAL, }
)
literal { Integer.MAX_VALUE.toLong() } internal fun indexOfNewPointInstruction(method: Method) = method.indexOfFirstInstructionReversed {
val reference = getReference<MethodReference>()
reference?.definingClass == "Landroid/graphics/Point;"
&& reference.name == "<init>"
} }
internal val slideToSeekFingerprint = fingerprint { internal val slideToSeekFingerprint = fingerprint {

View File

@@ -0,0 +1,46 @@
package app.revanced.patches.youtube.interaction.seekbar
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.seekbarFingerprint
import app.revanced.patches.youtube.shared.seekbarOnDrawFingerprint
val hideSeekbarPatch = bytecodePatch(
description = "Adds an option to hide the seekbar.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
seekbarColorPatch,
addResourcesPatch,
)
execute {
addResources("youtube", "layout.hide.seekbar.hideSeekbarPatch")
PreferenceScreen.SEEKBAR.addPreferences(
SwitchPreference("revanced_hide_seekbar"),
SwitchPreference("revanced_hide_seekbar_thumbnail"),
)
seekbarOnDrawFingerprint.match(seekbarFingerprint.originalClassDef).method.addInstructionsWithLabels(
0,
"""
const/4 v0, 0x0
invoke-static { }, Lapp/revanced/extension/youtube/patches/HideSeekbarPatch;->hideSeekbar()Z
move-result v0
if-eqz v0, :hide_seekbar
return-void
:hide_seekbar
nop
""",
)
}
}

View File

@@ -0,0 +1,32 @@
package app.revanced.patches.youtube.interaction.seekbar
import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
val seekbarPatch = bytecodePatch(
name = "Seekbar",
description = "Adds options to disable precise seeking when swiping up on the seekbar, " +
"slide to seek instead of playing at 2x speed when pressing and holding, " +
"tapping the player seekbar to seek, " +
"and hiding the video player seekbar."
) {
dependsOn(
disablePreciseSeekingGesturePatch,
enableSlideToSeekPatch,
enableSeekbarTappingPatch,
hideSeekbarPatch,
seekbarThumbnailsPatch
)
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
)
)
}

View File

@@ -10,6 +10,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.layout.seekbar.fullscreenSeekbarThumbnailsFingerprint import app.revanced.patches.youtube.layout.seekbar.fullscreenSeekbarThumbnailsFingerprint
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater import app.revanced.patches.youtube.misc.playservice.is_19_17_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
@@ -17,7 +18,6 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/SeekbarThumbnailsPatch;" "Lapp/revanced/extension/youtube/patches/SeekbarThumbnailsPatch;"
val seekbarThumbnailsPatch = bytecodePatch( val seekbarThumbnailsPatch = bytecodePatch(
name = "Seekbar thumbnails",
description = "Adds an option to use high quality fullscreen seekbar thumbnails. " + description = "Adds an option to use high quality fullscreen seekbar thumbnails. " +
"Patching 19.16.39 adds an option to restore old seekbar thumbnails.", "Patching 19.16.39 adds an option to restore old seekbar thumbnails.",
) { ) {
@@ -27,18 +27,13 @@ val seekbarThumbnailsPatch = bytecodePatch(
versionCheckPatch, versionCheckPatch,
) )
compatibleWith(
"com.google.android.youtube"(
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
)
)
execute { execute {
if (is_20_09_or_greater) {
// High quality seekbar thumbnails is partially broken in 20.09
// and the code is completely removed in 20.10+
return@execute
}
addResources("youtube", "layout.seekbar.seekbarThumbnailsPatch") addResources("youtube", "layout.seekbar.seekbarThumbnailsPatch")
if (is_19_17_or_greater) { if (is_19_17_or_greater) {

View File

@@ -47,6 +47,7 @@ private val swipeControlsResourcePatch = resourcePatch {
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER), TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER), TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER), TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_volume_sensitivity", inputType = InputType.NUMBER),
) )
copyResources( copyResources(
@@ -86,7 +87,8 @@ val swipeControlsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
@@ -117,7 +119,7 @@ val swipeControlsPatch = bytecodePatch(
// region patch to enable/disable swipe to change video. // region patch to enable/disable swipe to change video.
if (is_19_43_or_greater) { if (is_19_43_or_greater) {
swipeChangeVideoFingerprint.method.insertFeatureFlagBooleanOverride( swipeChangeVideoFingerprint.method.insertLiteralOverride(
SWIPE_CHANGE_VIDEO_FEATURE_FLAG, SWIPE_CHANGE_VIDEO_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z" "$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z"
) )

View File

@@ -8,7 +8,9 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.subtitleButtonControllerFingerprint
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;"
val autoCaptionsPatch = bytecodePatch( val autoCaptionsPatch = bytecodePatch(
name = "Disable auto captions", name = "Disable auto captions",
@@ -28,42 +30,41 @@ val autoCaptionsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
addResources("youtube", "layout.autocaptions.autoCaptionsPatch") addResources("youtube", "layout.autocaptions.autoCaptionsPatch")
PreferenceScreen.PLAYER.addPreferences( PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_auto_captions"), SwitchPreference("revanced_disable_auto_captions"),
)
subtitleTrackFingerprint.method.addInstructions(
0,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableAutoCaptions()Z
move-result v0
if-eqz v0, :auto_captions_enabled
const/4 v0, 0x1
return v0
:auto_captions_enabled
nop
"""
) )
mapOf( mapOf(
startVideoInformerFingerprint to 0, startVideoInformerFingerprint to 0,
subtitleButtonControllerFingerprint to 1, storyboardRendererDecoderRecommendedLevelFingerprint to 1
).forEach { (fingerprint, enabled) -> ).forEach { (fingerprint, enabled) ->
fingerprint.method.addInstructions( fingerprint.method.addInstructions(
0, 0,
""" """
const/4 v0, 0x$enabled const/4 v0, 0x$enabled
sput-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setCaptionsButtonStatus(Z)V
""", """
) )
} }
subtitleTrackFingerprint.method.addInstructions(
0,
"""
invoke-static {}, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->autoCaptionsEnabled()Z
move-result v0
if-eqz v0, :auto_captions_enabled
sget-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z
if-nez v0, :auto_captions_enabled
const/4 v0, 0x1
return v0
:auto_captions_enabled
nop
""",
)
} }
} }

View File

@@ -14,6 +14,13 @@ internal val startVideoInformerFingerprint = fingerprint {
strings("pc") strings("pc")
} }
internal val storyboardRendererDecoderRecommendedLevelFingerprint = fingerprint {
returns("V")
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L")
strings("#-1#")
}
internal val subtitleTrackFingerprint = fingerprint { internal val subtitleTrackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z") returns("Z")
@@ -28,6 +35,6 @@ internal val subtitleTrackFingerprint = fingerprint {
) )
strings("DISABLE_CAPTIONS_OPTION") strings("DISABLE_CAPTIONS_OPTION")
custom { _, classDef -> custom { _, classDef ->
classDef.endsWith("SubtitleTrack;") classDef.endsWith("/SubtitleTrack;")
} }
} }

View File

@@ -49,7 +49,8 @@ val customBrandingPatch = resourcePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
val appName by stringOption( val appName by stringOption(

View File

@@ -47,6 +47,7 @@ val changeHeaderPatch = resourcePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
"20.12.46",
) )
) )

View File

@@ -28,7 +28,8 @@ val hideButtonsPatch = resourcePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -18,7 +18,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertFeatureFlagBooleanOverride import app.revanced.util.insertLiteralOverride
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@@ -46,7 +46,8 @@ val navigationButtonsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
@@ -119,17 +120,17 @@ val navigationButtonsPatch = bytecodePatch(
// Force on/off translucent effect on status bar and navigation buttons. // Force on/off translucent effect on status bar and navigation buttons.
if (is_19_25_or_greater) { if (is_19_25_or_greater) {
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride( translucentNavigationStatusBarFeatureFlagFingerprint.method.insertLiteralOverride(
TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG, TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z", "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
) )
translucentNavigationButtonsFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride( translucentNavigationButtonsFeatureFlagFingerprint.method.insertLiteralOverride(
TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG, TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
) )
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride( translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertLiteralOverride(
TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG, TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z", "$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
) )

View File

@@ -60,7 +60,8 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -39,7 +39,8 @@ val changeFormFactorPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -65,7 +65,8 @@ val hideEndscreenCardsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -37,7 +37,8 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -35,7 +35,8 @@ val disableFullscreenAmbientModePatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.layout.hide.general package app.revanced.patches.youtube.layout.hide.general
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutFingerprint
import app.revanced.util.literal import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@@ -16,9 +17,22 @@ internal val hideShowMoreButtonFingerprint = fingerprint {
} }
/** /**
* 20.07+ * 20.12+
*/ */
internal val parseElementFromBufferFingerprint = fingerprint { internal val parseElementFromBufferFingerprint = fingerprint {
parameters("L", "L", "[B", "L", "L")
opcodes(
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
)
strings("Failed to parse Element") // String is a partial match.
}
/**
* 20.07+
*/
internal val parseElementFromBufferLegacy2007Fingerprint = fingerprint {
parameters("L", "L", "[B", "L", "L") parameters("L", "L", "[B", "L", "L")
opcodes( opcodes(
Opcode.IGET_OBJECT, Opcode.IGET_OBJECT,
@@ -29,7 +43,10 @@ internal val parseElementFromBufferFingerprint = fingerprint {
strings("Failed to parse Element") // String is a partial match. strings("Failed to parse Element") // String is a partial match.
} }
internal val parseElementFromBufferLegacyFingerprint = fingerprint { /**
* 19.01 - 20.06
*/
internal val parseElementFromBufferLegacy1901Fingerprint = fingerprint {
parameters("L", "L", "[B", "L", "L") parameters("L", "L", "[B", "L", "L")
opcodes( opcodes(
Opcode.IGET_OBJECT, Opcode.IGET_OBJECT,
@@ -51,6 +68,9 @@ internal val showWatermarkFingerprint = fingerprint {
parameters("L", "L") parameters("L", "L")
} }
/**
* Matches same method as [wideSearchbarLayoutFingerprint].
*/
internal val yoodlesImageViewFingerprint = fingerprint { internal val yoodlesImageViewFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;") returns("Landroid/view/View;")

View File

@@ -21,6 +21,7 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findFreeRegister import app.revanced.util.findFreeRegister
@@ -43,14 +44,12 @@ var crowdfundingBoxId = -1L
private set private set
var youTubeLogo = -1L var youTubeLogo = -1L
private set private set
var filterBarHeightId = -1L var filterBarHeightId = -1L
private set private set
var relatedChipCloudMarginId = -1L var relatedChipCloudMarginId = -1L
private set private set
var barContainerHeightId = -1L var barContainerHeightId = -1L
private set private set
var fabButtonId = -1L var fabButtonId = -1L
private set private set
@@ -130,11 +129,10 @@ val hideLayoutComponentsPatch = bytecodePatch(
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",
"19.43.41", "19.43.41",
"19.45.38",
"19.46.42",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
@@ -249,8 +247,9 @@ val hideLayoutComponentsPatch = bytecodePatch(
// region Mix playlists // region Mix playlists
(if (is_20_07_or_greater) parseElementFromBufferFingerprint (if (is_20_09_or_greater) parseElementFromBufferFingerprint
else parseElementFromBufferLegacyFingerprint).let { else if (is_20_07_or_greater) parseElementFromBufferLegacy2007Fingerprint
else parseElementFromBufferLegacy1901Fingerprint).let {
it.method.apply { it.method.apply {
val byteArrayParameter = "p3" val byteArrayParameter = "p3"
val startIndex = it.patternMatch!!.startIndex val startIndex = it.patternMatch!!.startIndex
@@ -269,7 +268,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
move-object v$returnEmptyComponentRegister, p1 # Required for 19.47 move-object v$returnEmptyComponentRegister, p1 # Required for 19.47
goto :return_empty_component goto :return_empty_component
:show :show
const/4 v$freeRegister, 0x0 # Restore register, required for 19.16 nop
""", """,
ExternalLabel("return_empty_component", returnEmptyComponentInstruction), ExternalLabel("return_empty_component", returnEmptyComponentInstruction),
) )

View File

@@ -63,7 +63,8 @@ val hideInfoCardsPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {

View File

@@ -30,7 +30,8 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
"19.43.41", "19.43.41",
"19.47.53", "19.47.53",
"20.07.39", "20.07.39",
), "20.12.46",
)
) )
execute { execute {
@@ -51,7 +52,10 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
SwitchPreference("revanced_hide_player_flyout_speed"), SwitchPreference("revanced_hide_player_flyout_speed"),
SwitchPreference("revanced_hide_player_flyout_lock_screen"), SwitchPreference("revanced_hide_player_flyout_lock_screen"),
SwitchPreference("revanced_hide_player_flyout_more_info"), SwitchPreference("revanced_hide_player_flyout_more_info"),
SwitchPreference("revanced_hide_player_flyout_audio_track"), SwitchPreference(
key = "revanced_hide_player_flyout_audio_track",
tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference"
),
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"), SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
SwitchPreference("revanced_hide_player_flyout_sleep_timer"), SwitchPreference("revanced_hide_player_flyout_sleep_timer"),
SwitchPreference("revanced_hide_player_flyout_video_quality_footer"), SwitchPreference("revanced_hide_player_flyout_video_quality_footer"),

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