Compare commits

...

183 Commits

Author SHA1 Message Date
semantic-release-bot
055ad04281 chore: Release v5.10.0-dev.6 [skip ci]
# [5.10.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.5...v5.10.0-dev.6) (2025-01-29)

### Bug Fixes

* **YouTube - Hide Ads:** Hide end screen store banner without leaving empty space ([#4367](https://github.com/ReVanced/revanced-patches/issues/4367)) ([aaeee4a](aaeee4a895))
2025-01-29 07:44:09 +00:00
LisoUseInAIKyrios
aaeee4a895 fix(YouTube - Hide Ads): Hide end screen store banner without leaving empty space (#4367) 2025-01-29 09:40:59 +02:00
semantic-release-bot
654b339f66 chore: Release v5.10.0-dev.5 [skip ci]
# [5.10.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.4...v5.10.0-dev.5) (2025-01-27)

### Bug Fixes

* **YouTube - Hide video description components:** Use correct string key names ([64cdce2](64cdce28a6))
2025-01-27 15:01:10 +00:00
LisoUseInAIKyrios
64cdce28a6 fix(YouTube - Hide video description components): Use correct string key names 2025-01-27 16:58:44 +02:00
semantic-release-bot
d01b9a67c5 chore: Release v5.10.0-dev.4 [skip ci]
# [5.10.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.3...v5.10.0-dev.4) (2025-01-27)

### Features

* **YouTube - Hide video description components:** Add `Hide How this content was made section` ([#4355](https://github.com/ReVanced/revanced-patches/issues/4355)) ([a72404e](a72404eeab))
2025-01-27 08:39:16 +00:00
ILoveOpenSourceApplications
a72404eeab feat(YouTube - Hide video description components): Add Hide How this content was made section (#4355) 2025-01-27 10:36:13 +02:00
semantic-release-bot
3ff104528e chore: Release v5.10.0-dev.3 [skip ci]
# [5.10.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.2...v5.10.0-dev.3) (2025-01-27)

### Features

* **YouTube - Hide ads:** Add `Hide end screen store banner` ([#4351](https://github.com/ReVanced/revanced-patches/issues/4351)) ([76bbd7e](76bbd7ed2f))
2025-01-27 08:35:53 +00:00
ILoveOpenSourceApplications
76bbd7ed2f feat(YouTube - Hide ads): Add Hide end screen store banner (#4351) 2025-01-27 10:32:15 +02:00
semantic-release-bot
2fdf0f85c1 chore: Release v5.10.0-dev.2 [skip ci]
# [5.10.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.1...v5.10.0-dev.2) (2025-01-25)

### Features

* **YouTube:** Add patch `Disable HDR video` ([#4347](https://github.com/ReVanced/revanced-patches/issues/4347)) ([1d12c41](1d12c4156d))
2025-01-25 08:29:47 +00:00
LisoUseInAIKyrios
1d12c4156d feat(YouTube): Add patch Disable HDR video (#4347) 2025-01-25 10:26:46 +02:00
semantic-release-bot
c43050dce8 chore: Release v5.10.0-dev.1 [skip ci]
# [5.10.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.4...v5.10.0-dev.1) (2025-01-23)

### Features

* **YouTube - Theme:** Add option to use custom seekbar accent color ([#4337](https://github.com/ReVanced/revanced-patches/issues/4337)) ([8104bbd](8104bbd7d7))
2025-01-23 20:18:40 +00:00
LisoUseInAIKyrios
8104bbd7d7 feat(YouTube - Theme): Add option to use custom seekbar accent color (#4337) 2025-01-23 22:15:23 +02:00
semantic-release-bot
8487888e6b chore: Release v5.9.1-dev.4 [skip ci]
## [5.9.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.3...v5.9.1-dev.4) (2025-01-22)

### Bug Fixes

* **YouTube - Hide layout components:** Hide new kind of community post ([#4341](https://github.com/ReVanced/revanced-patches/issues/4341)) ([6721a28](6721a284cd))
2025-01-22 21:03:27 +00:00
Bceez
6721a284cd fix(YouTube - Hide layout components): Hide new kind of community post (#4341) 2025-01-22 22:00:33 +01:00
semantic-release-bot
6cde702854 chore: Release v5.9.1-dev.3 [skip ci]
## [5.9.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.2...v5.9.1-dev.3) (2025-01-22)

### Bug Fixes

* **YouTube - Hide seekbar:** Do not hide player seekbar if hide feed seekbar is enabled ([#4333](https://github.com/ReVanced/revanced-patches/issues/4333)) ([7c8efca](7c8efcaf41))
2025-01-22 12:01:57 +00:00
LisoUseInAIKyrios
7c8efcaf41 fix(YouTube - Hide seekbar): Do not hide player seekbar if hide feed seekbar is enabled (#4333) 2025-01-22 12:57:53 +01:00
semantic-release-bot
350ee02e3b chore: Release v5.9.1-dev.2 [skip ci]
## [5.9.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.1...v5.9.1-dev.2) (2025-01-22)

### Bug Fixes

* **YouTube - Theme:** Fix 19.25 - 19.45 patch error ([df2d070](df2d070a43))
2025-01-22 08:26:21 +00:00
LisoUseInAIKyrios
df2d070a43 fix(YouTube - Theme): Fix 19.25 - 19.45 patch error 2025-01-22 09:23:31 +01:00
semantic-release-bot
8167aaccc8 chore: Release v5.9.1-dev.1 [skip ci]
## [5.9.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.9.0...v5.9.1-dev.1) (2025-01-21)

### Bug Fixes

* **YouTube - Theme:** Replace custom seekbar gradient colors instead of disabling ([#4329](https://github.com/ReVanced/revanced-patches/issues/4329)) ([f4989ed](f4989ed0a5))
2025-01-21 20:23:57 +00:00
LisoUseInAIKyrios
f4989ed0a5 fix(YouTube - Theme): Replace custom seekbar gradient colors instead of disabling (#4329) 2025-01-21 21:20:19 +01:00
semantic-release-bot
8f5a0531bc chore: Release v5.9.0 [skip ci]
# [5.9.0](https://github.com/ReVanced/revanced-patches/compare/v5.8.1...v5.9.0) (2025-01-20)

### Bug Fixes

* **YouTube - Spoof video streams:** Resolve playback issues after changing from cellular to wifi ([#4277](https://github.com/ReVanced/revanced-patches/issues/4277)) ([fcad0ab](fcad0ab5bb))
* **YouTube - Spoof video streams:** Update client user-agent ([#4304](https://github.com/ReVanced/revanced-patches/issues/4304)) ([d85bcc3](d85bcc3c16))

### Features

* **YouTube - Hide feed components:** Handle new type of surveys ([#4295](https://github.com/ReVanced/revanced-patches/issues/4295)) ([e5e897d](e5e897de77))
* **YouTube - Playback speed:** Add option to change 2x tap and hold speed ([#4307](https://github.com/ReVanced/revanced-patches/issues/4307)) ([0615990](0615990138))
* **YouTube - Settings:** Add option to use new Cairo settings menus ([#4305](https://github.com/ReVanced/revanced-patches/issues/4305)) ([064b859](064b859d39))
2025-01-20 11:18:05 +00:00
LisoUseInAIKyrios
622554de14 chore: Merge branch dev to main (#4280) 2025-01-20 13:14:47 +02:00
github-actions[bot]
66e330ffe6 chore: Sync translations (#4319) 2025-01-20 12:14:23 +01:00
LisoUseInAIKyrios
2afcd3d63d chore: Change localized string log to warning 2025-01-20 11:52:31 +01:00
semantic-release-bot
80d7c78cf6 chore: Release v5.9.0-dev.4 [skip ci]
# [5.9.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.3...v5.9.0-dev.4) (2025-01-20)

### Bug Fixes

* **YouTube - Spoof video streams:** Update client user-agent ([#4304](https://github.com/ReVanced/revanced-patches/issues/4304)) ([d85bcc3](d85bcc3c16))
2025-01-20 10:51:48 +00:00
LisoUseInAIKyrios
d85bcc3c16 fix(YouTube - Spoof video streams): Update client user-agent (#4304) 2025-01-20 11:49:00 +01:00
github-actions[bot]
21368ea696 chore: Sync translations (#4318) 2025-01-20 11:48:40 +01:00
semantic-release-bot
e687d3ed37 chore: Release v5.9.0-dev.3 [skip ci]
# [5.9.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.2...v5.9.0-dev.3) (2025-01-19)

### Features

* **YouTube - Settings:** Add option to use new Cairo settings menus ([#4305](https://github.com/ReVanced/revanced-patches/issues/4305)) ([064b859](064b859d39))
2025-01-19 23:25:39 +00:00
LisoUseInAIKyrios
064b859d39 feat(YouTube - Settings): Add option to use new Cairo settings menus (#4305)
Co-authored-by: MarcaDian <152095496+marcadian@users.noreply.github.com>
2025-01-20 01:22:15 +02:00
github-actions[bot]
89882ddaf8 chore: Sync translations (#4316) 2025-01-20 01:21:48 +02:00
semantic-release-bot
41881ba161 chore: Release v5.9.0-dev.2 [skip ci]
# [5.9.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.1...v5.9.0-dev.2) (2025-01-18)

### Features

* **YouTube - Playback speed:** Add option to change 2x tap and hold speed ([#4307](https://github.com/ReVanced/revanced-patches/issues/4307)) ([0615990](0615990138))
2025-01-18 09:40:42 +00:00
LisoUseInAIKyrios
0615990138 feat(YouTube - Playback speed): Add option to change 2x tap and hold speed (#4307) 2025-01-18 10:37:34 +01:00
semantic-release-bot
70532313db chore: Release v5.9.0-dev.1 [skip ci]
# [5.9.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.2-dev.1...v5.9.0-dev.1) (2025-01-17)

### Features

* **YouTube - Hide feed components:** Handle new type of surveys ([#4295](https://github.com/ReVanced/revanced-patches/issues/4295)) ([e5e897d](e5e897de77))
2025-01-17 00:28:17 +00:00
ILoveOpenSourceApplications
e5e897de77 feat(YouTube - Hide feed components): Handle new type of surveys (#4295) 2025-01-17 01:25:43 +01:00
semantic-release-bot
1e57ce9658 chore: Release v5.8.2-dev.1 [skip ci]
## [5.8.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.1...v5.8.2-dev.1) (2025-01-09)

### Bug Fixes

* **YouTube - Spoof video streams:** Resolve playback issues after changing from cellular to wifi ([#4277](https://github.com/ReVanced/revanced-patches/issues/4277)) ([fcad0ab](fcad0ab5bb))
2025-01-09 17:13:13 +00:00
LisoUseInAIKyrios
fcad0ab5bb fix(YouTube - Spoof video streams): Resolve playback issues after changing from cellular to wifi (#4277) 2025-01-09 18:09:44 +01:00
semantic-release-bot
91471eccf9 chore: Release v5.8.1 [skip ci]
## [5.8.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.0...v5.8.1) (2025-01-07)

### Bug Fixes

* **YouTube - Spoof video streams:** Add 'Android Creator' ([#4262](https://github.com/ReVanced/revanced-patches/issues/4262)) ([3ee99b7](3ee99b7bf1))
2025-01-07 16:01:12 +00:00
oSumAtrIX
d559f016c6 chore: Merge branch dev to main (#4271) 2025-01-07 16:57:59 +01:00
github-actions[bot]
5a82d26f03 chore: Sync translations (#4275) 2025-01-07 12:17:17 +01:00
LisoUseInAIKyrios
e2eae499d9 ci: Fix crowdin cron pull strings? 2025-01-07 12:12:53 +01:00
LisoUseInAIKyrios
64919d6443 chore: Fix typo 2025-01-06 14:57:11 +01:00
semantic-release-bot
c6ffaf86ae chore: Release v5.8.1-dev.1 [skip ci]
## [5.8.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.0...v5.8.1-dev.1) (2025-01-06)

### Bug Fixes

* **YouTube - Spoof video streams:** Add 'Android Creator' ([#4262](https://github.com/ReVanced/revanced-patches/issues/4262)) ([3ee99b7](3ee99b7bf1))
2025-01-06 11:01:28 +00:00
LisoUseInAIKyrios
3ee99b7bf1 fix(YouTube - Spoof video streams): Add 'Android Creator' (#4262) 2025-01-06 11:58:27 +01:00
semantic-release-bot
6f9bf4873f chore: Release v5.8.0 [skip ci]
# [5.8.0](https://github.com/ReVanced/revanced-patches/compare/v5.7.2...v5.8.0) (2024-12-30)

### Bug Fixes

* **GmsCore support:** Do not show battery optimization error on Android Automotive devices (Google built-in) ([#4218](https://github.com/ReVanced/revanced-patches/issues/4218)) ([fa4aa54](fa4aa54f0c))
* **YouTube - Exit fullscreen mode:** Exit fullscreen mode of first video opened after cold start ([9496438](9496438da1))
* **YouTube - Force original audio:** If stream spoofing to Android then show a summary text why force audio is not available ([#4220](https://github.com/ReVanced/revanced-patches/issues/4220)) ([4de768f](4de768febf))
* **YouTube - Spoof video streams:** Ignore harmless error toast if hide ads is disabled ([e7c6943](e7c6943ca7))

### Features

* **Swipe controls:** Add option to enable/disable fullscreen swipe to next video ([#4222](https://github.com/ReVanced/revanced-patches/issues/4222)) ([29dbc9f](29dbc9ffbf))
* **YouTube - Hide Shorts components:** Add option to hide Shorts in watch history ([#4214](https://github.com/ReVanced/revanced-patches/issues/4214)) ([094a6aa](094a6aa6de))
* **YouTube - Spoof app version:** Add 'Restore old navigation and toolbar icons' ([9fac161](9fac1614e7))
* **YouTube:** Add `Change form factor` patch ([#4217](https://github.com/ReVanced/revanced-patches/issues/4217)) ([189e1c9](189e1c90c4))
* **YouTube:** Add `Exit fullscreen mode` patch ([#4223](https://github.com/ReVanced/revanced-patches/issues/4223)) ([f3c4d6f](f3c4d6fd64))
* **YouTube:** Add in app option to select a preferred language for ReVanced specific text ([#4231](https://github.com/ReVanced/revanced-patches/issues/4231)) ([7b90baa](7b90baadb5))
2024-12-30 01:50:09 +00:00
LisoUseInAIKyrios
29a73089a3 chore: Merge branch dev to main (#4213) 2024-12-30 05:46:51 +04:00
github-actions[bot]
74ef1841eb chore: Sync translations (#4240) 2024-12-30 05:41:11 +04:00
github-actions[bot]
0c544d28e3 chore: Sync translations (#4239) 2024-12-30 04:55:33 +04:00
semantic-release-bot
b1e5b99b44 chore: Release v5.8.0-dev.8 [skip ci]
# [5.8.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.7...v5.8.0-dev.8) (2024-12-28)

### Features

* **YouTube:** Add in app option to select a preferred language for ReVanced specific text ([#4231](https://github.com/ReVanced/revanced-patches/issues/4231)) ([7b90baa](7b90baadb5))
2024-12-28 08:33:56 +00:00
LisoUseInAIKyrios
7b90baadb5 feat(YouTube): Add in app option to select a preferred language for ReVanced specific text (#4231) 2024-12-28 12:30:57 +04:00
semantic-release-bot
4a6f3c8555 chore: Release v5.8.0-dev.7 [skip ci]
# [5.8.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.6...v5.8.0-dev.7) (2024-12-27)

### Bug Fixes

* **YouTube - Spoof video streams:** Ignore harmless error toast if hide ads is disabled ([e7c6943](e7c6943ca7))
2024-12-27 21:13:07 +00:00
LisoUseInAIKyrios
e7c6943ca7 fix(YouTube - Spoof video streams): Ignore harmless error toast if hide ads is disabled 2024-12-28 01:10:01 +04:00
semantic-release-bot
ae1b987c0d chore: Release v5.8.0-dev.6 [skip ci]
# [5.8.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.5...v5.8.0-dev.6) (2024-12-27)

### Bug Fixes

* **YouTube - Exit fullscreen mode:** Exit fullscreen mode of first video opened after cold start ([9496438](9496438da1))
2024-12-27 15:34:52 +00:00
LisoUseInAIKyrios
9496438da1 fix(YouTube - Exit fullscreen mode): Exit fullscreen mode of first video opened after cold start 2024-12-27 19:31:39 +04:00
github-actions[bot]
fa51631ea6 chore: Sync translations (#4232) 2024-12-27 19:30:03 +04:00
LisoUseInAIKyrios
8bf7108001 ci: Not fixing Crowdin cron task 2024-12-27 19:27:52 +04:00
LisoUseInAIKyrios
030eece04a refactor(YouTube - Exit fullscreen mode): Improve logging 2024-12-27 18:19:43 +04:00
LisoUseInAIKyrios
30009b723d refactor: Change context field to volatile
Field is set from main thread, but can be immediately accessed by non main threads.
2024-12-27 11:15:35 +04:00
semantic-release-bot
53b25ea7e9 chore: Release v5.8.0-dev.5 [skip ci]
# [5.8.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.4...v5.8.0-dev.5) (2024-12-27)

### Features

* **YouTube:** Add `Change form factor` patch ([#4217](https://github.com/ReVanced/revanced-patches/issues/4217)) ([189e1c9](189e1c90c4))
2024-12-27 06:51:57 +00:00
LisoUseInAIKyrios
189e1c90c4 feat(YouTube): Add Change form factor patch (#4217) 2024-12-27 10:48:14 +04:00
github-actions[bot]
f01603b3f3 chore: Sync translations (#4229) 2024-12-27 10:46:32 +04:00
semantic-release-bot
3db5651e5c chore: Release v5.8.0-dev.4 [skip ci]
# [5.8.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.3...v5.8.0-dev.4) (2024-12-27)

### Bug Fixes

* **GmsCore support:** Do not show battery optimization error on Android Automotive devices (Google built-in) ([#4218](https://github.com/ReVanced/revanced-patches/issues/4218)) ([fa4aa54](fa4aa54f0c))

### Features

* **Swipe controls:** Add option to enable/disable fullscreen swipe to next video ([#4222](https://github.com/ReVanced/revanced-patches/issues/4222)) ([29dbc9f](29dbc9ffbf))
* **YouTube:** Add `Exit fullscreen mode` patch ([#4223](https://github.com/ReVanced/revanced-patches/issues/4223)) ([f3c4d6f](f3c4d6fd64))
2024-12-27 06:28:58 +00:00
LisoUseInAIKyrios
f3c4d6fd64 feat(YouTube): Add Exit fullscreen mode patch (#4223) 2024-12-27 10:25:17 +04:00
LisoUseInAIKyrios
29dbc9ffbf feat(Swipe controls): Add option to enable/disable fullscreen swipe to next video (#4222) 2024-12-27 10:23:30 +04:00
LisoUseInAIKyrios
fa4aa54f0c fix(GmsCore support): Do not show battery optimization error on Android Automotive devices (Google built-in) (#4218) 2024-12-27 10:22:50 +04:00
github-actions[bot]
1d89ada07f chore: Sync translations (#4228) 2024-12-27 10:22:25 +04:00
LisoUseInAIKyrios
8c529abad5 ci: Fix Crowdin cron task? 2024-12-27 10:17:28 +04:00
LisoUseInAIKyrios
4ade7c7329 ci: Fix Crowdin cron task? 2024-12-26 14:08:46 +04:00
semantic-release-bot
f35247a872 chore: Release v5.8.0-dev.3 [skip ci]
# [5.8.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.2...v5.8.0-dev.3) (2024-12-26)

### Bug Fixes

* **YouTube - Force original audio:** If stream spoofing to Android then show a summary text why force audio is not available ([#4220](https://github.com/ReVanced/revanced-patches/issues/4220)) ([4de768f](4de768febf))
2024-12-26 10:02:26 +00:00
LisoUseInAIKyrios
4de768febf fix(YouTube - Force original audio): If stream spoofing to Android then show a summary text why force audio is not available (#4220) 2024-12-26 13:58:29 +04:00
github-actions[bot]
1a5c86db93 chore: Sync translations (#4216) 2024-12-26 13:58:13 +04:00
LisoUseInAIKyrios
dbba795468 chore(YouTube): Fix inconsistent strings 2024-12-25 04:59:12 +04:00
semantic-release-bot
0a9320551d chore: Release v5.8.0-dev.2 [skip ci]
# [5.8.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.1...v5.8.0-dev.2) (2024-12-24)

### Features

* **YouTube - Spoof app version:** Add 'Restore old navigation and toolbar icons' ([9fac161](9fac1614e7))
2024-12-24 22:11:57 +00:00
LisoUseInAIKyrios
9fac1614e7 feat(YouTube - Spoof app version): Add 'Restore old navigation and toolbar icons' 2024-12-25 02:09:10 +04:00
semantic-release-bot
2de3523c59 chore: Release v5.8.0-dev.1 [skip ci]
# [5.8.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.2...v5.8.0-dev.1) (2024-12-24)

### Features

* **YouTube - Hide Shorts components:** Add option to hide Shorts in watch history ([#4214](https://github.com/ReVanced/revanced-patches/issues/4214)) ([094a6aa](094a6aa6de))
2024-12-24 21:37:46 +00:00
github-actions[bot]
ad1e40b130 chore: Sync translations (#4215) 2024-12-25 01:34:11 +04:00
LisoUseInAIKyrios
094a6aa6de feat(YouTube - Hide Shorts components): Add option to hide Shorts in watch history (#4214) 2024-12-25 01:32:42 +04:00
LisoUseInAIKyrios
a14e03e4bb chore(YouTube - Spoof video streams): Update iOS side effects text 2024-12-24 18:40:55 +04:00
semantic-release-bot
6f40b6d30f chore: Release v5.7.2 [skip ci]
## [5.7.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1...v5.7.2) (2024-12-24)

### Bug Fixes

* **YouTube - Hide layout components:** Don't hide Shorts channel bar when toggling for video player ([87e1c7f](87e1c7f4c8))
* **YouTube - Spoof video streams:** Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds ([#4202](https://github.com/ReVanced/revanced-patches/issues/4202)) ([ca21a69](ca21a69550))
2024-12-24 06:55:57 +00:00
LisoUseInAIKyrios
1711e1c39d chore: Merge branch dev to main (#4205) 2024-12-24 10:52:54 +04:00
github-actions[bot]
25372828d1 chore: Sync translations (#4210) 2024-12-24 10:52:18 +04:00
semantic-release-bot
f58245c6cd chore: Release v5.7.2-dev.2 [skip ci]
## [5.7.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.2-dev.1...v5.7.2-dev.2) (2024-12-23)

### Bug Fixes

* **YouTube - Hide layout components:** Don't hide Shorts channel bar when toggling for video player ([87e1c7f](87e1c7f4c8))
2024-12-23 23:00:32 +00:00
oSumAtrIX
87e1c7f4c8 fix(YouTube - Hide layout components): Don't hide Shorts channel bar when toggling for video player 2024-12-23 23:57:53 +01:00
semantic-release-bot
55d01c92d1 chore: Release v5.7.2-dev.1 [skip ci]
## [5.7.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.1...v5.7.2-dev.1) (2024-12-23)

### Bug Fixes

* **YouTube - Spoof video streams:** Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds ([#4202](https://github.com/ReVanced/revanced-patches/issues/4202)) ([ca21a69](ca21a69550))
2024-12-23 18:42:40 +00:00
LisoUseInAIKyrios
ca21a69550 fix(YouTube - Spoof video streams): Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds (#4202) 2024-12-23 22:39:27 +04:00
semantic-release-bot
634d0b4058 chore: Release v5.7.1 [skip ci]
## [5.7.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.0...v5.7.1) (2024-12-23)

### Bug Fixes

* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([838edb4](838edb48e7))
* **YouTube - Spoof video streams:** Use 2 letter device language code ([e174113](e1741130af))
* **YouTube - Spoof video streams:** Use Android VR authentication if using default audio language ([#4191](https://github.com/ReVanced/revanced-patches/issues/4191)) ([99334d1](99334d1e53))
* **YouTube - Theme:** Use dark theme color for status and navigation bar ([4b81f70](4b81f7009b))
* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([0ae756b](0ae756b0fc))
2024-12-23 01:16:40 +00:00
LisoUseInAIKyrios
47ea8d5ec8 chore: Merge branch dev to main (#4192) 2024-12-23 05:13:45 +04:00
github-actions[bot]
9509ed53f3 chore: Sync translations (#4198) 2024-12-23 04:53:27 +04:00
semantic-release-bot
39542ddf55 chore: Release v5.7.1-dev.5 [skip ci]
## [5.7.1-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.4...v5.7.1-dev.5) (2024-12-22)

### Bug Fixes

* **YouTube - Spoof video streams:** Use 2 letter device language code ([e174113](e1741130af))
2024-12-22 23:40:44 +00:00
LisoUseInAIKyrios
e1741130af fix(YouTube - Spoof video streams): Use 2 letter device language code 2024-12-23 03:37:21 +04:00
semantic-release-bot
e54eb3ce87 chore: Release v5.7.1-dev.4 [skip ci]
## [5.7.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.3...v5.7.1-dev.4) (2024-12-22)

### Bug Fixes

* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([0ae756b](0ae756b0fc))
2024-12-22 17:45:50 +00:00
LisoUseInAIKyrios
0ae756b0fc fix(YouTube): Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) (#4195) 2024-12-22 21:42:41 +04:00
github-actions[bot]
77a0ac5c9c chore: Sync translations (#4196) 2024-12-22 21:42:29 +04:00
semantic-release-bot
899121b9de chore: Release v5.7.1-dev.3 [skip ci]
## [5.7.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.2...v5.7.1-dev.3) (2024-12-22)

### Bug Fixes

* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([838edb4](838edb48e7))
2024-12-22 17:34:19 +00:00
LisoUseInAIKyrios
838edb48e7 fix(YouTube - SponsorBlock): Show a toast and not a dialog if segment submitted successfully 2024-12-22 21:31:41 +04:00
semantic-release-bot
b2665c916a chore: Release v5.7.1-dev.2 [skip ci]
## [5.7.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.1...v5.7.1-dev.2) (2024-12-22)

### Bug Fixes

* **YouTube - Theme:** Use dark theme color for status and navigation bar ([4b81f70](4b81f7009b))
2024-12-22 11:29:57 +00:00
LisoUseInAIKyrios
4b81f7009b fix(YouTube - Theme): Use dark theme color for status and navigation bar 2024-12-22 15:27:02 +04:00
semantic-release-bot
1a4c39a2ee chore: Release v5.7.1-dev.1 [skip ci]
## [5.7.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.0...v5.7.1-dev.1) (2024-12-22)

### Bug Fixes

* **YouTube - Spoof video streams:** Use Android VR authentication if using default audio language ([#4191](https://github.com/ReVanced/revanced-patches/issues/4191)) ([99334d1](99334d1e53))
2024-12-22 10:26:04 +00:00
LisoUseInAIKyrios
99334d1e53 fix(YouTube - Spoof video streams): Use Android VR authentication if using default audio language (#4191) 2024-12-22 14:22:49 +04:00
semantic-release-bot
2850a6ed4e chore: Release v5.7.0 [skip ci]
# [5.7.0](https://github.com/ReVanced/revanced-patches/compare/v5.6.0...v5.7.0) (2024-12-22)

### Bug Fixes

* **YouTube - Force original audio:** Use correct availability for settings UI ([a0b63df](a0b63dfa23))
* **YouTube - Spoof video stream:** Remove UI client type setting.  Allow setting default audio language. ([#4184](https://github.com/ReVanced/revanced-patches/issues/4184)) ([aeedec7](aeedec7fed))
* **YouTube - Spoof video streams:** Remove iOS, add clients Android TV and Android Creator ([#4180](https://github.com/ReVanced/revanced-patches/issues/4180)) ([cdb6820](cdb68209d1))
* **YouTube:** Change fingerprints to support a wider range of target versions ([25d7cc6](25d7cc68ae))

### Features

* **YouTube:** Support version `19.47.53` ([#4182](https://github.com/ReVanced/revanced-patches/issues/4182)) ([b8635d0](b8635d0b88))
2024-12-22 07:53:45 +00:00
LisoUseInAIKyrios
f28eb5105b chore: Merge branch dev to main (#4188) 2024-12-22 11:50:32 +04:00
github-actions[bot]
69bed4d9fa chore: Sync translations (#4187) 2024-12-22 11:49:37 +04:00
oSumAtrIX
a5f1efac27 chore: Merge branch dev to main (#4183) 2024-12-22 08:48:36 +01:00
semantic-release-bot
b51be82cff chore: Release v5.7.0-dev.1 [skip ci]
# [5.7.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.4...v5.7.0-dev.1) (2024-12-21)

### Features

* **YouTube:** Support version `19.47.53` ([#4182](https://github.com/ReVanced/revanced-patches/issues/4182)) ([b8635d0](b8635d0b88))
2024-12-21 15:09:41 +00:00
LisoUseInAIKyrios
b8635d0b88 feat(YouTube): Support version 19.47.53 (#4182) 2024-12-21 19:06:51 +04:00
semantic-release-bot
78699c8bbf chore: Release v5.6.1-dev.4 [skip ci]
## [5.6.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.3...v5.6.1-dev.4) (2024-12-21)

### Bug Fixes

* **YouTube - Spoof video stream:** Remove UI client type setting.  Allow setting default audio language. ([#4184](https://github.com/ReVanced/revanced-patches/issues/4184)) ([aeedec7](aeedec7fed))
2024-12-21 14:53:47 +00:00
LisoUseInAIKyrios
aeedec7fed fix(YouTube - Spoof video stream): Remove UI client type setting. Allow setting default audio language. (#4184) 2024-12-21 18:49:56 +04:00
semantic-release-bot
32b614696b chore: Release v5.6.1-dev.3 [skip ci]
## [5.6.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.2...v5.6.1-dev.3) (2024-12-21)

### Bug Fixes

* **YouTube - Force original audio:** Use correct availability for settings UI ([a0b63df](a0b63dfa23))
2024-12-21 12:11:12 +00:00
LisoUseInAIKyrios
a0b63dfa23 fix(YouTube - Force original audio): Use correct availability for settings UI 2024-12-21 16:08:22 +04:00
semantic-release-bot
f0f53cf72f chore: Release v5.6.1-dev.2 [skip ci]
## [5.6.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.1...v5.6.1-dev.2) (2024-12-21)

### Bug Fixes

* **YouTube - Spoof video streams:** Remove iOS, add clients Android TV and Android Creator ([#4180](https://github.com/ReVanced/revanced-patches/issues/4180)) ([cdb6820](cdb68209d1))
2024-12-21 11:37:01 +00:00
LisoUseInAIKyrios
cdb68209d1 fix(YouTube - Spoof video streams): Remove iOS, add clients Android TV and Android Creator (#4180) 2024-12-21 15:33:43 +04:00
semantic-release-bot
7369f7b8d5 chore: Release v5.6.1-dev.1 [skip ci]
## [5.6.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.6.0...v5.6.1-dev.1) (2024-12-21)

### Bug Fixes

* **YouTube:** Change fingerprints to support a wider range of target versions ([25d7cc6](25d7cc68ae))
2024-12-21 10:44:05 +00:00
LisoUseInAIKyrios
db521b940b refactor(YouTube): Change fingerprints to support a wider range of target versions (#4179) 2024-12-21 14:41:22 +04:00
LisoUseInAIKyrios
25d7cc68ae fix(YouTube): Change fingerprints to support a wider range of target versions 2024-12-21 12:55:18 +04:00
semantic-release-bot
9495064e6e chore: Release v5.6.0 [skip ci]
# [5.6.0](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.6.0) (2024-12-20)

### Bug Fixes

* **Twitter - Change link sharing domain:** Use correct extension package ([20a7ad4](20a7ad4715))
* **YouTube - Force original audio:** Use correct original audio stream if app language is not English ([1d0ec98](1d0ec98bec))
* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([246333f](246333f3dc))
* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([c87c788](c87c788a26))
* **YouTube - Open Shorts in regular player:** Do not show the miniplayer after opening a Short while a video is playing ([c7f42d9](c7f42d9a3c))
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([44995a9](44995a9f15))
* **YouTube - Spoof video streams:** Change default spoofing to iOS, allow setting a default language with Android VR ([#4171](https://github.com/ReVanced/revanced-patches/issues/4171)) ([cd08717](cd08717783))
* **YouTube - Spoof video streams:** Update iOS client version ([ec746cb](ec746cb05a))

### Features

* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([1dde485](1dde485013))
2024-12-20 21:57:47 +00:00
LisoUseInAIKyrios
64864c2cdb chore: Merge branch dev to main (#4147) 2024-12-21 01:54:28 +04:00
github-actions[bot]
ad0ffb3328 chore: Sync translations (#4173) 2024-12-21 01:48:25 +04:00
semantic-release-bot
06800324aa chore: Release v5.6.0-dev.6 [skip ci]
# [5.6.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.5...v5.6.0-dev.6) (2024-12-20)

### Bug Fixes

* **YouTube - Spoof video streams:** Update iOS client version ([ec746cb](ec746cb05a))
2024-12-20 21:38:18 +00:00
LisoUseInAIKyrios
ec746cb05a fix(YouTube - Spoof video streams): Update iOS client version 2024-12-21 01:35:06 +04:00
semantic-release-bot
67c5530ea6 chore: Release v5.6.0-dev.5 [skip ci]
# [5.6.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.4...v5.6.0-dev.5) (2024-12-20)

### Bug Fixes

* **YouTube - Spoof video streams:** Change default spoofing to iOS, allow setting a default language with Android VR ([#4171](https://github.com/ReVanced/revanced-patches/issues/4171)) ([cd08717](cd08717783))
2024-12-20 20:41:10 +00:00
LisoUseInAIKyrios
cd08717783 fix(YouTube - Spoof video streams): Change default spoofing to iOS, allow setting a default language with Android VR (#4171) 2024-12-21 00:38:11 +04:00
semantic-release-bot
7bac023ea5 chore: Release v5.6.0-dev.4 [skip ci]
# [5.6.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.3...v5.6.0-dev.4) (2024-12-20)

### Bug Fixes

* **YouTube - Force original audio:** Use correct original audio stream if app language is not English ([1d0ec98](1d0ec98bec))
2024-12-20 16:29:05 +00:00
LisoUseInAIKyrios
1d0ec98bec fix(YouTube - Force original audio): Use correct original audio stream if app language is not English 2024-12-20 20:26:17 +04:00
semantic-release-bot
3c603fac2d chore: Release v5.6.0-dev.3 [skip ci]
# [5.6.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.2...v5.6.0-dev.3) (2024-12-20)

### Bug Fixes

* **Twitter - Change link sharing domain:** Use correct extension package ([20a7ad4](20a7ad4715))
2024-12-20 07:16:17 +00:00
LisoUseInAIKyrios
20a7ad4715 fix(Twitter - Change link sharing domain): Use correct extension package 2024-12-20 11:12:50 +04:00
semantic-release-bot
25a60e305e chore: Release v5.6.0-dev.2 [skip ci]
# [5.6.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.1...v5.6.0-dev.2) (2024-12-19)

### Bug Fixes

* **YouTube - Open Shorts in regular player:** Do not show the miniplayer after opening a Short while a video is playing ([c7f42d9](c7f42d9a3c))
2024-12-19 11:20:50 +00:00
LisoUseInAIKyrios
c7f42d9a3c fix(YouTube - Open Shorts in regular player): Do not show the miniplayer after opening a Short while a video is playing 2024-12-19 15:18:18 +04:00
github-actions[bot]
670f100a29 chore: Sync translations (#4166) 2024-12-19 15:17:50 +04:00
semantic-release-bot
19140e5918 chore: Release v5.6.0-dev.1 [skip ci]
# [5.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.2...v5.6.0-dev.1) (2024-12-19)

### Features

* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([1dde485](1dde485013))
2024-12-19 08:14:57 +00:00
LisoUseInAIKyrios
1dde485013 feat(YouTube): Add Open Shorts in regular player patch (#4153) 2024-12-19 12:11:19 +04:00
LisoUseInAIKyrios
5efcdd31c8 ci: Don't upload strings to Crowdin when pulling (#4164) 2024-12-18 19:04:09 +01:00
github-actions[bot]
e6529837cb chore: Sync translations (#4162) 2024-12-18 16:29:33 +04:00
semantic-release-bot
fe07033444 chore: Release v5.5.2-dev.2 [skip ci]
## [5.5.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.1...v5.5.2-dev.2) (2024-12-17)

### Bug Fixes

* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([246333f](246333f3dc))
2024-12-17 15:50:15 +00:00
Bceez
246333f3dc fix(YouTube - Hide layout components): Hide new kind of community post (#4155) 2024-12-17 19:46:46 +04:00
semantic-release-bot
d82b02e4f5 chore: Release v5.5.2-dev.1 [skip ci]
## [5.5.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.5.2-dev.1) (2024-12-17)

### Bug Fixes

* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([c87c788](c87c788a26))
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([44995a9](44995a9f15))
2024-12-17 00:25:18 +00:00
LisoUseInAIKyrios
44995a9f15 fix(YouTube - SponsorBlock): Show create new segment error messages using a dialog (#4148) 2024-12-17 04:22:37 +04:00
LisoUseInAIKyrios
c87c788a26 fix(YouTube - Miniplayer): Use estimated maximum on screen size for devices with low density screens (#4150) 2024-12-17 04:22:16 +04:00
github-actions[bot]
4ef30618d1 chore: Sync translations (#4151) 2024-12-17 04:21:11 +04:00
oSumAtrIX
b23e6c39fc ci: Schedule pulling strings separately from opening a PR (#4146) 2024-12-16 23:02:13 +01:00
semantic-release-bot
de26766543 chore: Release v5.5.1 [skip ci]
## [5.5.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1) (2024-12-16)

### Bug Fixes

* **YouTube:** Fix string translations ([5e8dfed](5e8dfed3e8))
2024-12-16 20:04:21 +00:00
oSumAtrIX
9168b5eaaf chore: Merge branch dev to main (#4142) 2024-12-16 21:01:56 +01:00
semantic-release-bot
c43b9b3b03 chore: Release v5.5.1-dev.1 [skip ci]
## [5.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1-dev.1) (2024-12-16)

### Bug Fixes

* **YouTube:** Fix string translations ([5e8dfed](5e8dfed3e8))
2024-12-16 20:00:52 +00:00
github-actions[bot]
5e8dfed3e8 fix(YouTube): Fix string translations 2024-12-16 23:58:23 +04:00
semantic-release-bot
d67dbba76f chore: Release v5.5.0 [skip ci]
# [5.5.0](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.5.0) (2024-12-16)

### Bug Fixes

* **Twitch:** Change recommended target to the latest app version ([9525137](9525137800))
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client   ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([5965478](59654788fc))
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([ccb6a7f](ccb6a7f161))

### Features

* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([a7aab9a](a7aab9aeca))
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([9e6669d](9e6669d962))
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([68304fd](68304fd96a))
2024-12-16 19:28:30 +00:00
oSumAtrIX
5dc93156e0 chore: Merge branch dev to main (#4123) 2024-12-16 20:25:26 +01:00
github-actions[bot]
5275413ab7 chore: Sync translations (#4136)
Co-authored-by: revanced-bot <github@revanced.app>
2024-12-16 20:24:58 +01:00
semantic-release-bot
248c05b670 chore: Release v5.5.0-dev.5 [skip ci]
# [5.5.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.4...v5.5.0-dev.5) (2024-12-16)

### Features

* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([9e6669d](9e6669d962))
2024-12-16 19:10:30 +00:00
LisoUseInAIKyrios
9e6669d962 feat(YouTube - Navigation buttons): Add options to disable translucent status bar and navigation bar (#4133) 2024-12-16 23:07:08 +04:00
semantic-release-bot
9c81d01cc8 chore: Release v5.5.0-dev.4 [skip ci]
# [5.5.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.3...v5.5.0-dev.4) (2024-12-16)

### Bug Fixes

* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client   ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([5965478](59654788fc))
2024-12-16 18:46:48 +00:00
LisoUseInAIKyrios
59654788fc fix(YouTube - Spoof video streams): Make livestreams start at the current time when using iOS client (#4137) 2024-12-16 22:43:50 +04:00
semantic-release-bot
4c44982cde chore: Release v5.5.0-dev.3 [skip ci]
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)

### Features

* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([a7aab9a](a7aab9aeca))
2024-12-16 18:18:23 +00:00
ILoveOpenSourceApplications
a7aab9aeca feat(YouTube - Hide feed components): Remove obsolete Hide search result shelf header option (#4134) 2024-12-16 19:15:22 +01:00
semantic-release-bot
7a8486f562 chore: Release v5.5.0-dev.2 [skip ci]
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)

### Bug Fixes

* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([ccb6a7f](ccb6a7f161))
2024-12-16 18:10:37 +00:00
oSumAtrIX
ccb6a7f161 fix(YouTube Music): Add Spoof client patch to fix playback (#4132) 2024-12-16 19:07:37 +01:00
LisoUseInAIKyrios
c792edfb77 chore: fix typo 2024-12-15 17:06:36 +04:00
semantic-release-bot
339cd6cc70 chore: Release v5.5.0-dev.1 [skip ci]
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)

### Features

* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([68304fd](68304fd96a))
2024-12-15 12:55:13 +00:00
LisoUseInAIKyrios
68304fd96a feat(YouTube): Add Force original audio patch (#4122) 2024-12-15 16:51:34 +04:00
semantic-release-bot
4033048c9b chore: Release v5.4.1-dev.1 [skip ci]
## [5.4.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.4.1-dev.1) (2024-12-14)

### Bug Fixes

* **Twitch:** Change recommended target to the latest app version ([9525137](9525137800))
2024-12-14 20:18:28 +00:00
LisoUseInAIKyrios
9525137800 fix(Twitch): Change recommended target to the latest app version 2024-12-15 00:15:00 +04:00
semantic-release-bot
0cf05fa2b0 chore: Release v5.4.0 [skip ci]
# [5.4.0](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0) (2024-12-14)

### Bug Fixes

* **GmsCore support:** Adjust presentation of battery optimization dialog  ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([2062660](2062660d60))
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([18f1884](18f18849f3))
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([3c47bff](3c47bfff1a))
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([c348b10](c348b10a35))
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([3ec2577](3ec25778eb))
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([32be03c](32be03c28d))
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([11216cd](11216cd942))
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([78c5118](78c51182f2))

### Features

* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([89c45af](89c45afcc6))
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([c44a4af](c44a4af406))
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([b217ca9](b217ca9f9d))
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([8d06a4a](8d06a4a8ad))
2024-12-14 07:40:53 +00:00
oSumAtrIX
a9bfaf44e2 chore: Merge branch dev to main (#4078) 2024-12-14 08:37:30 +01:00
semantic-release-bot
7b08051371 chore: Release v5.4.0-dev.11 [skip ci]
# [5.4.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.10...v5.4.0-dev.11) (2024-12-14)

### Features

* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([b217ca9](b217ca9f9d))
2024-12-14 07:36:35 +00:00
oSumAtrIX
b217ca9f9d feat(Twitch): Make patches compatible with latest versions (#4099) 2024-12-14 08:33:27 +01:00
github-actions[bot]
9482092579 chore: Sync translations (#4116) 2024-12-14 11:18:19 +04:00
semantic-release-bot
134c2e52bd chore: Release v5.4.0-dev.10 [skip ci]
# [5.4.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.9...v5.4.0-dev.10) (2024-12-13)

### Bug Fixes

* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([c348b10](c348b10a35))
2024-12-13 21:36:09 +00:00
ILoveOpenSourceApplications
c348b10a35 fix(YouTube - Hide ads): Hide new type of featured promotions (#4113) 2024-12-14 01:33:25 +04:00
github-actions[bot]
9a9ec7ef18 chore: Sync translations (#4114) 2024-12-14 01:32:59 +04:00
oSumAtrIX
e746507339 ci: Upload PR build artifact 2024-12-12 23:17:44 +01:00
oSumAtrIX
862ca077db ci: Only build relevant project 2024-12-12 23:17:09 +01:00
semantic-release-bot
138d43b34b chore: Release v5.4.0-dev.9 [skip ci]
# [5.4.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.8...v5.4.0-dev.9) (2024-12-12)

### Features

* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([8d06a4a](8d06a4a8ad))
2024-12-12 18:42:30 +00:00
ILoveOpenSourceApplications
8d06a4a8ad feat(YouTube - Comments): Add Hide 'Chat summary' (#4110) 2024-12-12 22:39:08 +04:00
github-actions[bot]
d7ca7c1733 chore: Sync translations (#4105) 2024-12-12 19:01:48 +04:00
LisoUseInAIKyrios
8e0b7db82a refactor: Use raw string resources (#4109) 2024-12-12 17:01:16 +04:00
semantic-release-bot
b9d7867cee chore: Release v5.4.0-dev.8 [skip ci]
# [5.4.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.7...v5.4.0-dev.8) (2024-12-11)

### Bug Fixes

* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([11216cd](11216cd942))
2024-12-11 20:04:06 +00:00
LisoUseInAIKyrios
11216cd942 fix(YouTube Music - Bypass certificate checks): Add a recommended target version (#4104) 2024-12-11 21:00:32 +01:00
LisoUseInAIKyrios
b163e5f64d chore(YouTube): Simplify strings for translations 2024-12-11 17:54:17 +04:00
semantic-release-bot
5c2bbd0671 chore: Release v5.4.0-dev.7 [skip ci]
# [5.4.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.6...v5.4.0-dev.7) (2024-12-10)

### Bug Fixes

* **GmsCore support:** Adjust presentation of battery optimization dialog  ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([2062660](2062660d60))
2024-12-10 21:47:35 +00:00
LisoUseInAIKyrios
2062660d60 fix(GmsCore support): Adjust presentation of battery optimization dialog (#4091)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-12-11 01:43:57 +04:00
semantic-release-bot
2d9f08a08e chore: Release v5.4.0-dev.6 [skip ci]
# [5.4.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.5...v5.4.0-dev.6) (2024-12-10)

### Bug Fixes

* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([78c5118](78c51182f2))
2024-12-10 21:08:47 +00:00
LisoUseInAIKyrios
78c51182f2 fix(YouTube Music - Spoof video streams): Disable stable volume (#4097) 2024-12-11 01:04:48 +04:00
semantic-release-bot
feac2ab439 chore: Release v5.4.0-dev.5 [skip ci]
# [5.4.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.4...v5.4.0-dev.5) (2024-12-10)

### Bug Fixes

* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([32be03c](32be03c28d))
2024-12-10 20:46:59 +00:00
LisoUseInAIKyrios
32be03c28d fix(YouTube - Spoof video streams): Resolve playback of age restricted videos (#4096) 2024-12-11 00:42:56 +04:00
github-actions[bot]
6a345eee37 chore: Sync translations (#4093) 2024-12-10 21:26:57 +04:00
LisoUseInAIKyrios
61be7731e3 refactor(YouTube - Spoof streaming data): Add more debug logging 2024-12-10 21:12:38 +04:00
264 changed files with 28675 additions and 11118 deletions

View File

@@ -28,4 +28,10 @@ jobs:
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew build --no-daemon
run: ./gradlew :patches:buildAndroid --no-daemon
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: revanced-patches
path: patches/build/libs

View File

@@ -1,6 +1,8 @@
name: Pull strings
on:
schedule:
- cron: "0 */8 * * *"
workflow_dispatch:
jobs:
@@ -14,23 +16,28 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: dev
fetch-depth: 0
clean: true
- name: Pull strings
uses: crowdin/github-action@v2
with:
config: crowdin.yml
upload_sources: false
download_translations: true
localization_branch_name: feat/translations
create_pull_request: true
pull_request_title: "chore: Sync translations"
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
pull_request_base_branch_name: "dev"
commit_message: "chore: Sync translations"
github_user_name: revanced-bot
github_user_email: github@revanced.app
create_pull_request: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: Open pull request
if: github.event_name == 'workflow_dispatch'
uses: repo-sync/pull-request@v2
with:
source_branch: feat/translations
destination_branch: dev
pr_title: "chore: Sync translations"
pr_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"

View File

@@ -35,7 +35,7 @@ jobs:
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew build clean
run: ./gradlew :patches:buildAndroid clean
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -1,3 +1,556 @@
# [5.10.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.5...v5.10.0-dev.6) (2025-01-29)
### Bug Fixes
* **YouTube - Hide Ads:** Hide end screen store banner without leaving empty space ([#4367](https://github.com/ReVanced/revanced-patches/issues/4367)) ([7e68390](https://github.com/ReVanced/revanced-patches/commit/7e683906418434dd4e2104337d73a2292415c615))
# [5.10.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.4...v5.10.0-dev.5) (2025-01-27)
### Bug Fixes
* **YouTube - Hide video description components:** Use correct string key names ([0f28c2b](https://github.com/ReVanced/revanced-patches/commit/0f28c2b44c0051ea7ab3136433b84c73321cf5bd))
# [5.10.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.3...v5.10.0-dev.4) (2025-01-27)
### Features
* **YouTube - Hide video description components:** Add `Hide How this content was made section` ([#4355](https://github.com/ReVanced/revanced-patches/issues/4355)) ([68ec54e](https://github.com/ReVanced/revanced-patches/commit/68ec54ef850ae8d6461dd0ef2846e6efbb59e482))
# [5.10.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.2...v5.10.0-dev.3) (2025-01-27)
### Features
* **YouTube - Hide ads:** Add `Hide end screen store banner` ([#4351](https://github.com/ReVanced/revanced-patches/issues/4351)) ([5505087](https://github.com/ReVanced/revanced-patches/commit/55050878028fed82b0f583a9f7ba06b8f267f8ec))
# [5.10.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.1...v5.10.0-dev.2) (2025-01-25)
### Features
* **YouTube:** Add patch `Disable HDR video` ([#4347](https://github.com/ReVanced/revanced-patches/issues/4347)) ([0528f7c](https://github.com/ReVanced/revanced-patches/commit/0528f7cad856a2b1347e41944167b0583fc4a3d9))
# [5.10.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.4...v5.10.0-dev.1) (2025-01-23)
### Features
* **YouTube - Theme:** Add option to use custom seekbar accent color ([#4337](https://github.com/ReVanced/revanced-patches/issues/4337)) ([952b4fc](https://github.com/ReVanced/revanced-patches/commit/952b4fc4c9291e1a3e71437b503857763c973dd4))
## [5.9.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.3...v5.9.1-dev.4) (2025-01-22)
### Bug Fixes
* **YouTube - Hide layout components:** Hide new kind of community post ([#4341](https://github.com/ReVanced/revanced-patches/issues/4341)) ([02685c4](https://github.com/ReVanced/revanced-patches/commit/02685c4567aca55f22d45dc238a7d1f0ea264143))
## [5.9.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.2...v5.9.1-dev.3) (2025-01-22)
### Bug Fixes
* **YouTube - Hide seekbar:** Do not hide player seekbar if hide feed seekbar is enabled ([#4333](https://github.com/ReVanced/revanced-patches/issues/4333)) ([f5cf6f2](https://github.com/ReVanced/revanced-patches/commit/f5cf6f2a445492d33815a9772f49deac2d70eba9))
## [5.9.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.1...v5.9.1-dev.2) (2025-01-22)
### Bug Fixes
* **YouTube - Theme:** Fix 19.25 - 19.45 patch error ([5b47a5f](https://github.com/ReVanced/revanced-patches/commit/5b47a5f0f6299daaae209341064fd85f16ca18a6))
## [5.9.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.9.0...v5.9.1-dev.1) (2025-01-21)
### Bug Fixes
* **YouTube - Theme:** Replace custom seekbar gradient colors instead of disabling ([#4329](https://github.com/ReVanced/revanced-patches/issues/4329)) ([f03da98](https://github.com/ReVanced/revanced-patches/commit/f03da983051021e0c372557a5354d5d967409564))
# [5.9.0](https://github.com/ReVanced/revanced-patches/compare/v5.8.1...v5.9.0) (2025-01-20)
### Bug Fixes
* **YouTube - Spoof video streams:** Resolve playback issues after changing from cellular to wifi ([#4277](https://github.com/ReVanced/revanced-patches/issues/4277)) ([e93e1c8](https://github.com/ReVanced/revanced-patches/commit/e93e1c8ec3367e941034e9c4e3725ec1db429a60))
* **YouTube - Spoof video streams:** Update client user-agent ([#4304](https://github.com/ReVanced/revanced-patches/issues/4304)) ([7917871](https://github.com/ReVanced/revanced-patches/commit/7917871f510b6b805370ef98a0cf8a4e2df0e900))
### Features
* **YouTube - Hide feed components:** Handle new type of surveys ([#4295](https://github.com/ReVanced/revanced-patches/issues/4295)) ([c770e03](https://github.com/ReVanced/revanced-patches/commit/c770e03f3801367cb531af860fbdfa43dca89af0))
* **YouTube - Playback speed:** Add option to change 2x tap and hold speed ([#4307](https://github.com/ReVanced/revanced-patches/issues/4307)) ([02fb26e](https://github.com/ReVanced/revanced-patches/commit/02fb26e9458fb8635d497e6e78f964055244d738))
* **YouTube - Settings:** Add option to use new Cairo settings menus ([#4305](https://github.com/ReVanced/revanced-patches/issues/4305)) ([7b8a2a2](https://github.com/ReVanced/revanced-patches/commit/7b8a2a2721ab5351f8c0251401aceddf0c5327df))
# [5.9.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.3...v5.9.0-dev.4) (2025-01-20)
### Bug Fixes
* **YouTube - Spoof video streams:** Update client user-agent ([#4304](https://github.com/ReVanced/revanced-patches/issues/4304)) ([7917871](https://github.com/ReVanced/revanced-patches/commit/7917871f510b6b805370ef98a0cf8a4e2df0e900))
# [5.9.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.2...v5.9.0-dev.3) (2025-01-19)
### Features
* **YouTube - Settings:** Add option to use new Cairo settings menus ([#4305](https://github.com/ReVanced/revanced-patches/issues/4305)) ([7b8a2a2](https://github.com/ReVanced/revanced-patches/commit/7b8a2a2721ab5351f8c0251401aceddf0c5327df))
# [5.9.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.1...v5.9.0-dev.2) (2025-01-18)
### Features
* **YouTube - Playback speed:** Add option to change 2x tap and hold speed ([#4307](https://github.com/ReVanced/revanced-patches/issues/4307)) ([02fb26e](https://github.com/ReVanced/revanced-patches/commit/02fb26e9458fb8635d497e6e78f964055244d738))
# [5.9.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.2-dev.1...v5.9.0-dev.1) (2025-01-17)
### Features
* **YouTube - Hide feed components:** Handle new type of surveys ([#4295](https://github.com/ReVanced/revanced-patches/issues/4295)) ([c770e03](https://github.com/ReVanced/revanced-patches/commit/c770e03f3801367cb531af860fbdfa43dca89af0))
## [5.8.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.1...v5.8.2-dev.1) (2025-01-09)
### Bug Fixes
* **YouTube - Spoof video streams:** Resolve playback issues after changing from cellular to wifi ([#4277](https://github.com/ReVanced/revanced-patches/issues/4277)) ([e93e1c8](https://github.com/ReVanced/revanced-patches/commit/e93e1c8ec3367e941034e9c4e3725ec1db429a60))
## [5.8.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.0...v5.8.1) (2025-01-07)
### Bug Fixes
* **YouTube - Spoof video streams:** Add 'Android Creator' ([#4262](https://github.com/ReVanced/revanced-patches/issues/4262)) ([0479dd2](https://github.com/ReVanced/revanced-patches/commit/0479dd265e09b0accdf6ff6b00c8e938dc5b96c7))
## [5.8.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.0...v5.8.1-dev.1) (2025-01-06)
### Bug Fixes
* **YouTube - Spoof video streams:** Add 'Android Creator' ([#4262](https://github.com/ReVanced/revanced-patches/issues/4262)) ([0479dd2](https://github.com/ReVanced/revanced-patches/commit/0479dd265e09b0accdf6ff6b00c8e938dc5b96c7))
# [5.8.0](https://github.com/ReVanced/revanced-patches/compare/v5.7.2...v5.8.0) (2024-12-30)
### Bug Fixes
* **GmsCore support:** Do not show battery optimization error on Android Automotive devices (Google built-in) ([#4218](https://github.com/ReVanced/revanced-patches/issues/4218)) ([d6e389c](https://github.com/ReVanced/revanced-patches/commit/d6e389cc43bc40724f032b230f70048276349a19))
* **YouTube - Exit fullscreen mode:** Exit fullscreen mode of first video opened after cold start ([be5cf2e](https://github.com/ReVanced/revanced-patches/commit/be5cf2e834d87d51b5d3061d46bd7154d6306787))
* **YouTube - Force original audio:** If stream spoofing to Android then show a summary text why force audio is not available ([#4220](https://github.com/ReVanced/revanced-patches/issues/4220)) ([029aee8](https://github.com/ReVanced/revanced-patches/commit/029aee8023f096413fc80a2c583b4fe55ecb10ac))
* **YouTube - Spoof video streams:** Ignore harmless error toast if hide ads is disabled ([c3423bb](https://github.com/ReVanced/revanced-patches/commit/c3423bb9e531cfa52f6d28e0b98bbe8ab8684c30))
### Features
* **Swipe controls:** Add option to enable/disable fullscreen swipe to next video ([#4222](https://github.com/ReVanced/revanced-patches/issues/4222)) ([119092f](https://github.com/ReVanced/revanced-patches/commit/119092fafa4129849246df15fe8076ed3b491b85))
* **YouTube - Hide Shorts components:** Add option to hide Shorts in watch history ([#4214](https://github.com/ReVanced/revanced-patches/issues/4214)) ([19c2742](https://github.com/ReVanced/revanced-patches/commit/19c2742aa367367c77bb50ddad6f8a20fef8ea0a))
* **YouTube - Spoof app version:** Add 'Restore old navigation and toolbar icons' ([f84e459](https://github.com/ReVanced/revanced-patches/commit/f84e459d3d54b3001586796ab4e114ebadf09043))
* **YouTube:** Add `Change form factor` patch ([#4217](https://github.com/ReVanced/revanced-patches/issues/4217)) ([644ac5b](https://github.com/ReVanced/revanced-patches/commit/644ac5baa68b209a32300149a2efa009b776f9a7))
* **YouTube:** Add `Exit fullscreen mode` patch ([#4223](https://github.com/ReVanced/revanced-patches/issues/4223)) ([bb5d03b](https://github.com/ReVanced/revanced-patches/commit/bb5d03bd89a3f932c77e4e9de90174c374933688))
* **YouTube:** Add in app option to select a preferred language for ReVanced specific text ([#4231](https://github.com/ReVanced/revanced-patches/issues/4231)) ([3932af3](https://github.com/ReVanced/revanced-patches/commit/3932af397ae89a0b30191cd870bd6cddb7a078db))
# [5.8.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.7...v5.8.0-dev.8) (2024-12-28)
### Features
* **YouTube:** Add in app option to select a preferred language for ReVanced specific text ([#4231](https://github.com/ReVanced/revanced-patches/issues/4231)) ([3932af3](https://github.com/ReVanced/revanced-patches/commit/3932af397ae89a0b30191cd870bd6cddb7a078db))
# [5.8.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.6...v5.8.0-dev.7) (2024-12-27)
### Bug Fixes
* **YouTube - Spoof video streams:** Ignore harmless error toast if hide ads is disabled ([c3423bb](https://github.com/ReVanced/revanced-patches/commit/c3423bb9e531cfa52f6d28e0b98bbe8ab8684c30))
# [5.8.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.5...v5.8.0-dev.6) (2024-12-27)
### Bug Fixes
* **YouTube - Exit fullscreen mode:** Exit fullscreen mode of first video opened after cold start ([be5cf2e](https://github.com/ReVanced/revanced-patches/commit/be5cf2e834d87d51b5d3061d46bd7154d6306787))
# [5.8.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.4...v5.8.0-dev.5) (2024-12-27)
### Features
* **YouTube:** Add `Change form factor` patch ([#4217](https://github.com/ReVanced/revanced-patches/issues/4217)) ([644ac5b](https://github.com/ReVanced/revanced-patches/commit/644ac5baa68b209a32300149a2efa009b776f9a7))
# [5.8.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.3...v5.8.0-dev.4) (2024-12-27)
### Bug Fixes
* **GmsCore support:** Do not show battery optimization error on Android Automotive devices (Google built-in) ([#4218](https://github.com/ReVanced/revanced-patches/issues/4218)) ([d6e389c](https://github.com/ReVanced/revanced-patches/commit/d6e389cc43bc40724f032b230f70048276349a19))
### Features
* **Swipe controls:** Add option to enable/disable fullscreen swipe to next video ([#4222](https://github.com/ReVanced/revanced-patches/issues/4222)) ([119092f](https://github.com/ReVanced/revanced-patches/commit/119092fafa4129849246df15fe8076ed3b491b85))
* **YouTube:** Add `Exit fullscreen mode` patch ([#4223](https://github.com/ReVanced/revanced-patches/issues/4223)) ([bb5d03b](https://github.com/ReVanced/revanced-patches/commit/bb5d03bd89a3f932c77e4e9de90174c374933688))
# [5.8.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.2...v5.8.0-dev.3) (2024-12-26)
### Bug Fixes
* **YouTube - Force original audio:** If stream spoofing to Android then show a summary text why force audio is not available ([#4220](https://github.com/ReVanced/revanced-patches/issues/4220)) ([029aee8](https://github.com/ReVanced/revanced-patches/commit/029aee8023f096413fc80a2c583b4fe55ecb10ac))
# [5.8.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.1...v5.8.0-dev.2) (2024-12-24)
### Features
* **YouTube - Spoof app version:** Add 'Restore old navigation and toolbar icons' ([f84e459](https://github.com/ReVanced/revanced-patches/commit/f84e459d3d54b3001586796ab4e114ebadf09043))
# [5.8.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.2...v5.8.0-dev.1) (2024-12-24)
### Features
* **YouTube - Hide Shorts components:** Add option to hide Shorts in watch history ([#4214](https://github.com/ReVanced/revanced-patches/issues/4214)) ([19c2742](https://github.com/ReVanced/revanced-patches/commit/19c2742aa367367c77bb50ddad6f8a20fef8ea0a))
## [5.7.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1...v5.7.2) (2024-12-24)
### Bug Fixes
* **YouTube - Hide layout components:** Don't hide Shorts channel bar when toggling for video player ([9af6412](https://github.com/ReVanced/revanced-patches/commit/9af6412d92ec31e612eaabba6578453da0fc61d6))
* **YouTube - Spoof video streams:** Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds ([#4202](https://github.com/ReVanced/revanced-patches/issues/4202)) ([ab29f80](https://github.com/ReVanced/revanced-patches/commit/ab29f808a9f55b5ab0055533c1a6de549b0631a6))
## [5.7.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.2-dev.1...v5.7.2-dev.2) (2024-12-23)
### Bug Fixes
* **YouTube - Hide layout components:** Don't hide Shorts channel bar when toggling for video player ([9af6412](https://github.com/ReVanced/revanced-patches/commit/9af6412d92ec31e612eaabba6578453da0fc61d6))
## [5.7.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.1...v5.7.2-dev.1) (2024-12-23)
### Bug Fixes
* **YouTube - Spoof video streams:** Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds ([#4202](https://github.com/ReVanced/revanced-patches/issues/4202)) ([ab29f80](https://github.com/ReVanced/revanced-patches/commit/ab29f808a9f55b5ab0055533c1a6de549b0631a6))
## [5.7.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.0...v5.7.1) (2024-12-23)
### Bug Fixes
* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([134b189](https://github.com/ReVanced/revanced-patches/commit/134b189791113dcf1a1cb7c87b8a0954f432730c))
* **YouTube - Spoof video streams:** Use 2 letter device language code ([33ff997](https://github.com/ReVanced/revanced-patches/commit/33ff9972000581aca92262f984efb114eeeb9537))
* **YouTube - Spoof video streams:** Use Android VR authentication if using default audio language ([#4191](https://github.com/ReVanced/revanced-patches/issues/4191)) ([98773cc](https://github.com/ReVanced/revanced-patches/commit/98773cc7d46e5c9c7715b82c8006f1ccbcc5443c))
* **YouTube - Theme:** Use dark theme color for status and navigation bar ([0240efe](https://github.com/ReVanced/revanced-patches/commit/0240efe33e5444625ca2b760c861c9046d3dc836))
* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([dda788c](https://github.com/ReVanced/revanced-patches/commit/dda788c58c789d4f91646ea8e8a8077f590ab6b3))
## [5.7.1-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.4...v5.7.1-dev.5) (2024-12-22)
### Bug Fixes
* **YouTube - Spoof video streams:** Use 2 letter device language code ([33ff997](https://github.com/ReVanced/revanced-patches/commit/33ff9972000581aca92262f984efb114eeeb9537))
## [5.7.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.3...v5.7.1-dev.4) (2024-12-22)
### Bug Fixes
* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([dda788c](https://github.com/ReVanced/revanced-patches/commit/dda788c58c789d4f91646ea8e8a8077f590ab6b3))
## [5.7.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.2...v5.7.1-dev.3) (2024-12-22)
### Bug Fixes
* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([134b189](https://github.com/ReVanced/revanced-patches/commit/134b189791113dcf1a1cb7c87b8a0954f432730c))
## [5.7.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.1...v5.7.1-dev.2) (2024-12-22)
### Bug Fixes
* **YouTube - Theme:** Use dark theme color for status and navigation bar ([0240efe](https://github.com/ReVanced/revanced-patches/commit/0240efe33e5444625ca2b760c861c9046d3dc836))
## [5.7.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.0...v5.7.1-dev.1) (2024-12-22)
### Bug Fixes
* **YouTube - Spoof video streams:** Use Android VR authentication if using default audio language ([#4191](https://github.com/ReVanced/revanced-patches/issues/4191)) ([98773cc](https://github.com/ReVanced/revanced-patches/commit/98773cc7d46e5c9c7715b82c8006f1ccbcc5443c))
# [5.7.0](https://github.com/ReVanced/revanced-patches/compare/v5.6.0...v5.7.0) (2024-12-22)
### Bug Fixes
* **YouTube - Force original audio:** Use correct availability for settings UI ([a7eedcb](https://github.com/ReVanced/revanced-patches/commit/a7eedcb4cca6b7b12629c478c24c0899c80e3615))
* **YouTube - Spoof video stream:** Remove UI client type setting. Allow setting default audio language. ([#4184](https://github.com/ReVanced/revanced-patches/issues/4184)) ([99f3f29](https://github.com/ReVanced/revanced-patches/commit/99f3f29c649bf7693c05bbce2bb49bd53e05f050))
* **YouTube - Spoof video streams:** Remove iOS, add clients Android TV and Android Creator ([#4180](https://github.com/ReVanced/revanced-patches/issues/4180)) ([86abfb2](https://github.com/ReVanced/revanced-patches/commit/86abfb2b0d4675f0a1cb9ab244783075bfe89281))
* **YouTube:** Change fingerprints to support a wider range of target versions ([8a09174](https://github.com/ReVanced/revanced-patches/commit/8a09174def205a26ce49cb7815097e235069526a))
### Features
* **YouTube:** Support version `19.47.53` ([#4182](https://github.com/ReVanced/revanced-patches/issues/4182)) ([2089e61](https://github.com/ReVanced/revanced-patches/commit/2089e613d36c45352db7d852aaee0087b1c3e1a4))
# [5.7.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.4...v5.7.0-dev.1) (2024-12-21)
### Features
* **YouTube:** Support version `19.47.53` ([#4182](https://github.com/ReVanced/revanced-patches/issues/4182)) ([2089e61](https://github.com/ReVanced/revanced-patches/commit/2089e613d36c45352db7d852aaee0087b1c3e1a4))
## [5.6.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.3...v5.6.1-dev.4) (2024-12-21)
### Bug Fixes
* **YouTube - Spoof video stream:** Remove UI client type setting. Allow setting default audio language. ([#4184](https://github.com/ReVanced/revanced-patches/issues/4184)) ([99f3f29](https://github.com/ReVanced/revanced-patches/commit/99f3f29c649bf7693c05bbce2bb49bd53e05f050))
## [5.6.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.2...v5.6.1-dev.3) (2024-12-21)
### Bug Fixes
* **YouTube - Force original audio:** Use correct availability for settings UI ([a7eedcb](https://github.com/ReVanced/revanced-patches/commit/a7eedcb4cca6b7b12629c478c24c0899c80e3615))
## [5.6.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.1...v5.6.1-dev.2) (2024-12-21)
### Bug Fixes
* **YouTube - Spoof video streams:** Remove iOS, add clients Android TV and Android Creator ([#4180](https://github.com/ReVanced/revanced-patches/issues/4180)) ([86abfb2](https://github.com/ReVanced/revanced-patches/commit/86abfb2b0d4675f0a1cb9ab244783075bfe89281))
## [5.6.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.6.0...v5.6.1-dev.1) (2024-12-21)
### Bug Fixes
* **YouTube:** Change fingerprints to support a wider range of target versions ([8a09174](https://github.com/ReVanced/revanced-patches/commit/8a09174def205a26ce49cb7815097e235069526a))
# [5.6.0](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.6.0) (2024-12-20)
### Bug Fixes
* **Twitter - Change link sharing domain:** Use correct extension package ([ad7fab6](https://github.com/ReVanced/revanced-patches/commit/ad7fab67319ba23f267d27da9b74266965fc4be3))
* **YouTube - Force original audio:** Use correct original audio stream if app language is not English ([0d20171](https://github.com/ReVanced/revanced-patches/commit/0d2017133efac230887b5c2a331d87159df8af11))
* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([08f68cb](https://github.com/ReVanced/revanced-patches/commit/08f68cb5d33f2cfe656d2f93d159c69981f31418))
* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([2694158](https://github.com/ReVanced/revanced-patches/commit/2694158c3c9935ede21c96832533222f850068df))
* **YouTube - Open Shorts in regular player:** Do not show the miniplayer after opening a Short while a video is playing ([894e366](https://github.com/ReVanced/revanced-patches/commit/894e36665d17d5a3a5728961d424dffc55faa50b))
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([5870906](https://github.com/ReVanced/revanced-patches/commit/587090636dfff0b358b15026cf7d47c65a4296dc))
* **YouTube - Spoof video streams:** Change default spoofing to iOS, allow setting a default language with Android VR ([#4171](https://github.com/ReVanced/revanced-patches/issues/4171)) ([171b4e7](https://github.com/ReVanced/revanced-patches/commit/171b4e7e40066e38fba773b7a6525e9a038779ef))
* **YouTube - Spoof video streams:** Update iOS client version ([df3aeed](https://github.com/ReVanced/revanced-patches/commit/df3aeed3b173e408fad80197a89ec5d003a2b328))
### Features
* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([c7c5e5b](https://github.com/ReVanced/revanced-patches/commit/c7c5e5b2b9cf63d8225bb6bd5e735ddf945b6c29))
# [5.6.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.5...v5.6.0-dev.6) (2024-12-20)
### Bug Fixes
* **YouTube - Spoof video streams:** Update iOS client version ([df3aeed](https://github.com/ReVanced/revanced-patches/commit/df3aeed3b173e408fad80197a89ec5d003a2b328))
# [5.6.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.4...v5.6.0-dev.5) (2024-12-20)
### Bug Fixes
* **YouTube - Spoof video streams:** Change default spoofing to iOS, allow setting a default language with Android VR ([#4171](https://github.com/ReVanced/revanced-patches/issues/4171)) ([171b4e7](https://github.com/ReVanced/revanced-patches/commit/171b4e7e40066e38fba773b7a6525e9a038779ef))
# [5.6.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.3...v5.6.0-dev.4) (2024-12-20)
### Bug Fixes
* **YouTube - Force original audio:** Use correct original audio stream if app language is not English ([0d20171](https://github.com/ReVanced/revanced-patches/commit/0d2017133efac230887b5c2a331d87159df8af11))
# [5.6.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.2...v5.6.0-dev.3) (2024-12-20)
### Bug Fixes
* **Twitter - Change link sharing domain:** Use correct extension package ([ad7fab6](https://github.com/ReVanced/revanced-patches/commit/ad7fab67319ba23f267d27da9b74266965fc4be3))
# [5.6.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.1...v5.6.0-dev.2) (2024-12-19)
### Bug Fixes
* **YouTube - Open Shorts in regular player:** Do not show the miniplayer after opening a Short while a video is playing ([894e366](https://github.com/ReVanced/revanced-patches/commit/894e36665d17d5a3a5728961d424dffc55faa50b))
# [5.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.2...v5.6.0-dev.1) (2024-12-19)
### Features
* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([c7c5e5b](https://github.com/ReVanced/revanced-patches/commit/c7c5e5b2b9cf63d8225bb6bd5e735ddf945b6c29))
## [5.5.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.1...v5.5.2-dev.2) (2024-12-17)
### Bug Fixes
* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([08f68cb](https://github.com/ReVanced/revanced-patches/commit/08f68cb5d33f2cfe656d2f93d159c69981f31418))
## [5.5.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.5.2-dev.1) (2024-12-17)
### Bug Fixes
* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([2694158](https://github.com/ReVanced/revanced-patches/commit/2694158c3c9935ede21c96832533222f850068df))
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([5870906](https://github.com/ReVanced/revanced-patches/commit/587090636dfff0b358b15026cf7d47c65a4296dc))
## [5.5.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1) (2024-12-16)
### Bug Fixes
* **YouTube:** Fix string translations ([52e04d3](https://github.com/ReVanced/revanced-patches/commit/52e04d340c1a85f3d683c67a15ae96529432d5fe))
## [5.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1-dev.1) (2024-12-16)
### Bug Fixes
* **YouTube:** Fix string translations ([52e04d3](https://github.com/ReVanced/revanced-patches/commit/52e04d340c1a85f3d683c67a15ae96529432d5fe))
# [5.5.0](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.5.0) (2024-12-16)
### Bug Fixes
* **Twitch:** Change recommended target to the latest app version ([fb32972](https://github.com/ReVanced/revanced-patches/commit/fb32972f4de92dac1fc5d73f56a392a671c4e94b))
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
### Features
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([a2d2141](https://github.com/ReVanced/revanced-patches/commit/a2d2141cec9b0b4929e07a8010889b21c324b229))
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([f4aa440](https://github.com/ReVanced/revanced-patches/commit/f4aa4406080b91f01d623e54b11b99ea849ddcdf))
# [5.5.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.4...v5.5.0-dev.5) (2024-12-16)
### Features
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([a2d2141](https://github.com/ReVanced/revanced-patches/commit/a2d2141cec9b0b4929e07a8010889b21c324b229))
# [5.5.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.3...v5.5.0-dev.4) (2024-12-16)
### Bug Fixes
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)
### Features
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)
### Bug Fixes
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)
### Features
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([f4aa440](https://github.com/ReVanced/revanced-patches/commit/f4aa4406080b91f01d623e54b11b99ea849ddcdf))
## [5.4.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.4.1-dev.1) (2024-12-14)
### Bug Fixes
* **Twitch:** Change recommended target to the latest app version ([fb32972](https://github.com/ReVanced/revanced-patches/commit/fb32972f4de92dac1fc5d73f56a392a671c4e94b))
# [5.4.0](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0) (2024-12-14)
### Bug Fixes
* **GmsCore support:** Adjust presentation of battery optimization dialog ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([5d8fc1b](https://github.com/ReVanced/revanced-patches/commit/5d8fc1bcd4e453298cfac086cdbdf279612bfb63))
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([6bd22ff](https://github.com/ReVanced/revanced-patches/commit/6bd22ffa7e8af4d8f5d2d3b1711bd92c44b4e4aa))
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([f4659a3](https://github.com/ReVanced/revanced-patches/commit/f4659a328eaf600e1e5f02a66fa2af4b6d8dc7c1))
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([13c7592](https://github.com/ReVanced/revanced-patches/commit/13c7592b21defd27e3a7aa9b219ffc0247bb5914))
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([4c46cb2](https://github.com/ReVanced/revanced-patches/commit/4c46cb27a02c6f29626cd769b6a8e825645d5b16))
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([839a404](https://github.com/ReVanced/revanced-patches/commit/839a4045f1bb1759d89047834e0b7695781e82a3))
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([17a5a6c](https://github.com/ReVanced/revanced-patches/commit/17a5a6c1691b0c23f601d3355b72f122c2bd5dcb))
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([16bb9df](https://github.com/ReVanced/revanced-patches/commit/16bb9dfc299612f3922724c136878606987ab132))
### Features
* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([cb22f65](https://github.com/ReVanced/revanced-patches/commit/cb22f652ed678d81ffda9ece659b3971225d6931))
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([75c740c](https://github.com/ReVanced/revanced-patches/commit/75c740c6ba2e0c62e567f7dc90cdad368fc4f372))
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([eecfbb7](https://github.com/ReVanced/revanced-patches/commit/eecfbb7122a9072e55e687f2c003f63108654888))
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([269493c](https://github.com/ReVanced/revanced-patches/commit/269493cd198604f1438ea2850fb68fe900d0e56f))
# [5.4.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.10...v5.4.0-dev.11) (2024-12-14)
### Features
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([eecfbb7](https://github.com/ReVanced/revanced-patches/commit/eecfbb7122a9072e55e687f2c003f63108654888))
# [5.4.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.9...v5.4.0-dev.10) (2024-12-13)
### Bug Fixes
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([13c7592](https://github.com/ReVanced/revanced-patches/commit/13c7592b21defd27e3a7aa9b219ffc0247bb5914))
# [5.4.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.8...v5.4.0-dev.9) (2024-12-12)
### Features
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([269493c](https://github.com/ReVanced/revanced-patches/commit/269493cd198604f1438ea2850fb68fe900d0e56f))
# [5.4.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.7...v5.4.0-dev.8) (2024-12-11)
### Bug Fixes
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([17a5a6c](https://github.com/ReVanced/revanced-patches/commit/17a5a6c1691b0c23f601d3355b72f122c2bd5dcb))
# [5.4.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.6...v5.4.0-dev.7) (2024-12-10)
### Bug Fixes
* **GmsCore support:** Adjust presentation of battery optimization dialog ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([5d8fc1b](https://github.com/ReVanced/revanced-patches/commit/5d8fc1bcd4e453298cfac086cdbdf279612bfb63))
# [5.4.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.5...v5.4.0-dev.6) (2024-12-10)
### Bug Fixes
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([16bb9df](https://github.com/ReVanced/revanced-patches/commit/16bb9dfc299612f3922724c136878606987ab132))
# [5.4.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.4...v5.4.0-dev.5) (2024-12-10)
### Bug Fixes
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([839a404](https://github.com/ReVanced/revanced-patches/commit/839a4045f1bb1759d89047834e0b7695781e82a3))
# [5.4.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.3...v5.4.0-dev.4) (2024-12-10)

View File

@@ -0,0 +1 @@
// Do not remove. Necessary for the extension plugin to be applied to the project.

View File

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

View File

@@ -0,0 +1,27 @@
package app.revanced.extension.music.spoof;
/**
* @noinspection unused
*/
public class SpoofClientPatch {
private static final int CLIENT_TYPE_ID = 26;
private static final String CLIENT_VERSION = "6.21";
private static final String DEVICE_MODEL = "iPhone16,2";
private static final String OS_VERSION = "17.7.2.21H221";
public static int getClientId() {
return CLIENT_TYPE_ID;
}
public static String getClientVersion() {
return CLIENT_VERSION;
}
public static String getClientModel() {
return DEVICE_MODEL;
}
public static String getOsVersion() {
return OS_VERSION;
}
}

View File

@@ -54,17 +54,20 @@ public class GmsCoreSupport {
private static void showBatteryOptimizationDialog(Activity context,
String dialogMessageRef,
String positiveButtonStringRef,
String positiveButtonTextRef,
DialogInterface.OnClickListener onPositiveClickListener) {
// Do not set cancelable to false, to allow using back button to skip the action,
// just in case the check can never be satisfied.
var dialog = new AlertDialog.Builder(context)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(str("gms_core_dialog_title"))
.setMessage(str(dialogMessageRef))
.setPositiveButton(str(positiveButtonStringRef), onPositiveClickListener)
.create();
Utils.showDialog(context, dialog);
// Use a delay to allow the activity to finish initializing.
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
Utils.runOnMainThreadDelayed(() -> {
// Do not set cancelable to false, to allow using back button to skip the action,
// just in case the battery change can never be satisfied.
var dialog = new AlertDialog.Builder(context)
.setTitle(str("gms_core_dialog_title"))
.setMessage(str(dialogMessageRef))
.setPositiveButton(str(positiveButtonTextRef), onPositiveClickListener)
.create();
Utils.showDialog(context, dialog);
}, 100);
}
/**
@@ -102,7 +105,22 @@ public class GmsCoreSupport {
return;
}
// Check if GmsCore is running in the background.
// Check if GmsCore is whitelisted from battery optimizations.
if (isAndroidAutomotive(context)) {
// Ignore Android Automotive devices (Google built-in),
// as there is no way to disable battery optimizations.
Logger.printDebug(() -> "Device is Android Automotive");
} else if (batteryOptimizationsEnabled(context)) {
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
showBatteryOptimizationDialog(context,
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
"gms_core_dialog_continue_text",
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
return;
}
// Check if GmsCore is currently running in the background.
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
if (client == null) {
Logger.printInfo(() -> "GmsCore is not running in the background");
@@ -111,18 +129,8 @@ public class GmsCoreSupport {
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
"gms_core_dialog_open_website_text",
(dialog, id) -> open(DONT_KILL_MY_APP_LINK));
return;
}
}
// Check if GmsCore is whitelisted from battery optimizations.
if (batteryOptimizationsEnabled(context)) {
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
showBatteryOptimizationDialog(context,
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
"gms_core_dialog_continue_text",
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
}
} catch (Exception ex) {
Logger.printException(() -> "checkGmsCore failure", ex);
}
@@ -143,6 +151,10 @@ public class GmsCoreSupport {
return !powerManager.isIgnoringBatteryOptimizations(GMS_CORE_PACKAGE_NAME);
}
private static boolean isAndroidAutomotive(Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
private static String getGmsCoreDownload() {
final var vendorGroupId = getGmsCoreVendorGroupId();
//noinspection SwitchStatementWithTooFewBranches

View File

@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.app.*;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
@@ -39,15 +40,18 @@ import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
public class Utils {
@SuppressLint("StaticFieldLeak")
private static Context context;
private static volatile Context context;
private static String versionName;
private static String applicationLabel;
private Utils() {
} // utility class
@@ -62,28 +66,30 @@ public class Utils {
return ""; // Value is replaced during patching.
}
private static PackageInfo getPackageInfo() throws PackageManager.NameNotFoundException {
final var packageName = Objects.requireNonNull(getContext()).getPackageName();
PackageManager packageManager = context.getPackageManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return packageManager.getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(0)
);
}
return packageManager.getPackageInfo(
packageName,
0
);
}
/**
* @return The version name of the app, such as 19.11.43
*/
public static String getAppVersionName() {
if (versionName == null) {
try {
final var packageName = Objects.requireNonNull(getContext()).getPackageName();
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageInfo = packageManager.getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(0)
);
} else {
packageInfo = packageManager.getPackageInfo(
packageName,
0
);
}
versionName = packageInfo.versionName;
versionName = getPackageInfo().versionName;
} catch (Exception ex) {
Logger.printException(() -> "Failed to get package info", ex);
versionName = "Unknown";
@@ -93,6 +99,19 @@ public class Utils {
return versionName;
}
public static String getApplicationName() {
if (applicationLabel == null) {
try {
ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
applicationLabel = (String) applicationInfo.loadLabel(context.getPackageManager());
} catch (Exception ex) {
Logger.printException(() -> "Failed to get application name", ex);
applicationLabel = "Unknown";
}
}
return applicationLabel;
}
/**
* Hide a view by setting its layout height and width to 1dp.
@@ -326,7 +345,7 @@ public class Utils {
public static void restartApp(@NonNull Context context) {
String packageName = context.getPackageName();
Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
Intent intent = Objects.requireNonNull(context.getPackageManager().getLaunchIntentForPackage(packageName));
Intent mainIntent = Intent.makeRestartActivityTask(intent.getComponent());
// Required for API 34 and later
// Ref: https://developer.android.com/about/versions/14/behavior-changes-14#safer-intents
@@ -343,7 +362,17 @@ public class Utils {
}
public static void setContext(Context appContext) {
// Must initially set context as the language settings needs it.
context = appContext;
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language != AppLanguage.DEFAULT) {
// Create a new context with the desired language.
Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale());
context = appContext.createConfigurationContext(config);
}
// In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies.
// Calling the regular printDebug method here can cause a Settings context null pointer exception,
// even though the context is already set before the call.
@@ -506,6 +535,11 @@ public class Utils {
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
}
public static boolean isLandscapeOrientation() {
final int orientation = context.getResources().getConfiguration().orientation;
return orientation == Configuration.ORIENTATION_LANDSCAPE;
}
/**
* Automatically logs any exceptions the runnable throws.
*
@@ -578,7 +612,7 @@ public class Utils {
|| networkType == NetworkType.OTHER;
}
@SuppressLint("MissingPermission") // permission already included in YouTube
@SuppressLint({"MissingPermission", "deprecation"}) // Permission already included in YouTube.
public static NetworkType getNetworkType() {
Context networkContext = getContext();
if (networkContext == null) {
@@ -688,8 +722,8 @@ public class Utils {
Preference preference = group.getPreference(i);
final Sort preferenceSort;
if (preference instanceof PreferenceGroup) {
sortPreferenceGroups((PreferenceGroup) preference);
if (preference instanceof PreferenceGroup subGroup) {
sortPreferenceGroups(subGroup);
preferenceSort = groupSort; // Sort value for groups is for it's content, not itself.
} else {
// Allow individual preferences to set a key sorting.
@@ -743,8 +777,8 @@ public class Utils {
return;
}
String deviceLanguage = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
if (deviceLanguage.equals("en")) {
String revancedLocale = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
if (revancedLocale.equals(Locale.ENGLISH.getLanguage())) {
return;
}
@@ -752,8 +786,8 @@ public class Utils {
Preference pref = group.getPreference(i);
pref.setSingleLineTitle(false);
if (pref instanceof PreferenceGroup) {
setPreferenceTitlesToMultiLineIfNeeded((PreferenceGroup) pref);
if (pref instanceof PreferenceGroup subGroup) {
setPreferenceTitlesToMultiLineIfNeeded(subGroup);
}
}
}

View File

@@ -1,12 +1,15 @@
package app.revanced.extension.shared.spoof;
package app.revanced.extension.shared.settings;
import java.util.Locale;
public enum AudioStreamLanguage {
public enum AppLanguage {
/**
* The current app language.
*/
DEFAULT,
// Language codes found in locale_config.xml
// Region specific variants of Chinese/English/Spanish/French have been removed.
// All region specific variants have been removed.
AF,
AM,
AR,
@@ -31,7 +34,7 @@ public enum AudioStreamLanguage {
GL,
GU,
HI,
HE, // App uses obsolete 'IW' and 'HE' is modern ISO code.
HE, // App uses obsolete 'IW' and not the modern 'HE' ISO code.
HR,
HU,
HY,
@@ -60,8 +63,7 @@ public enum AudioStreamLanguage {
OR,
PA,
PL,
PT_BR,
PT_PT,
PT,
RO,
RU,
SI,
@@ -83,20 +85,30 @@ public enum AudioStreamLanguage {
ZH,
ZU;
private final String iso639_1;
private final String language;
AudioStreamLanguage() {
iso639_1 = name().replace('_', '-');
AppLanguage() {
language = name().toLowerCase(Locale.US);
}
public String getIso639_1() {
/**
* @return The 2 letter ISO 639_1 language code.
*/
public String getLanguage() {
// Changing the app language does not force the app to completely restart,
// so the default needs to be the current language and not a static field.
if (this == DEFAULT) {
// Android VR requires uppercase language code.
return Locale.getDefault().toLanguageTag().toUpperCase(Locale.US);
return Locale.getDefault().getLanguage();
}
return iso639_1;
return language;
}
public Locale getLocale() {
if (this == DEFAULT) {
return Locale.getDefault();
}
return Locale.forLanguageTag(language);
}
}

View File

@@ -3,9 +3,9 @@ package app.revanced.extension.shared.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.ForceiOSAVCAvailability;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
import app.revanced.extension.shared.spoof.ClientType;
/**
@@ -21,10 +21,14 @@ public class BaseSettings {
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
public static final EnumSetting<AppLanguage> REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true, "revanced_language_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<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, parent(SPOOF_VIDEO_STREAMS));
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_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new ForceiOSAVCAvailability());
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
// Client type must be last spoof setting due to cyclic references.
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
}

View File

@@ -7,7 +7,6 @@ import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.StringRef;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
import org.jetbrains.annotations.NotNull;
import org.json.JSONException;
import org.json.JSONObject;
@@ -154,7 +153,6 @@ public abstract class Setting<T> {
/**
* Confirmation message to display, if the user tries to change the setting from the default value.
* Currently this works only for Boolean setting types.
*/
@Nullable
public final StringRef userDialogMessage;
@@ -245,6 +243,7 @@ public abstract class Setting<T> {
*
* This method will be deleted in the future.
*/
@SuppressWarnings("rawtypes")
public static void migrateFromOldPreferences(@NonNull SharedPrefCategory oldPrefs, @NonNull Setting setting, String settingKey) {
if (!oldPrefs.preferences.contains(settingKey)) {
return; // Nothing to do.
@@ -330,7 +329,7 @@ public abstract class Setting<T> {
return value.equals(defaultValue);
}
@NotNull
@NonNull
@Override
public String toString() {
return key + "=" + get();
@@ -420,6 +419,7 @@ public abstract class Setting<T> {
boolean rebootSettingChanged = false;
int numberOfSettingsImported = 0;
//noinspection rawtypes
for (Setting setting : SETTINGS) {
String key = setting.getImportExportKey();
if (json.has(key)) {

View File

@@ -42,7 +42,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
try {
Setting<?> setting = Setting.getSettingFromPath(str);
Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str));
if (setting == null) {
return;
}
@@ -52,23 +52,21 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
}
Logger.printDebug(() -> "Preference changed: " + setting.key);
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
updatePreference(pref, setting, true, settingImportInProgress);
// Update any other preference availability that may now be different.
updateUIAvailability();
if (settingImportInProgress) {
return;
}
if (!showingUserDialogMessage) {
if (setting.userDialogMessage != null && ((SwitchPreference) pref).isChecked() != (Boolean) setting.defaultValue) {
showSettingUserDialogConfirmation((SwitchPreference) pref, (BooleanSetting) setting);
if (!settingImportInProgress && !showingUserDialogMessage) {
if (setting.userDialogMessage != null && !prefIsSetToDefault(pref, setting)) {
// Do not change the setting yet, to allow preserving whatever
// list/text value was previously set if it needs to be reverted.
showSettingUserDialogConfirmation(pref, setting);
return;
} else if (setting.rebootApp) {
showRestartDialog(getContext());
}
}
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
updatePreference(pref, setting, true, settingImportInProgress);
// Update any other preference availability that may now be different.
updateUIAvailability();
} catch (Exception ex) {
Logger.printException(() -> "OnSharedPreferenceChangeListener failure", ex);
}
@@ -92,7 +90,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
Utils.setPreferenceTitlesToMultiLineIfNeeded(screen);
}
private void showSettingUserDialogConfirmation(SwitchPreference switchPref, BooleanSetting setting) {
private void showSettingUserDialogConfirmation(Preference pref, Setting<?> setting) {
Utils.verifyOnMainThread();
final var context = getContext();
@@ -104,12 +102,19 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
.setTitle(confirmDialogTitle)
.setMessage(Objects.requireNonNull(setting.userDialogMessage).toString())
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
// User confirmed, save to the Setting.
updatePreference(pref, setting, true, false);
// Update availability of other preferences that may be changed.
updateUIAvailability();
if (setting.rebootApp) {
showRestartDialog(context);
}
})
.setNegativeButton(android.R.string.cancel, (dialog, id) -> {
switchPref.setChecked(setting.defaultValue); // Recursive call that resets the Setting value.
// Restore whatever the setting was before the change.
updatePreference(pref, setting, true, true);
})
.setOnDismissListener(dialog -> {
showingUserDialogMessage = false;
@@ -132,6 +137,24 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
updatePreferenceScreen(getPreferenceScreen(), false, false);
}
/**
* @return If the preference is currently set to the default value of the Setting.
*/
protected boolean prefIsSetToDefault(Preference pref, Setting<?> setting) {
if (pref instanceof SwitchPreference switchPref) {
return switchPref.isChecked() == (Boolean) setting.defaultValue;
}
if (pref instanceof EditTextPreference editPreference) {
return editPreference.getText().equals(setting.defaultValue.toString());
}
if (pref instanceof ListPreference listPref) {
return listPref.getValue().equals(setting.defaultValue.toString());
}
throw new IllegalStateException("Must override method to handle "
+ "preference type: " + pref.getClass());
}
/**
* Syncs all UI Preferences to any {@link Setting} they represent.
*/
@@ -170,23 +193,20 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
protected void syncSettingWithPreference(@NonNull Preference pref,
@NonNull Setting<?> setting,
boolean applySettingToPreference) {
if (pref instanceof SwitchPreference) {
SwitchPreference switchPref = (SwitchPreference) pref;
if (pref instanceof SwitchPreference switchPref) {
BooleanSetting boolSetting = (BooleanSetting) setting;
if (applySettingToPreference) {
switchPref.setChecked(boolSetting.get());
} else {
BooleanSetting.privateSetValue(boolSetting, switchPref.isChecked());
}
} else if (pref instanceof EditTextPreference) {
EditTextPreference editPreference = (EditTextPreference) pref;
} else if (pref instanceof EditTextPreference editPreference) {
if (applySettingToPreference) {
editPreference.setText(setting.get().toString());
} else {
Setting.privateSetValueFromString(setting, editPreference.getText());
}
} else if (pref instanceof ListPreference) {
ListPreference listPref = (ListPreference) pref;
} else if (pref instanceof ListPreference listPref) {
if (applySettingToPreference) {
listPref.setValue(setting.get().toString());
} else {

View File

@@ -4,40 +4,114 @@ import android.os.Build;
import androidx.annotation.Nullable;
import java.util.Locale;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
public enum ClientType {
// Specific purpose for age restricted, or private videos, because the iOS client is not logged in.
// https://dumps.tadiphone.dev/dumps/oculus/eureka
ANDROID_VR(28,
ANDROID_VR_NO_AUTH(
28,
"ANDROID_VR",
"com.google.android.apps.youtube.vr.oculus",
"Oculus",
"Quest 3",
"Android",
"12",
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
"32", // Android 12.1
"1.56.21",
true
// Android 12.1
"32",
"SQ3A.220605.009.A1",
"132.0.6808.3",
"1.61.48",
false,
false,
"Android VR No auth"
),
// Specific for kids videos.
IOS(5,
// Chromecast with Google TV 4K.
// https://dumps.tadiphone.dev/dumps/google/kirkwood
ANDROID_UNPLUGGED(
29,
"ANDROID_UNPLUGGED",
"com.google.android.apps.youtube.unplugged",
"Google",
"Google TV Streamer",
"Android",
"14",
"34",
"UTT3.240625.001.K5",
"132.0.6808.3",
"8.49.0",
true,
true,
"Android TV"
),
// Cannot play livestreams and lacks HDR, but can play videos with music and labeled "for children".
// Google Pixel 9 Pro Fold
// https://dumps.tadiphone.dev/dumps/google/barbet
ANDROID_CREATOR(
14,
"ANDROID_CREATOR",
"com.google.android.apps.youtube.creator",
"Google",
"Pixel 9 Pro Fold",
"Android",
"15",
"35",
"AP3A.241005.015.A2",
"132.0.6779.0",
"23.47.101",
true,
true,
"Android Creator"
),
ANDROID_VR(
ANDROID_VR_NO_AUTH.id,
ANDROID_VR_NO_AUTH.clientName,
ANDROID_VR_NO_AUTH.packageName,
ANDROID_VR_NO_AUTH.deviceMake,
ANDROID_VR_NO_AUTH.deviceModel,
ANDROID_VR_NO_AUTH.osName,
ANDROID_VR_NO_AUTH.osVersion,
ANDROID_VR_NO_AUTH.androidSdkVersion,
ANDROID_VR_NO_AUTH.buildId,
ANDROID_VR_NO_AUTH.cronetVersion,
ANDROID_VR_NO_AUTH.clientVersion,
ANDROID_VR_NO_AUTH.requiresAuth,
true,
"Android VR"
),
IOS_UNPLUGGED(
33,
"IOS_UNPLUGGED",
"com.google.ios.youtubeunplugged",
"Apple",
forceAVC()
? "iPhone12,5" // 11 Pro Max (last device with iOS 13)
: "iPhone16,2", // 15 Pro Max
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
// 11 Pro Max (last device with iOS 13)
? "iPhone12,5"
// 15 Pro Max
: "iPhone16,2",
"iOS",
forceAVC()
? "13.7.17H35" // Last release of iOS 13.
: "17.5.1.21F90",
forceAVC()
? "com.google.ios.youtube/17.40.5 (iPhone; U; CPU iOS 13_7 like Mac OS X)"
: "com.google.ios.youtube/19.47.7 (iPhone; U; CPU iOS 17_5_1 like Mac OS X)",
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
? "13.7.17H35"
: "18.2.22C152",
null,
null,
null,
// Version number should be a valid iOS release.
// https://www.ipa4fun.com/history/185230
// https://www.ipa4fun.com/history/152043/
forceAVC()
// Some newer versions can also force AVC,
// but 17.40 is the last version that supports iOS 13.
? "17.40.5"
: "19.47.7",
false
// but 6.45 is the last version that supports iOS 13.
? "6.45"
: "8.49",
true,
true,
forceAVC()
? "iOS TV Force AVC"
: "iOS TV"
);
private static boolean forceAVC() {
@@ -50,21 +124,38 @@ public enum ClientType {
*/
public final int id;
/**
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model)
*/
public final String deviceModel;
public final String clientName;
/**
* Device OS version.
* App package name.
*/
public final String osVersion;
private final String packageName;
/**
* Player user-agent.
*/
public final String userAgent;
/**
* Device model, equivalent to {@link Build#MANUFACTURER} (System property: ro.product.vendor.manufacturer)
*/
public final String deviceMake;
/**
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.vendor.model)
*/
public final String deviceModel;
/**
* Device OS name.
*/
public final String osName;
/**
* Device OS version.
*/
public final String osVersion;
/**
* Android SDK version, equivalent to {@link Build.VERSION#SDK} (System property: ro.build.version.sdk)
* Field is null if not applicable.
@@ -72,30 +163,97 @@ public enum ClientType {
@Nullable
public final String androidSdkVersion;
/**
* Android build id, equivalent to {@link Build#ID}.
* Field is null if not applicable.
*/
@Nullable
private final String buildId;
/**
* Cronet release version, as found in decompiled client apk.
* Field is null if not applicable.
*/
@Nullable
private final String cronetVersion;
/**
* App version.
*/
public final String clientVersion;
/**
* If the client can access the API logged in.
* If this client requires authentication and does not work
* if logged out or in incognito mode.
*/
public final boolean canLogin;
public final boolean requiresAuth;
/**
* If the client should use authentication if available.
*/
public final boolean useAuth;
/**
* Friendly name displayed in stats for nerds.
*/
public final String friendlyName;
@SuppressWarnings("ConstantLocale")
ClientType(int id,
String clientName,
String packageName,
String deviceMake,
String deviceModel,
String osName,
String osVersion,
String userAgent,
@Nullable String androidSdkVersion,
@Nullable String buildId,
@Nullable String cronetVersion,
String clientVersion,
boolean canLogin
) {
boolean requiresAuth,
boolean useAuth,
String friendlyName) {
this.id = id;
this.clientName = clientName;
this.packageName = packageName;
this.deviceMake = deviceMake;
this.deviceModel = deviceModel;
this.osName = osName;
this.osVersion = osVersion;
this.userAgent = userAgent;
this.androidSdkVersion = androidSdkVersion;
this.buildId = buildId;
this.cronetVersion = cronetVersion;
this.clientVersion = clientVersion;
this.canLogin = canLogin;
this.requiresAuth = requiresAuth;
this.useAuth = useAuth;
this.friendlyName = friendlyName;
Locale defaultLocale = Locale.getDefault();
if (androidSdkVersion == null) {
// Convert version from '18.2.22C152' into '18_2_22'
String userAgentOsVersion = osVersion
.replaceAll("(\\d+\\.\\d+\\.\\d+).*", "$1")
.replace(".", "_");
// https://github.com/mitmproxy/mitmproxy/issues/4836
this.userAgent = String.format("%s/%s (%s; U; CPU iOS %s like Mac OS X; %s)",
packageName,
clientVersion,
deviceModel,
userAgentOsVersion,
defaultLocale
);
} else {
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)",
packageName,
clientVersion,
osVersion,
defaultLocale,
deviceModel,
Objects.requireNonNull(buildId),
Objects.requireNonNull(cronetVersion)
);
}
Logger.printDebug(() -> "userAgent: " + this.userAgent);
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.extension.shared.spoof;
import android.net.Uri;
import android.text.TextUtils;
import androidx.annotation.Nullable;
@@ -16,12 +17,29 @@ import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
@SuppressWarnings("unused")
public class SpoofVideoStreamsPatch {
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_VIDEO_STREAMS.get();
private static final boolean FIX_HLS_CURRENT_TIME = SPOOF_STREAMING_DATA
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
/**
* Any unreachable ip address. Used to intentionally fail requests.
*/
private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
/**
* @return If this patch was included during patching.
*/
private static boolean isPatchIncluded() {
return false; // Modified during patching.
}
public static boolean notSpoofingToAndroid() {
return !isPatchIncluded()
|| !BaseSettings.SPOOF_VIDEO_STREAMS.get()
|| BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
}
/**
* Injection point.
* Blocks /get_watch requests by returning an unreachable URI.
@@ -59,9 +77,9 @@ public class SpoofVideoStreamsPatch {
String path = originalUri.getPath();
if (path != null && path.contains("initplayback")) {
Logger.printDebug(() -> "Blocking 'initplayback' by returning unreachable url");
Logger.printDebug(() -> "Blocking 'initplayback' by clearing query");
return UNREACHABLE_HOST_URI_STRING;
return originalUri.buildUpon().clearQuery().build().toString();
}
} catch (Exception ex) {
Logger.printException(() -> "blockInitPlaybackRequest failure", ex);
@@ -78,6 +96,17 @@ public class SpoofVideoStreamsPatch {
return SPOOF_STREAMING_DATA;
}
/**
* Injection point.
* Only invoked when playing a livestream on an iOS client.
*/
public static boolean fixHLSCurrentTime(boolean original) {
if (!SPOOF_STREAMING_DATA) {
return original;
}
return false;
}
/**
* Injection point.
*/
@@ -86,20 +115,27 @@ public class SpoofVideoStreamsPatch {
try {
Uri uri = Uri.parse(url);
String path = uri.getPath();
if (path == null || !path.contains("player")) {
return;
}
// 'get_drm_license' has no video id and appears to happen when waiting for a paid video to start.
// 'heartbeat' has no video id and appears to be only after playback has started.
// 'refresh' has no video id and appears to happen when waiting for a livestream to start.
if (path != null && path.contains("player") && !path.contains("heartbeat")
&& !path.contains("refresh")) {
String id = uri.getQueryParameter("id");
if (id == null) {
Logger.printException(() -> "Ignoring request that has no video id." +
" Url: " + url + " headers: " + requestHeaders);
return;
}
StreamingDataRequest.fetchRequest(id, requestHeaders);
// 'ad_break' has no video id.
if (path.contains("get_drm_license") || path.contains("heartbeat")
|| path.contains("refresh") || path.contains("ad_break")) {
Logger.printDebug(() -> "Ignoring path: " + path);
return;
}
String id = uri.getQueryParameter("id");
if (id == null) {
Logger.printException(() -> "Ignoring request with no id: " + url);
return;
}
StreamingDataRequest.fetchRequest(id, requestHeaders);
} catch (Exception ex) {
Logger.printException(() -> "buildRequest failure", ex);
}
@@ -165,10 +201,37 @@ public class SpoofVideoStreamsPatch {
return postData;
}
public static final class ForceiOSAVCAvailability implements Setting.Availability {
/**
* Injection point.
*/
public static String appendSpoofedClient(String videoFormat) {
try {
if (SPOOF_STREAMING_DATA && BaseSettings.SPOOF_STREAMING_DATA_STATS_FOR_NERDS.get()
&& !TextUtils.isEmpty(videoFormat)) {
// Force LTR layout, to match the same LTR video time/length layout YouTube uses for all languages.
return "\u202D" + videoFormat + "\u2009(" // u202D = left to right override
+ StreamingDataRequest.getLastSpoofedClientName() + ")";
}
} catch (Exception ex) {
Logger.printException(() -> "appendSpoofedClient failure", ex);
}
return videoFormat;
}
public static final class AudioStreamLanguageOverrideAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return BaseSettings.SPOOF_VIDEO_STREAMS.get() && BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.ANDROID_VR_NO_AUTH;
}
}
public static final class SpoofiOSAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
}
}
}

View File

@@ -5,6 +5,7 @@ import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Locale;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.requests.Requester;
@@ -19,7 +20,9 @@ final class PlayerRoutes {
"?fields=streamingData" +
"&alt=proto"
).compile();
private static final String YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/";
/**
* TCP connection and HTTP read timeout
*/
@@ -28,27 +31,39 @@ final class PlayerRoutes {
private PlayerRoutes() {
}
static String createInnertubeBody(ClientType clientType) {
static String createInnertubeBody(ClientType clientType, String videoId) {
JSONObject innerTubeBody = new JSONObject();
try {
JSONObject context = new JSONObject();
// Can override default language only if no login is used.
// Could use preferred audio for all clients that do not login,
// but if this is a fall over client it will set the language even though
// the audio language is not selectable in the UI.
ClientType userSelectedClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
Locale streamLocale = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH
? BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getLocale()
: Locale.getDefault();
JSONObject client = new JSONObject();
client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getIso639_1());
client.put("clientName", clientType.name());
client.put("clientVersion", clientType.clientVersion);
client.put("deviceMake", clientType.deviceMake);
client.put("deviceModel", clientType.deviceModel);
client.put("clientName", clientType.clientName);
client.put("clientVersion", clientType.clientVersion);
client.put("osName", clientType.osName);
client.put("osVersion", clientType.osVersion);
if (clientType.androidSdkVersion != null) {
client.put("androidSdkVersion", clientType.androidSdkVersion);
}
client.put("hl", streamLocale.getLanguage());
client.put("gl", streamLocale.getCountry());
context.put("client", client);
innerTubeBody.put("context", context);
innerTubeBody.put("contentCheckOk", true);
innerTubeBody.put("racyCheckOk", true);
innerTubeBody.put("videoId", "%s");
innerTubeBody.put("videoId", videoId);
} catch (JSONException e) {
Logger.printException(() -> "Failed to create innerTubeBody", e);
}
@@ -64,6 +79,9 @@ final class PlayerRoutes {
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", clientType.userAgent);
// Not a typo. "Client-Name" uses the client type id.
connection.setRequestProperty("X-YouTube-Client-Name", String.valueOf(clientType.id));
connection.setRequestProperty("X-YouTube-Client-Version", clientType.clientVersion);
connection.setUseCaches(false);
connection.setDoOutput(true);

View File

@@ -36,20 +36,40 @@ import app.revanced.extension.shared.spoof.ClientType;
public class StreamingDataRequest {
private static final ClientType[] CLIENT_ORDER_TO_USE;
static {
ClientType[] allClientTypes = ClientType.values();
ClientType preferredClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
CLIENT_ORDER_TO_USE[0] = preferredClient;
int i = 1;
for (ClientType c : allClientTypes) {
if (c != preferredClient) {
CLIENT_ORDER_TO_USE[i++] = c;
}
}
}
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String[] REQUEST_HEADER_KEYS = {
AUTHORIZATION_HEADER, // Available only to logged-in users.
"X-GOOG-API-FORMAT-VERSION",
"X-Goog-Visitor-Id"
};
/**
* TCP connection and HTTP read timeout.
*/
private static final int HTTP_TIMEOUT_MILLISECONDS = 10 * 1000;
/**
* Any arbitrarily large value, but must be at least twice {@link #HTTP_TIMEOUT_MILLISECONDS}
*/
private static final int MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000;
private static final Map<String, StreamingDataRequest> cache = Collections.synchronizedMap(
new LinkedHashMap<>(100) {
/**
@@ -67,22 +87,15 @@ public class StreamingDataRequest {
}
});
static {
ClientType[] allClientTypes = ClientType.values();
ClientType preferredClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
private static volatile ClientType lastSpoofedClientType;
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
CLIENT_ORDER_TO_USE[0] = preferredClient;
int i = 1;
for (ClientType c : allClientTypes) {
if (c != preferredClient) {
CLIENT_ORDER_TO_USE[i++] = c;
}
}
public static String getLastSpoofedClientName() {
ClientType client = lastSpoofedClientType;
return client == null ? "Unknown" : client.friendlyName;
}
private final String videoId;
private final Future<ByteBuffer> future;
private StreamingDataRequest(String videoId, Map<String, String> playerHeaders) {
@@ -107,7 +120,8 @@ public class StreamingDataRequest {
}
@Nullable
private static HttpURLConnection send(ClientType clientType, String videoId,
private static HttpURLConnection send(ClientType clientType,
String videoId,
Map<String, String> playerHeaders,
boolean showErrorToasts) {
Objects.requireNonNull(clientType);
@@ -115,26 +129,40 @@ public class StreamingDataRequest {
Objects.requireNonNull(playerHeaders);
final long startTime = System.currentTimeMillis();
String clientTypeName = clientType.name();
Logger.printDebug(() -> "Fetching video streams for: " + videoId + " using client: " + clientType.name());
try {
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
connection.setConnectTimeout(HTTP_TIMEOUT_MILLISECONDS);
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
for (String key : REQUEST_HEADER_KEYS) {
if (!clientType.canLogin && key.equals(AUTHORIZATION_HEADER)) {
continue;
}
boolean authHeadersIncludes = false;
for (String key : REQUEST_HEADER_KEYS) {
String value = playerHeaders.get(key);
if (value != null) {
if (key.equals(AUTHORIZATION_HEADER)) {
if (!clientType.useAuth) {
Logger.printDebug(() -> "Not including request header: " + key);
continue;
}
authHeadersIncludes = true;
}
Logger.printDebug(() -> "Including request header: " + key);
connection.setRequestProperty(key, value);
}
}
String innerTubeBody = String.format(PlayerRoutes.createInnertubeBody(clientType), videoId);
if (!authHeadersIncludes && clientType.requiresAuth) {
Logger.printDebug(() -> "Skipping client since user is not logged in: " + clientType
+ " videoId: " + videoId);
return null;
}
Logger.printDebug(() -> "Fetching video streams for: " + videoId + " using client: " + clientType);
String innerTubeBody = PlayerRoutes.createInnertubeBody(clientType, videoId);
byte[] requestBody = innerTubeBody.getBytes(StandardCharsets.UTF_8);
connection.setFixedLengthStreamingMode(requestBody.length);
connection.getOutputStream().write(requestBody);
@@ -142,8 +170,10 @@ public class StreamingDataRequest {
final int responseCode = connection.getResponseCode();
if (responseCode == 200) return connection;
handleConnectionError(clientTypeName + " not available with response code: "
+ responseCode + " message: " + connection.getResponseMessage(),
// This situation likely means the patches are outdated.
// Use a toast message that suggests updating.
handleConnectionError("Playback error (App is outdated?) " + clientType + ": "
+ responseCode + " response: " + connection.getResponseMessage(),
null, showErrorToasts);
} catch (SocketTimeoutException ex) {
handleConnectionError("Connection timeout", ex, showErrorToasts);
@@ -164,7 +194,7 @@ public class StreamingDataRequest {
// Retry with different client if empty response body is received.
int i = 0;
for (ClientType clientType : CLIENT_ORDER_TO_USE) {
// Show an error if the last client type fails, or if the debug is enabled then show for all attempts.
// Show an error if the last client type fails, or if debug is enabled then show for all attempts.
final boolean showErrorToast = (++i == CLIENT_ORDER_TO_USE.length) || debugEnabled;
HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast);
@@ -172,17 +202,22 @@ public class StreamingDataRequest {
try {
// gzip encoding doesn't response with content length (-1),
// but empty response body does.
if (connection.getContentLength() != 0) {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream())) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) >= 0) {
baos.write(buffer, 0, bytesRead);
}
if (connection.getContentLength() == 0) {
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
Utils.showToastShort("Ignoring empty spoof stream client: " + clientType);
}
} else {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
return ByteBuffer.wrap(baos.toByteArray());
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) >= 0) {
baos.write(buffer, 0, bytesRead);
}
lastSpoofedClientType = clientType;
return ByteBuffer.wrap(baos.toByteArray());
}
}
} catch (IOException ex) {
@@ -191,7 +226,8 @@ public class StreamingDataRequest {
}
}
handleConnectionError("Could not fetch any client streams", null, debugEnabled);
lastSpoofedClientType = null;
handleConnectionError("Could not fetch any client streams", null, true);
return null;
}

View File

@@ -90,4 +90,12 @@ public class ThemeHelper {
public static int getForegroundColor() {
return isDarkTheme() ? getLightThemeColor() : getDarkThemeColor();
}
public static int getToolbarBackgroundColor() {
final String colorName = isDarkTheme()
? "yt_black3"
: "yt_white1";
return getColorInt(colorName);
}
}

View File

@@ -176,14 +176,13 @@ public final class AlternativeThumbnailsPatch {
// Unknown tab, treat as the home tab;
return homeOption;
}
if (selectedNavButton == NavigationButton.HOME) {
return homeOption;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS || selectedNavButton == NavigationButton.NOTIFICATIONS) {
return subscriptionsOption;
}
// A library tab variant is active.
return libraryOption;
return switch (selectedNavButton) {
case SUBSCRIPTIONS, NOTIFICATIONS -> subscriptionsOption;
case LIBRARY -> libraryOption;
// Home or explore tab.
default -> homeOption;
};
}
/**

View File

@@ -0,0 +1,54 @@
package app.revanced.extension.youtube.patches;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class ChangeFormFactorPatch {
public enum FormFactor {
/**
* Unmodified, and same as un-patched.
*/
DEFAULT(null),
/**
* <pre>
* Some changes include:
* - Explore tab is present.
* - watch history is missing.
* - feed thumbnails fade in.
*/
UNKNOWN(0),
SMALL(1),
LARGE(2),
/**
* Cars with 'Google built-in'.
* Layout seems identical to {@link #UNKNOWN}
* even when using an Android Automotive device.
*/
AUTOMOTIVE(3),
WEARABLE(4);
@Nullable
final Integer formFactorType;
FormFactor(@Nullable Integer formFactorType) {
this.formFactorType = formFactorType;
}
}
@Nullable
private static final Integer FORM_FACTOR_TYPE = Settings.CHANGE_FORM_FACTOR.get().formFactorType;
/**
* Injection point.
*/
public static int getFormFactor(int original) {
return FORM_FACTOR_TYPE == null
? original
: FORM_FACTOR_TYPE;
}
}

View File

@@ -3,12 +3,13 @@ package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class OpenVideosFullscreen {
public class DisableHdrPatch {
/**
* Injection point.
*/
public static boolean openVideoFullscreenPortrait(boolean original) {
return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
public static boolean disableHDRVideo() {
return !Settings.DISABLE_HDR_VIDEO.get();
}
}

View File

@@ -9,14 +9,23 @@ import app.revanced.extension.shared.settings.BaseSettings;
@SuppressWarnings("unused")
public final class EnableDebuggingPatch {
private static final ConcurrentMap<Long, Boolean> featureFlags
= new ConcurrentHashMap<>(300, 0.75f, 1);
/**
* Only log if debugging is enabled on startup.
* This prevents enabling debugging
* while the app is running then failing to restart
* resulting in an incomplete log.
*/
private static final boolean LOG_FEATURE_FLAGS = BaseSettings.DEBUG.get();
private static final ConcurrentMap<Long, Boolean> featureFlags = LOG_FEATURE_FLAGS
? new ConcurrentHashMap<>(800, 0.5f, 1)
: null;
/**
* Injection point.
*/
public static boolean isBooleanFeatureFlagEnabled(boolean value, long flag) {
if (value && BaseSettings.DEBUG.get()) {
if (LOG_FEATURE_FLAGS && value) {
if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> "boolean feature is enabled: " + flag);
}
@@ -29,7 +38,7 @@ public final class EnableDebuggingPatch {
* Injection point.
*/
public static double isDoubleFeatureFlagEnabled(double value, long flag, double defaultValue) {
if (defaultValue != value && BaseSettings.DEBUG.get()) {
if (LOG_FEATURE_FLAGS && defaultValue != value) {
if (featureFlags.putIfAbsent(flag, true) == null) {
// Align the log outputs to make post processing easier.
Logger.printDebug(() -> " double feature is enabled: " + flag
@@ -44,7 +53,7 @@ public final class EnableDebuggingPatch {
* Injection point.
*/
public static long isLongFeatureFlagEnabled(long value, long flag, long defaultValue) {
if (defaultValue != value && BaseSettings.DEBUG.get()) {
if (LOG_FEATURE_FLAGS && defaultValue != value) {
if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> " long feature is enabled: " + flag
+ " value: " + value + (defaultValue == 0 ? "" : " default: " + defaultValue));
@@ -58,7 +67,7 @@ public final class EnableDebuggingPatch {
* Injection point.
*/
public static String isStringFeatureFlagEnabled(String value, long flag, String defaultValue) {
if (BaseSettings.DEBUG.get() && !defaultValue.equals(value)) {
if (LOG_FEATURE_FLAGS && !defaultValue.equals(value)) {
if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> " string feature is enabled: " + flag
+ " value: " + value + (defaultValue.isEmpty() ? "" : " default: " + defaultValue));

View File

@@ -0,0 +1,63 @@
package app.revanced.extension.youtube.patches;
import android.widget.ImageView;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class ExitFullscreenPatch {
public enum FullscreenMode {
DISABLED,
PORTRAIT,
LANDSCAPE,
PORTRAIT_LANDSCAPE,
}
/**
* Injection point.
*/
public static void endOfVideoReached() {
try {
FullscreenMode mode = Settings.EXIT_FULLSCREEN.get();
if (mode == FullscreenMode.DISABLED) {
return;
}
if (PlayerType.getCurrent() == PlayerType.WATCH_WHILE_FULLSCREEN) {
if (mode != FullscreenMode.PORTRAIT_LANDSCAPE) {
if (Utils.isLandscapeOrientation()) {
if (mode == FullscreenMode.PORTRAIT) {
return;
}
} else if (mode == FullscreenMode.LANDSCAPE) {
return;
}
}
// If the user cold launches the app and plays a video but does not
// tap to show the overlay controls, the fullscreen button is not
// set because the overlay controls are not attached.
// To fix this, push the perform click to the back fo the main thread,
// and by then the overlay controls will be visible since the video is now finished.
Utils.runOnMainThread(() -> {
ImageView button = PlayerControlsPatch.fullscreenButtonRef.get();
if (button == null) {
Logger.printDebug(() -> "Fullscreen button is null, cannot click");
} else {
Logger.printDebug(() -> "Clicking fullscreen button");
final boolean soundEffectsEnabled = button.isSoundEffectsEnabled();
button.setSoundEffectsEnabled(false);
button.performClick();
button.setSoundEffectsEnabled(soundEffectsEnabled);
}
});
}
} catch (Exception ex) {
Logger.printException(() -> "endOfVideoReached failure", ex);
}
}
}

View File

@@ -0,0 +1,24 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class FixPlaybackSpeedWhilePlayingPatch {
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
public static boolean playbackSpeedChanged(float playbackSpeed) {
if (playbackSpeed == DEFAULT_YOUTUBE_PLAYBACK_SPEED &&
PlayerType.getCurrent().isMaximizedOrFullscreen()) {
Logger.printDebug(() -> "Blocking call to change playback speed to 1.0x");
return true;
}
return false;
}
}

View File

@@ -0,0 +1,57 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class ForceOriginalAudioPatch {
private static final String DEFAULT_AUDIO_TRACKS_SUFFIX = ".4";
/**
* If the conditions to use this patch were present when the app launched.
*/
public static boolean PATCH_AVAILABLE = SpoofVideoStreamsPatch.notSpoofingToAndroid();
public static final class ForceOriginalAudioAvailability implements Setting.Availability {
@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 PATCH_AVAILABLE && SpoofVideoStreamsPatch.notSpoofingToAndroid();
}
}
/**
* Injection point.
*/
public static boolean isDefaultAudioStream(boolean isDefault, String audioTrackId, String audioTrackDisplayName) {
try {
if (!Settings.FORCE_ORIGINAL_AUDIO.get()) {
return isDefault;
}
if (audioTrackId.isEmpty()) {
// Older app targets can have empty audio tracks and these might be placeholders.
// The real audio tracks are called after these.
return isDefault;
}
Logger.printDebug(() -> "default: " + String.format("%-5s", isDefault) + " id: "
+ String.format("%-8s", audioTrackId) + " name:" + audioTrackDisplayName);
final boolean isOriginal = audioTrackId.endsWith(DEFAULT_AUDIO_TRACKS_SUFFIX);
if (isOriginal) {
Logger.printDebug(() -> "Using audio: " + audioTrackId);
}
return isOriginal;
} catch (Exception ex) {
Logger.printException(() -> "isDefaultAudioStream failure", ex);
return isDefault;
}
}
}

View File

@@ -82,7 +82,13 @@ public final class MiniplayerPatch {
final int WIDTH_DIP_MIN = 170; // Seems to be the smallest that works.
final int HORIZONTAL_PADDING_DIP = 15; // Estimated padding.
// Round down to the nearest 5 pixels, to keep any error toasts easier to read.
final int WIDTH_DIP_MAX = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
final int estimatedWidthDipMax = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
// On some ultra low end devices the pixel width and density are the same number,
// which causes the estimate to always give a value of 1.
// Fix this by using a fixed size of double the min width.
final int WIDTH_DIP_MAX = estimatedWidthDipMax <= WIDTH_DIP_MIN
? 2 * WIDTH_DIP_MIN
: estimatedWidthDipMax;
Logger.printDebug(() -> "Screen dip width: " + deviceDipWidth + " maxWidth: " + WIDTH_DIP_MAX);
int dipWidth = Settings.MINIPLAYER_WIDTH_DIP.get();
@@ -127,8 +133,10 @@ public final class MiniplayerPatch {
private static final boolean HIDE_SUBTEXT_ENABLED =
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get();
private static final boolean HIDE_REWIND_FORWARD_ENABLED =
CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get();
// 19.25 is last version that has forward/back buttons for phones,
// but buttons still show for tablets/foldable devices and they don't work well so always hide.
private static final boolean HIDE_REWIND_FORWARD_ENABLED = CURRENT_TYPE == MODERN_1
&& (VersionCheckPatch.IS_19_34_OR_GREATER || Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get());
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
Settings.MINIPLAYER_ROUNDED_CORNERS.get();
@@ -145,6 +153,18 @@ public final class MiniplayerPatch {
private static final int OPACITY_LEVEL;
static {
int opacity = Settings.MINIPLAYER_OPACITY.get();
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
Settings.MINIPLAYER_OPACITY.resetToDefault();
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
}
OPACITY_LEVEL = (opacity * 255) / 100;
}
public static final class MiniplayerHorizontalDragAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
@@ -163,18 +183,6 @@ public final class MiniplayerPatch {
}
}
static {
int opacity = Settings.MINIPLAYER_OPACITY.get();
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
Settings.MINIPLAYER_OPACITY.resetToDefault();
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
}
OPACITY_LEVEL = (opacity * 255) / 100;
}
/**
* Injection point.
*

View File

@@ -3,12 +3,15 @@ package app.revanced.extension.youtube.patches;
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.os.Build;
import android.view.View;
import java.util.EnumMap;
import java.util.Map;
import android.widget.TextView;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -26,6 +29,15 @@ public final class NavigationButtonsPatch {
private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON
= Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get();
private static final Boolean DISABLE_TRANSLUCENT_STATUS_BAR
= Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get();
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT.get();
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK.get();
/**
* Injection point.
*/
@@ -48,4 +60,42 @@ public final class NavigationButtonsPatch {
public static void hideNavigationButtonLabels(TextView navigationLabelsView) {
hideViewUnderCondition(Settings.HIDE_NAVIGATION_BUTTON_LABELS, navigationLabelsView);
}
/**
* Injection point.
*/
public static boolean useTranslucentNavigationStatusBar(boolean original) {
// Must check Android version, as forcing this on Android 11 or lower causes app hang and crash.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
return original;
}
if (DISABLE_TRANSLUCENT_STATUS_BAR) {
return false;
}
return original;
}
/**
* Injection point.
*/
public static boolean useTranslucentNavigationButtons(boolean original) {
// Feature requires Android 13+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return original;
}
if (!DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
return original;
}
if (DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
return false;
}
return Utils.isDarkModeEnabled(Utils.getContext())
? !DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
: !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT;
}
}

View File

@@ -0,0 +1,95 @@
package app.revanced.extension.youtube.patches;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import java.lang.ref.WeakReference;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class OpenShortsInRegularPlayerPatch {
public enum ShortsPlayerType {
SHORTS_PLAYER,
REGULAR_PLAYER,
REGULAR_PLAYER_FULLSCREEN
}
static {
if (!VersionCheckPatch.IS_19_46_OR_GREATER
&& Settings.SHORTS_PLAYER_TYPE.get() == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN) {
// User imported newer settings to an older app target.
Logger.printInfo(() -> "Resetting " + Settings.SHORTS_PLAYER_TYPE);
Settings.SHORTS_PLAYER_TYPE.resetToDefault();
}
}
private static WeakReference<Activity> mainActivityRef = new WeakReference<>(null);
/**
* Injection point.
*/
public static void setMainActivity(Activity activity) {
mainActivityRef = new WeakReference<>(activity);
}
/**
* Injection point.
*/
public static boolean openShort(String videoID) {
try {
ShortsPlayerType type = Settings.SHORTS_PLAYER_TYPE.get();
if (type == ShortsPlayerType.SHORTS_PLAYER) {
return false; // Default unpatched behavior.
}
if (videoID.isEmpty()) {
// Shorts was opened using launcher app shortcut.
//
// This check will not detect if the Shorts app shortcut is used
// while the app is running in the background (instead the regular player is opened).
// To detect that the hooked method map parameter can be checked
// if integer key 'com.google.android.apps.youtube.app.endpoint.flags'
// has bitmask 16 set.
//
// This use case seems unlikely if the user has the Shorts
// set to open in the regular player, so it's ignored as
// checking the map makes the patch more complicated.
Logger.printDebug(() -> "Ignoring Short with no videoId");
return false;
}
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.SHORTS) {
return false; // Always use Shorts player for the Shorts nav button.
}
final boolean forceFullScreen = (type == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN);
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(forceFullScreen);
// Can use the application context and add intent flags of
// FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TOP
// But the activity context seems to fix random app crashes
// if Shorts urls are opened outside the app.
var context = mainActivityRef.get();
Intent videoPlayerIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://youtube.com/watch?v=" + videoID)
);
videoPlayerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
videoPlayerIntent.setPackage(context.getPackageName());
context.startActivity(videoPlayerIntent);
return true;
} catch (Exception ex) {
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(null);
Logger.printException(() -> "openShort failure", ex);
return false;
}
}
}

View File

@@ -0,0 +1,41 @@
package app.revanced.extension.youtube.patches;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class OpenVideosFullscreenHookPatch {
@Nullable
private static volatile Boolean openNextVideoFullscreen;
public static void setOpenNextVideoFullscreen(@Nullable Boolean forceFullScreen) {
openNextVideoFullscreen = forceFullScreen;
}
/**
* Changed during patching since this class is also
* used by {@link OpenVideosFullscreenHookPatch}.
*/
private static boolean isFullScreenPatchIncluded() {
return false;
}
/**
* Injection point.
*/
public static boolean openVideoFullscreenPortrait(boolean original) {
Boolean openFullscreen = openNextVideoFullscreen;
if (openFullscreen != null) {
openNextVideoFullscreen = null;
return openFullscreen;
}
if (!isFullScreenPatchIncluded()) {
return false;
}
return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
}
}

View File

@@ -4,15 +4,30 @@ import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class PlayerControlsPatch {
public static WeakReference<ImageView> fullscreenButtonRef = new WeakReference<>(null);
private static boolean fullscreenButtonVisibilityCallbacksExist() {
return false; // Modified during patching if needed.
}
/**
* Injection point.
*/
public static void setFullscreenCloseButton(ImageView imageButton) {
fullscreenButtonRef = new WeakReference<>(imageButton);
Logger.printDebug(() -> "Fullscreen button set");
if (!fullscreenButtonVisibilityCallbacksExist()) {
return;
}
// Add a global listener, since the protected method
// View#onVisibilityChanged() does not have any call backs.
imageButton.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@@ -39,7 +54,7 @@ public class PlayerControlsPatch {
}
// noinspection EmptyMethod
public static void fullscreenButtonVisibilityChanged(boolean isVisible) {
private static void fullscreenButtonVisibilityChanged(boolean isVisible) {
// Code added during patching.
}

View File

@@ -1,16 +0,0 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class TabletLayoutPatch {
private static final boolean TABLET_LAYOUT_ENABLED = Settings.TABLET_LAYOUT.get();
/**
* Injection point.
*/
public static boolean getTabletLayoutEnabled() {
return TABLET_LAYOUT_ENABLED;
}
}

View File

@@ -9,4 +9,5 @@ public class VersionCheckPatch {
public static final boolean IS_19_26_OR_GREATER = Utils.getAppVersionName().compareTo("19.26.00") >= 0;
public static final boolean IS_19_29_OR_GREATER = Utils.getAppVersionName().compareTo("19.29.00") >= 0;
public static final boolean IS_19_34_OR_GREATER = Utils.getAppVersionName().compareTo("19.34.00") >= 0;
public static final boolean IS_19_46_OR_GREATER = Utils.getAppVersionName().compareTo("19.46.00") >= 0;
}

View File

@@ -5,10 +5,13 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class VideoAdsPatch {
// Used by app.revanced.patches.youtube.ad.general.video.patch.VideoAdsPatch
// depends on Whitelist patch (still needs to be written)
private static final boolean SHOW_VIDEO_ADS = !Settings.HIDE_VIDEO_ADS.get();
/**
* Injection point.
*/
public static boolean shouldShowAds() {
return !Settings.HIDE_VIDEO_ADS.get(); // TODO && Whitelist.shouldShowAds();
return SHOW_VIDEO_ADS;
}
}

View File

@@ -8,10 +8,12 @@ import android.view.View;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.settings.Settings;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.StringTrieSearch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class AdsFilter extends Filter {
@@ -22,6 +24,11 @@ public final class AdsFilter extends Filter {
// endregion
// https://encrypted-tbn0.gstatic.com/shopping?q=abc
private static final String STORE_BANNER_DOMAIN = "gstatic.com/shopping";
private static final boolean HIDE_END_SCREEN_STORE_BANNER =
Settings.HIDE_END_SCREEN_STORE_BANNER.get();
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup playerShoppingShelf;
@@ -85,7 +92,8 @@ public final class AdsFilter extends Filter {
"composite_concurrent_carousel_layout",
"carousel_headered_layout",
"full_width_portrait_image_layout",
"brand_video_shelf"
"brand_video_shelf",
"brand_video_singleton"
);
final var movieAds = new StringFilterGroup(
@@ -111,23 +119,23 @@ public final class AdsFilter extends Filter {
"expandable_list"
);
channelProfile = new StringFilterGroup(
null,
"channel_profile.eml"
);
playerShoppingShelf = new StringFilterGroup(
null,
Settings.HIDE_PLAYER_STORE_SHELF,
"horizontal_shelf.eml"
);
playerShoppingShelfBuffer = new ByteArrayFilterGroup(
Settings.HIDE_PLAYER_STORE_SHELF,
null,
"shopping_item_card_list.eml"
);
visitStoreButton = new ByteArrayFilterGroup(
channelProfile = new StringFilterGroup(
Settings.HIDE_VISIT_STORE_BUTTON,
"channel_profile.eml"
);
visitStoreButton = new ByteArrayFilterGroup(
null,
"header_store_button"
);
@@ -171,6 +179,11 @@ public final class AdsFilter extends Filter {
return false;
}
// Check for the index because of likelihood of false positives.
if (matchedGroup == shoppingLinks && contentIndex != 0) {
return false;
}
if (exceptions.matches(path))
return false;
@@ -187,13 +200,25 @@ public final class AdsFilter extends Filter {
return false;
}
// Check for the index because of likelihood of false positives.
if (matchedGroup == shoppingLinks && contentIndex != 0)
return false;
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
/**
* Injection point.
*
* @param elementsList List of components of the end screen container.
* @param protobufList Component (ProtobufList).
*/
public static void hideEndScreenStoreBanner(List<Object> elementsList, Object protobufList) {
if (HIDE_END_SCREEN_STORE_BANNER && protobufList.toString().contains(STORE_BANNER_DOMAIN)) {
Logger.printDebug(() -> "Hiding store banner");
return;
}
elementsList.add(protobufList);
}
/**
* Hide the view, which shows ads in the homepage.
*

View File

@@ -14,6 +14,11 @@ final class CommentsFilter extends Filter {
private final ByteArrayFilterGroup emojiPickerBufferGroup;
public CommentsFilter() {
var chatSummary = new StringFilterGroup(
Settings.HIDE_COMMENTS_CHAT_SUMMARY,
"live_chat_summary_banner.eml"
);
var commentsByMembers = new StringFilterGroup(
Settings.HIDE_COMMENTS_BY_MEMBERS_HEADER,
"sponsorships_comments_header.eml",
@@ -54,6 +59,7 @@ final class CommentsFilter extends Filter {
);
addPathCallbacks(
chatSummary,
commentsByMembers,
comments,
createAShort,

View File

@@ -45,6 +45,11 @@ final class DescriptionComponentsFilter extends Filter {
"transcript_section"
);
final StringFilterGroup howThisWasMadeSection = new StringFilterGroup(
Settings.HIDE_HOW_THIS_WAS_MADE_SECTION,
"how_this_was_made_section"
);
macroMarkersCarousel = new StringFilterGroup(
null,
"macro_markers_carousel.eml"
@@ -64,6 +69,7 @@ final class DescriptionComponentsFilter extends Filter {
addPathCallbacks(
attributesSection,
infoCardsSection,
howThisWasMadeSection,
podcastSection,
transcriptSection,
macroMarkersCarousel

View File

@@ -528,14 +528,13 @@ final class KeywordContentFilter extends Filter {
if (selectedNavButton == null) {
return hideHome; // Unknown tab, treat the same as home.
}
if (selectedNavButton == NavigationButton.HOME) {
return hideHome;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS) {
return hideSubscriptions;
}
// User is in the Library or Notifications tab.
return false;
return switch (selectedNavButton) {
case HOME, EXPLORE -> hideHome;
case SUBSCRIPTIONS -> hideSubscriptions;
// User is in the Library or notifications.
default -> false;
};
}
private void updateStats(boolean videoWasHidden, @Nullable String keyword) {

View File

@@ -36,7 +36,6 @@ public final class LayoutComponentsFilter extends Filter {
);
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup searchResultShelfHeader;
private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup notifyMe;
private final StringFilterGroup expandableMetadata;
@@ -74,13 +73,15 @@ public final class LayoutComponentsFilter extends Filter {
final var communityPosts = new StringFilterGroup(
Settings.HIDE_COMMUNITY_POSTS,
"post_base_wrapper",
"post_base_wrapper", // may be obsolete and no longer needed.
"text_post_root.eml",
"images_post_root.eml",
"images_post_slim.eml",
"images_post_slim.eml", // may be obsolete and no longer needed.
"images_post_root_slim.eml",
"text_post_root_slim.eml",
"post_base_wrapper_slim.eml"
"post_base_wrapper_slim.eml",
"poll_post_root.eml",
"videos_post_root.eml"
);
final var communityGuidelines = new StringFilterGroup(
@@ -106,7 +107,8 @@ public final class LayoutComponentsFilter extends Filter {
inFeedSurvey = new StringFilterGroup(
Settings.HIDE_FEED_SURVEY,
"in_feed_survey",
"slimline_survey"
"slimline_survey",
"feed_nudge"
);
final var medicalPanel = new StringFilterGroup(
@@ -161,9 +163,9 @@ public final class LayoutComponentsFilter extends Filter {
"inline_expander"
);
final var channelBar = new StringFilterGroup(
final var compactChannelBar = new StringFilterGroup(
Settings.HIDE_CHANNEL_BAR,
"channel_bar"
"compact_channel_bar"
);
final var relatedVideos = new StringFilterGroup(
@@ -194,11 +196,6 @@ public final class LayoutComponentsFilter extends Filter {
"timed_reaction"
);
searchResultShelfHeader = new StringFilterGroup(
Settings.HIDE_SEARCH_RESULT_SHELF_HEADER,
"shelf_header.eml"
);
notifyMe = new StringFilterGroup(
Settings.HIDE_NOTIFY_ME_BUTTON,
"set_reminder_button"
@@ -257,7 +254,7 @@ public final class LayoutComponentsFilter extends Filter {
inFeedSurvey,
notifyMe,
likeSubscribeGlow,
channelBar,
compactChannelBar,
communityPosts,
paidPromotion,
searchResultVideo,
@@ -324,9 +321,6 @@ public final class LayoutComponentsFilter extends Filter {
return false;
}
// TODO: This also hides the feed Shorts shelf header
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
if (matchedGroup == horizontalShelves) {
if (contentIndex == 0 && hideShelves()) {
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);

View File

@@ -297,7 +297,7 @@ public final class ShortsFilter extends Filter {
if (matchedGroup == suggestedAction) {
// Skip searching the buffer if all suggested actions are set to hidden.
// This has a secondary effect of hiding all new un-identified actions
// under the assumption that the user wants all actions hidden.
// under the assumption that the user wants all suggestions hidden.
if (isEverySuggestedActionFilterEnabled()) {
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
@@ -324,19 +324,22 @@ public final class ShortsFilter extends Filter {
}
private static boolean shouldHideShortsFeedItems() {
// Known issue if hide home is on but at least one other hide is off:
//
// Shorts suggestions will load in the background if a video is opened and
// immediately minimized before any suggestions are loaded.
// In this state the player type will show minimized, which cannot
// distinguish between Shorts suggestions loading in the player and between
// scrolling thru search/home/subscription tabs while a player is minimized.
final boolean hideHome = Settings.HIDE_SHORTS_HOME.get();
final boolean hideSubscriptions = Settings.HIDE_SHORTS_SUBSCRIPTIONS.get();
final boolean hideSearch = Settings.HIDE_SHORTS_SEARCH.get();
final boolean hideHistory = Settings.HIDE_SHORTS_HISTORY.get();
if (hideHome && hideSubscriptions && hideSearch) {
// Shorts suggestions can load in the background if a video is opened and
// then immediately minimized before any suggestions are loaded.
// In this state the player type will show minimized, which makes it not possible to
// distinguish between Shorts suggestions loading in the player and between
// scrolling thru search/home/subscription tabs while a player is minimized.
//
// To avoid this situation for users that never want to show Shorts (all hide Shorts options are enabled)
// then hide all Shorts everywhere including the Library history and Library playlists.
if (!hideHome && !hideSubscriptions && !hideSearch && !hideHistory) {
return false;
}
if (hideHome && hideSubscriptions && hideSearch && hideHistory) {
return true;
}
@@ -352,24 +355,29 @@ public final class ShortsFilter extends Filter {
}
// Avoid checking navigation button status if all other Shorts should show.
if (!hideHome && !hideSubscriptions) {
if (!hideHome && !hideSubscriptions && !hideHistory) {
return false;
}
// Check navigation absolutely last since the check may block this thread.
NavigationButton selectedNavButton = NavigationButton.getSelectedNavigationButton();
if (selectedNavButton == null) {
return hideHome; // Unknown tab, treat the same as home.
}
if (selectedNavButton == NavigationButton.HOME) {
return hideHome;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS) {
return hideSubscriptions;
}
// User must be in the library tab. Don't hide the history or any playlists here.
return false;
return switch (selectedNavButton) {
case HOME, EXPLORE -> hideHome;
case SUBSCRIPTIONS -> hideSubscriptions;
case LIBRARY -> hideHistory;
default -> false;
};
}
/**
* Injection point. Only used if patching older than 19.03.
* This hook may be obsolete even for old versions
* as they now use a litho layout like newer versions.
*/
public static void hideShortsShelf(final View shortsShelfView) {
if (shouldHideShortsFeedItems()) {
Utils.hideViewByLayoutParams(shortsShelfView);

View File

@@ -32,6 +32,11 @@ public class CustomPlaybackSpeedPatch {
*/
public static final float PLAYBACK_SPEED_MAXIMUM = 8;
/**
* Tap and hold speed.
*/
private static final float TAP_AND_HOLD_SPEED;
/**
* Custom playback speeds.
*/
@@ -48,12 +53,27 @@ public class CustomPlaybackSpeedPatch {
private static String[] preferenceListEntries, preferenceListEntryValues;
static {
final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get();
if (holdSpeed > 0 && holdSpeed <= PLAYBACK_SPEED_MAXIMUM) {
TAP_AND_HOLD_SPEED = holdSpeed;
} else {
showInvalidCustomSpeedToast();
Settings.SPEED_TAP_AND_HOLD.resetToDefault();
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.get();
}
loadCustomSpeeds();
}
private static void resetCustomSpeeds(@NonNull String toastMessage) {
Utils.showToastLong(toastMessage);
Settings.CUSTOM_PLAYBACK_SPEEDS.resetToDefault();
/**
* Injection point.
*/
public static float tapAndHoldSpeed() {
return TAP_AND_HOLD_SPEED;
}
private static void showInvalidCustomSpeedToast() {
Utils.showToastLong(str("revanced_custom_playback_speeds_invalid", PLAYBACK_SPEED_MAXIMUM));
}
private static void loadCustomSpeeds() {
@@ -74,17 +94,18 @@ public class CustomPlaybackSpeedPatch {
}
if (speedFloat >= PLAYBACK_SPEED_MAXIMUM) {
resetCustomSpeeds(str("revanced_custom_playback_speeds_invalid", PLAYBACK_SPEED_MAXIMUM));
showInvalidCustomSpeedToast();
Settings.CUSTOM_PLAYBACK_SPEEDS.resetToDefault();
loadCustomSpeeds();
return;
}
customPlaybackSpeeds[i] = speedFloat;
i++;
customPlaybackSpeeds[i++] = speedFloat;
}
} catch (Exception ex) {
Logger.printInfo(() -> "parse error", ex);
resetCustomSpeeds(str("revanced_custom_playback_speeds_parse_exception"));
Utils.showToastLong(str("revanced_custom_playback_speeds_parse_exception"));
Settings.CUSTOM_PLAYBACK_SPEEDS.resetToDefault();
loadCustomSpeeds();
}
}

View File

@@ -18,6 +18,8 @@ public final class SeekbarColorPatch {
private static final boolean SEEKBAR_CUSTOM_COLOR_ENABLED = Settings.SEEKBAR_CUSTOM_COLOR.get();
private static final boolean HIDE_SEEKBAR_THUMBNAIL_ENABLED = Settings.HIDE_SEEKBAR_THUMBNAIL.get();
/**
* Default color of the litho seekbar.
* Differs slightly from the default custom seekbar color setting.
@@ -25,14 +27,19 @@ public final class SeekbarColorPatch {
private static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000;
/**
* Default colors of the gradient seekbar.
* Feed default colors of the gradient seekbar.
*/
private static final int[] ORIGINAL_SEEKBAR_GRADIENT_COLORS = { 0xFFFF0033, 0xFFFF2791 };
private static final int[] FEED_ORIGINAL_SEEKBAR_GRADIENT_COLORS = { 0xFFFF0033, 0xFFFF2791 };
/**
* Default positions of the gradient seekbar.
* Feed default positions of the gradient seekbar.
*/
private static final float[] ORIGINAL_SEEKBAR_GRADIENT_POSITIONS = { 0.8f, 1.0f };
private static final float[] FEED_ORIGINAL_SEEKBAR_GRADIENT_POSITIONS = { 0.8f, 1.0f };
/**
* Empty seekbar gradient, if hide seekbar in feed is enabled.
*/
private static final int[] HIDDEN_SEEKBAR_GRADIENT_COLORS = { 0x0, 0x0 };
/**
* Default YouTube seekbar color brightness.
@@ -41,16 +48,21 @@ public final class SeekbarColorPatch {
/**
* If {@link Settings#SEEKBAR_CUSTOM_COLOR} is enabled,
* this is the color value of {@link Settings#SEEKBAR_CUSTOM_COLOR_VALUE}.
* this is the color value of {@link Settings#SEEKBAR_CUSTOM_COLOR_PRIMARY}.
* Otherwise this is {@link #ORIGINAL_SEEKBAR_COLOR}.
*/
private static int seekbarColor = ORIGINAL_SEEKBAR_COLOR;
private static int customSeekbarColor = ORIGINAL_SEEKBAR_COLOR;
/**
* Custom seekbar hue, saturation, and brightness values.
*/
private static final float[] customSeekbarColorHSV = new float[3];
/**
* Custom seekbar color, used for linear gradient replacements.
*/
private static final int[] customSeekbarColorGradient = new int[2];
static {
float[] hsv = new float[3];
Color.colorToHSV(ORIGINAL_SEEKBAR_COLOR, hsv);
@@ -63,26 +75,22 @@ public final class SeekbarColorPatch {
private static void loadCustomSeekbarColor() {
try {
seekbarColor = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_VALUE.get());
Color.colorToHSV(seekbarColor, customSeekbarColorHSV);
customSeekbarColor = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_PRIMARY.get());
Color.colorToHSV(customSeekbarColor, customSeekbarColorHSV);
customSeekbarColorGradient[0] = customSeekbarColor;
customSeekbarColorGradient[1] = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_ACCENT.get());
} catch (Exception ex) {
Utils.showToastShort(str("revanced_seekbar_custom_color_invalid"));
Settings.SEEKBAR_CUSTOM_COLOR_VALUE.resetToDefault();
Settings.SEEKBAR_CUSTOM_COLOR_PRIMARY.resetToDefault();
Settings.SEEKBAR_CUSTOM_COLOR_ACCENT.resetToDefault();
loadCustomSeekbarColor();
}
}
public static int getSeekbarColor() {
return seekbarColor;
}
/**
* Injection point
*/
public static boolean playerSeekbarGradientEnabled(boolean original) {
if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false;
return original;
return customSeekbarColor;
}
/**
@@ -126,7 +134,7 @@ public final class SeekbarColorPatch {
// Even if the seekbar color xml value is changed to a completely different color (such as green),
// a color filter still cannot be selectively applied when the drawable has more than 1 color.
try {
String seekbarStyle = get9BitStyleIdentifier(seekbarColor);
String seekbarStyle = get9BitStyleIdentifier(customSeekbarColor);
Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle);
final int styleIdentifierDefault = Utils.getResourceIdentifier(
@@ -146,6 +154,13 @@ public final class SeekbarColorPatch {
}
}
/**
* Injection point.
*/
public static boolean showWatchHistoryProgressDrawable(boolean original) {
return !HIDE_SEEKBAR_THUMBNAIL_ENABLED && original;
}
/**
* Injection point.
*
@@ -156,35 +171,61 @@ public final class SeekbarColorPatch {
*/
public static int getLithoColor(int colorValue) {
if (colorValue == ORIGINAL_SEEKBAR_COLOR) {
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) {
return 0x00000000;
if (HIDE_SEEKBAR_THUMBNAIL_ENABLED) {
return 0x0;
}
return getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR);
return customSeekbarColor;
}
return colorValue;
}
private static String colorArrayToHex(int[] colors) {
final int length = colors.length;
StringBuilder builder = new StringBuilder(length * 12);
builder.append("[");
int i = 0;
for (int color : colors) {
builder.append(String.format("#%X", color));
if (++i < length) {
builder.append(", ");
}
}
builder.append("]");
return builder.toString();
}
/**
* Injection point.
*/
public static void setLinearGradient(int[] colors, float[] positions) {
final boolean hideSeekbar = Settings.HIDE_SEEKBAR_THUMBNAIL.get();
public static int[] getPlayerLinearGradient(int[] original) {
return SEEKBAR_CUSTOM_COLOR_ENABLED
? customSeekbarColorGradient
: original;
}
if (SEEKBAR_CUSTOM_COLOR_ENABLED || hideSeekbar) {
/**
* Injection point.
*/
public static int[] getLithoLinearGradient(int[] colors, float[] positions) {
if (SEEKBAR_CUSTOM_COLOR_ENABLED || HIDE_SEEKBAR_THUMBNAIL_ENABLED) {
// Most litho usage of linear gradients is hooked here,
// so must only change if the values are those for the seekbar.
if (Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors)
&& Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions)) {
Arrays.fill(colors, hideSeekbar
? 0x00000000
: seekbarColor);
return;
if ((Arrays.equals(FEED_ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors)
&& Arrays.equals(FEED_ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions))) {
return HIDE_SEEKBAR_THUMBNAIL_ENABLED
? HIDDEN_SEEKBAR_GRADIENT_COLORS
: customSeekbarColorGradient;
}
Logger.printDebug(() -> "Ignoring gradient colors: " + Arrays.toString(colors)
Logger.printDebug(() -> "Ignoring gradient colors: " + colorArrayToHex(colors)
+ " positions: " + Arrays.toString(positions));
}
return colors;
}
/**
@@ -198,7 +239,7 @@ public final class SeekbarColorPatch {
}
return colorValue == ORIGINAL_SEEKBAR_COLOR
? getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR)
? customSeekbarColor
: colorValue;
}
@@ -208,11 +249,9 @@ public final class SeekbarColorPatch {
* Overrides color used for the video player seekbar.
*/
public static int getVideoPlayerSeekbarColor(int originalColor) {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED) {
return originalColor;
}
return getSeekbarColorValue(originalColor);
return SEEKBAR_CUSTOM_COLOR_ENABLED
? getSeekbarColorValue(originalColor)
: originalColor;
}
/**
@@ -221,10 +260,6 @@ public final class SeekbarColorPatch {
*/
private static int getSeekbarColorValue(int originalColor) {
try {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED || originalColor == seekbarColor) {
return originalColor; // nothing to do
}
final int alphaDifference = Color.alpha(originalColor) - Color.alpha(ORIGINAL_SEEKBAR_COLOR);
// The seekbar uses the same color but different brightness for different situations.
@@ -237,7 +272,7 @@ public final class SeekbarColorPatch {
hsv[1] = customSeekbarColorHSV[1];
hsv[2] = clamp(customSeekbarColorHSV[2] + brightnessDifference, 0, 1);
final int replacementAlpha = clamp(Color.alpha(seekbarColor) + alphaDifference, 0, 255);
final int replacementAlpha = clamp(Color.alpha(customSeekbarColor) + alphaDifference, 0, 255);
final int replacementColor = Color.HSVToColor(replacementAlpha, hsv);
Logger.printDebug(() -> String.format("Original color: #%08X replacement color: #%08X",
originalColor, replacementColor));

View File

@@ -1,21 +1,30 @@
package app.revanced.extension.youtube.settings;
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.preference.PreferenceFragment;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
import android.widget.Toolbar;
import androidx.annotation.RequiresApi;
import java.util.Objects;
import static app.revanced.extension.shared.Utils.getChildView;
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
/**
* Hooks LicenseActivity.
@@ -25,17 +34,57 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
@SuppressWarnings("unused")
public class LicenseActivityHook {
private static ViewGroup.LayoutParams toolbarLayoutParams;
public static void setToolbarLayoutParams(Toolbar toolbar) {
if (toolbarLayoutParams != null) {
toolbar.setLayoutParams(toolbarLayoutParams);
}
}
/**
* Injection point.
* Overrides the ReVanced settings language.
*/
public static Context getAttachBaseContext(Context original) {
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language == AppLanguage.DEFAULT) {
return original;
}
return Utils.getContext();
}
/**
* Injection point.
*/
public static boolean useCairoSettingsFragment(boolean original) {
// Early targets have layout issues and it's better to always force off.
if (!VersionCheckPatch.IS_19_34_OR_GREATER) {
return false;
}
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
return false;
}
// On the first launch of a clean install, forcing the cairo menu can give a
// half broken appearance because all the preference icons may not be available yet.
// 19.34+ cairo settings are always on, so it doesn't need to be forced anyway.
// Cairo setting will show on the next launch of the app.
return original;
}
/**
* Injection point.
* <p>
* Hooks LicenseActivity#onCreate in order to inject our own fragment.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
public static void initialize(Activity licenseActivity) {
try {
ThemeHelper.setActivityTheme(licenseActivity);
licenseActivity.setContentView(
getResourceIdentifier("revanced_settings_with_toolbar", "layout"));
setBackButton(licenseActivity);
licenseActivity.setContentView(getResourceIdentifier(
"revanced_settings_with_toolbar", "layout"));
PreferenceFragment fragment;
String toolbarTitleResourceName;
@@ -58,7 +107,7 @@ public class LicenseActivityHook {
return;
}
setToolbarTitle(licenseActivity, toolbarTitleResourceName);
createToolbar(licenseActivity, toolbarTitleResourceName);
//noinspection deprecation
licenseActivity.getFragmentManager()
@@ -70,28 +119,36 @@ public class LicenseActivityHook {
}
}
private static void setToolbarTitle(Activity activity, String toolbarTitleResourceName) {
ViewGroup toolbar = activity.findViewById(getToolbarResourceId());
TextView toolbarTextView = Objects.requireNonNull(getChildView(toolbar, false,
view -> view instanceof TextView));
toolbarTextView.setText(getResourceIdentifier(toolbarTitleResourceName, "string"));
}
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressLint("UseCompatLoadingForDrawables")
private static void setBackButton(Activity activity) {
ViewGroup toolbar = activity.findViewById(getToolbarResourceId());
ImageButton imageButton = Objects.requireNonNull(getChildView(toolbar, false,
view -> view instanceof ImageButton));
imageButton.setImageDrawable(ReVancedPreferenceFragment.getBackButtonDrawable());
imageButton.setOnClickListener(view -> activity.onBackPressed());
}
private static void createToolbar(Activity activity, String toolbarTitleResourceName) {
// Replace dummy placeholder toolbar.
// This is required to fix submenu title alignment issue with Android ASOP 15+
ViewGroup toolBarParent = activity.findViewById(
getResourceIdentifier("revanced_toolbar_parent", "id"));
ViewGroup dummyToolbar = toolBarParent.findViewById(getResourceIdentifier(
"revanced_toolbar", "id"));
toolbarLayoutParams = dummyToolbar.getLayoutParams();
toolBarParent.removeView(dummyToolbar);
private static int getToolbarResourceId() {
final int toolbarResourceId = getResourceIdentifier("revanced_toolbar", "id");
if (toolbarResourceId == 0) {
throw new IllegalStateException("Could not find back button resource");
Toolbar toolbar = new Toolbar(toolBarParent.getContext());
toolbar.setBackgroundColor(ThemeHelper.getToolbarBackgroundColor());
toolbar.setNavigationIcon(ReVancedPreferenceFragment.getBackButtonDrawable());
toolbar.setNavigationOnClickListener(view -> activity.onBackPressed());
toolbar.setTitle(getResourceIdentifier(toolbarTitleResourceName, "string"));
final int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16,
Utils.getContext().getResources().getDisplayMetrics());
toolbar.setTitleMarginStart(margin);
toolbar.setTitleMarginEnd(margin);
TextView toolbarTextView = Utils.getChildView(toolbar, false,
view -> view instanceof TextView);
if (toolbarTextView != null) {
toolbarTextView.setTextColor(ThemeHelper.getForegroundColor());
}
return toolbarResourceId;
setToolbarLayoutParams(toolbar);
toolBarParent.addView(toolbar, 0);
}
}

View File

@@ -7,7 +7,10 @@ import static app.revanced.extension.shared.settings.Setting.migrateFromOldPrefe
import static app.revanced.extension.shared.settings.Setting.migrateOldSettingToNew;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.settings.Setting.parentsAny;
import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormFactor;
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
@@ -16,7 +19,7 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_2;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.PHONE;
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.VersionCheckPatch.IS_19_17_OR_GREATER;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
@@ -24,6 +27,8 @@ import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehavi
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
import android.graphics.Color;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
@@ -42,19 +47,24 @@ import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
public class Settings extends BaseSettings {
// Video
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
public static final BooleanSetting RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
// Speed
public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true);
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
public static final BooleanSetting CUSTOM_SPEED_MENU = new BooleanSetting("revanced_custom_speed_menu", TRUE);
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", -2.0f);
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
// Audio
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE, new ForceOriginalAudioAvailability());
// Ads
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE);
public static final BooleanSetting HIDE_END_SCREEN_STORE_BANNER = new BooleanSetting("revanced_hide_end_screen_store_banner", TRUE, true);
public static final BooleanSetting HIDE_FULLSCREEN_ADS = new BooleanSetting("revanced_hide_fullscreen_ads", TRUE);
public static final BooleanSetting HIDE_GENERAL_ADS = new BooleanSetting("revanced_hide_general_ads", TRUE);
public static final BooleanSetting HIDE_GET_PREMIUM = new BooleanSetting("revanced_hide_get_premium", TRUE);
@@ -91,7 +101,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
public static final BooleanSetting HIDE_SEARCH_RESULT_SHELF_HEADER = new BooleanSetting("revanced_hide_search_result_shelf_header", FALSE);
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
// Alternative thumbnails
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
@@ -118,6 +127,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
@@ -137,10 +147,10 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_subscribers_community_guidelines", TRUE);
public static final BooleanSetting HIDE_TIMED_REACTIONS = new BooleanSetting("revanced_hide_timed_reactions", TRUE);
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
// Miniplayer
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.DEFAULT, true);
private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);
@@ -149,7 +159,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting MINIPLAYER_HORIZONTAL_DRAG = new BooleanSetting("revanced_miniplayer_horizontal_drag", FALSE, true, new MiniplayerHorizontalDragAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, new MiniplayerHideExpandCloseAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1));
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, MINIPLAYER_TYPE.availability(MODERN_1));
public static final BooleanSetting MINIPLAYER_ROUNDED_CORNERS = new BooleanSetting("revanced_miniplayer_rounded_corners", TRUE, true, MINIPLAYER_ANY_MODERN);
public static final IntegerSetting MINIPLAYER_WIDTH_DIP = new IntegerSetting("revanced_miniplayer_width_dip", 192, true, MINIPLAYER_ANY_MODERN);
public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1));
@@ -159,6 +169,7 @@ public class Settings extends BaseSettings {
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name",
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
// Comments
public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_summary", FALSE);
public static final BooleanSetting HIDE_COMMENTS_BY_MEMBERS_HEADER = new BooleanSetting("revanced_hide_comments_by_members_header", FALSE);
public static final BooleanSetting HIDE_COMMENTS_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
@@ -168,6 +179,7 @@ public class Settings extends BaseSettings {
// Description
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE);
public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE);
public static final BooleanSetting HIDE_HOW_THIS_WAS_MADE_SECTION = new BooleanSetting("revanced_hide_how_this_was_made_section", FALSE);
public static final BooleanSetting HIDE_INFO_CARDS_SECTION = new BooleanSetting("revanced_hide_info_cards_section", TRUE);
public static final BooleanSetting HIDE_KEY_CONCEPTS_SECTION = new BooleanSetting("revanced_hide_key_concepts_section", FALSE);
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
@@ -197,15 +209,16 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_PLAYER_FLYOUT_WATCH_IN_VR = new BooleanSetting("revanced_hide_player_flyout_watch_in_vr", TRUE);
// General layout
public static final BooleanSetting RESTORE_OLD_SETTINGS_MENUS = new BooleanSetting("revanced_restore_old_settings_menus", FALSE, true);
public static final EnumSetting<FormFactor> CHANGE_FORM_FACTOR = new EnumSetting<>("revanced_change_form_factor", FormFactor.DEFAULT, true, "revanced_change_form_factor_user_dialog_message");
public static final BooleanSetting BYPASS_IMAGE_REGION_RESTRICTIONS = new BooleanSetting("revanced_bypass_image_region_restrictions", FALSE, true);
public static final BooleanSetting GRADIENT_LOADING_SCREEN = new BooleanSetting("revanced_gradient_loading_screen", FALSE, true);
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE,
"revanced_remove_viewer_discretion_dialog_user_dialog_message");
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", IS_19_17_OR_GREATER ? "19.35.36" : "17.33.42", true, parent(SPOOF_APP_VERSION));
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", IS_19_17_OR_GREATER ? "19.26.42" : "17.33.42", true, parent(SPOOF_APP_VERSION));
// Custom filter
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
@@ -216,16 +229,21 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true);
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true);
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true);
// Shorts
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
public static final EnumSetting<ShortsPlayerType> SHORTS_PLAYER_TYPE = new EnumSetting<>("revanced_shorts_player_type", ShortsPlayerType.SHORTS_PLAYER);
public static final BooleanSetting HIDE_SHORTS_CHANNEL_BAR = new BooleanSetting("revanced_hide_shorts_channel_bar", FALSE);
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_FULL_VIDEO_LINK_LABEL = new BooleanSetting("revanced_hide_shorts_full_video_link_label", FALSE);
public static final BooleanSetting HIDE_SHORTS_GREEN_SCREEN_BUTTON = new BooleanSetting("revanced_hide_shorts_green_screen_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_HASHTAG_BUTTON = new BooleanSetting("revanced_hide_shorts_hashtag_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_HISTORY = new BooleanSetting("revanced_hide_shorts_history", FALSE);
public static final BooleanSetting HIDE_SHORTS_HOME = new BooleanSetting("revanced_hide_shorts_home", FALSE);
public static final BooleanSetting HIDE_SHORTS_INFO_PANEL = new BooleanSetting("revanced_hide_shorts_info_panel", TRUE);
public static final BooleanSetting HIDE_SHORTS_JOIN_BUTTON = new BooleanSetting("revanced_hide_shorts_join_button", TRUE);
@@ -254,18 +272,19 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SHORTS_AUTOPLAY_BACKGROUND = new BooleanSetting("revanced_shorts_autoplay_background", TRUE);
// Seekbar
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", TRUE);
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", FALSE);
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true);
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE);
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE, true);
public static final BooleanSetting HIDE_TIMESTAMP = new BooleanSetting("revanced_hide_timestamp", FALSE);
public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE);
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", FALSE);
public static final BooleanSetting SEEKBAR_THUMBNAILS_HIGH_QUALITY = new BooleanSetting("revanced_seekbar_thumbnails_high_quality", FALSE, true,
"revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability());
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
public static final StringSetting SEEKBAR_CUSTOM_COLOR_VALUE = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
public static final StringSetting SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_primary", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
public static final StringSetting SEEKBAR_CUSTOM_COLOR_ACCENT = new StringSetting("revanced_seekbar_custom_color_accent", "#FF2791", true, parent(SEEKBAR_CUSTOM_COLOR));
// Misc
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
@@ -286,8 +305,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, parent(BaseSettings.DEBUG));
// Swipe controls
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", TRUE);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", TRUE);
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE);
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
@@ -387,10 +407,35 @@ public class Settings extends BaseSettings {
}
// Migrate renamed enum.
if (MINIPLAYER_TYPE.get() == PHONE) {
//noinspection deprecation
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {
MINIPLAYER_TYPE.save(MINIMAL);
}
// Migrate old single color seekbar with a slightly brighter accent color based on the primary.
// Eventually delete this logic.
if (!DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.isSetToDefault()) {
try {
String oldPrimaryColorString = DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.get();
final int oldPrimaryColor = Color.parseColor(oldPrimaryColorString);
SEEKBAR_CUSTOM_COLOR_PRIMARY.save(oldPrimaryColorString);
final float brightnessScale = 1.3f;
final int accentColor = Color.argb(
0, // Save without alpha channel.
Math.min(255, (int) (brightnessScale * Color.red(oldPrimaryColor))),
Math.min(255, (int) (brightnessScale * Color.green(oldPrimaryColor))),
Math.min(255, (int) (brightnessScale * Color.blue(oldPrimaryColor)))
);
SEEKBAR_CUSTOM_COLOR_ACCENT.save(String.format("#%06X", accentColor));
} catch (Exception ex) {
Logger.printException(() -> "Could not parse old seekbar color", ex);
}
DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.resetToDefault();
}
// endregion
// 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.youtube.patches.ForceOriginalAudioPatch;
@SuppressWarnings({"deprecation", "unused"})
public class ForceOriginalAudioSwitchPreference extends SwitchPreference {
{
if (!ForceOriginalAudioPatch.PATCH_AVAILABLE) {
// Show why force audio is not available.
String summary = str("revanced_force_original_audio_not_available");
setSummary(summary);
setSummaryOn(summary);
setSummaryOff(summary);
}
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ForceOriginalAudioSwitchPreference(Context context) {
super(context);
}
}

View File

@@ -25,9 +25,12 @@ import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.EnumSetting;
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.LicenseActivityHook;
import app.revanced.extension.youtube.settings.Settings;
/**
@@ -48,35 +51,44 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
/**
* Sorts a preference list by menu entries, but preserves the first value as the first entry.
*
* @noinspection SameParameterValue
*/
private static void sortListPreferenceByValues(ListPreference listPreference) {
private static void sortListPreferenceByValues(ListPreference listPreference, int firstEntriesToPreserve) {
CharSequence[] entries = listPreference.getEntries();
CharSequence[] entryValues = listPreference.getEntryValues();
final int entrySize = entries.length;
if (entrySize != entryValues.length) {
// Xml array declaration has a missing/extra entry.
throw new IllegalStateException();
}
// Ensure the first entry remains the first after sorting.
CharSequence firstEntry = entries[0];
CharSequence firstEntryValue = entryValues[0];
List<Pair<String, String>> firstPairs = new ArrayList<>(firstEntriesToPreserve);
List<Pair<String, String>> pairsToSort = new ArrayList<>(entrySize);
List<Pair<String, String>> entryPairs = new ArrayList<>(entrySize);
for (int i = 1; i < entrySize; i++) {
entryPairs.add(new Pair<>(entries[i].toString(), entryValues[i].toString()));
for (int i = 0; i < entrySize; i++) {
Pair<String, String> pair = new Pair<>(entries[i].toString(), entryValues[i].toString());
if (i < firstEntriesToPreserve) {
firstPairs.add(pair);
} else {
pairsToSort.add(pair);
}
}
Collections.sort(entryPairs, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
Collections.sort(pairsToSort, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
CharSequence[] sortedEntries = new CharSequence[entrySize];
CharSequence[] sortedEntryValues = new CharSequence[entrySize];
sortedEntries[0] = firstEntry;
sortedEntryValues[0] = firstEntryValue;
int i = 0;
for (Pair<String, String> pair : firstPairs) {
sortedEntries[i] = pair.first;
sortedEntryValues[i] = pair.second;
i++;
}
int i = 1;
for (Pair<String, String> pair : entryPairs) {
for (Pair<String, String> pair : pairsToSort) {
sortedEntries[i] = pair.first;
sortedEntryValues[i] = pair.second;
i++;
@@ -100,15 +112,20 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
}
preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference);
}
sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE);
sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
}
private void sortPreferenceListMenu(EnumSetting<?> setting) {
Preference preference = findPreference(setting.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference, 1);
}
}
private void setPreferenceScreenToolbar(PreferenceScreen parentScreen) {
for (int i = 0, preferenceCount = parentScreen.getPreferenceCount(); i < preferenceCount; i++) {
Preference childPreference = parentScreen.getPreference(i);
@@ -124,9 +141,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
.getParent();
// Fix required for Android 15 and YT 19.45+
// FIXME:
// On Android 15 the text layout is not aligned the same as the parent
// screen and it looks a little off. Otherwise this works.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
rootView.setOnApplyWindowInsetsListener((v, insets) -> {
Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars());
@@ -153,6 +167,8 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
toolbarTextView.setTextColor(ThemeHelper.getForegroundColor());
}
LicenseActivityHook.setToolbarLayoutParams(toolbar);
rootView.addView(toolbar, 0);
return false;
}

View File

@@ -507,7 +507,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
Utils.showToastLong(str("revanced_sb_stats_username_changed"));
} else {
preference.setText(userName); // revert to previous
Utils.showToastLong(errorMessage);
SponsorBlockUtils.showErrorDialog(errorMessage);
}
});
});

View File

@@ -77,9 +77,19 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
Logger.printDebug(() -> "Updating spoof stream side effects preference");
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
String key = "revanced_spoof_video_streams_about_"
+ clientType.name().toLowerCase();
setTitle(str(key + "_title"));
setSummary(str(key + "_summary"));
String key = "revanced_spoof_video_streams_about_" +
(clientType == ClientType.IOS_UNPLUGGED
? "ios_tv"
: "android");
String title = str(key + "_title");
String summary = str(key + "_summary");
// Android VR supports AV1 but all other clients do not.
if (clientType != ClientType.ANDROID_VR && clientType != ClientType.ANDROID_VR_NO_AUTH) {
summary += '\n' + str("revanced_spoof_video_streams_about_no_av1");
}
setTitle(title);
setSummary(summary);
}
}

View File

@@ -3,12 +3,15 @@ package app.revanced.extension.youtube.shared;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
import android.app.Activity;
import android.os.Build;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -54,20 +57,21 @@ public final class NavigationBar {
* How long to wait for the set nav button latch to be released. Maximum wait time must
* be as small as possible while still allowing enough time for the nav bar to update.
*
* YT calls it's back button handlers out of order,
* and litho starts filtering before the navigation bar is updated.
* YT calls it's back button handlers out of order, and litho starts filtering before the
* navigation bar is updated. Fixing this situation and not needlessly waiting requires
* somehow detecting if a back button key/gesture will not change the active tab.
*
* Fixing this situation and not needlessly waiting requires somehow
* detecting if a back button key-press will cause a tab change.
* On average the time between pressing the back button and the first litho event is
* about 10-20ms. Waiting up to 75-150ms should be enough time to handle normal use cases
* and not be noticeable, since YT typically takes 100-200ms (or more) to update the view.
*
* Typically after pressing the back button, the time between the first litho event and
* when the nav button is updated is about 10-20ms. Using 50-100ms here should be enough time
* and not noticeable, since YT typically takes 100-200ms (or more) to update the view anyways.
* This delay is only noticeable when the device back button/gesture will not
* change the current navigation tab, such as backing out of the watch history.
*
* This issue can also be avoided on a patch by patch basis, by avoiding calls to
* {@link NavigationButton#getSelectedNavigationButton()} unless absolutely necessary.
*/
private static final long LATCH_AWAIT_TIMEOUT_MILLISECONDS = 75;
private static final long LATCH_AWAIT_TIMEOUT_MILLISECONDS = 120;
/**
* Used as a workaround to fix the issue of YT calling back button handlers out of order.
@@ -113,7 +117,8 @@ public final class NavigationBar {
// The latch is released from the main thread, and waiting from the main thread will always timeout.
// This situation has only been observed when navigating out of a submenu and not changing tabs.
// and for that use case the nav bar does not change so it's safe to return here.
Logger.printDebug(() -> "Cannot block main thread waiting for nav button. Using last known navbar button status.");
Logger.printDebug(() -> "Cannot block main thread waiting for nav button. " +
"Using last known navbar button status.");
return;
}
@@ -131,7 +136,9 @@ public final class NavigationBar {
Logger.printDebug(() -> "Latch wait timed out");
} catch (InterruptedException ex) {
Logger.printException(() -> "Latch wait interrupted failure", ex); // Will never happen.
// Calling YouTube thread was interrupted.
Logger.printException(() -> "Latch wait interrupted", ex);
Thread.currentThread().interrupt(); // Restore interrupt status flag.
}
}
@@ -238,6 +245,30 @@ public final class NavigationBar {
// Code is added during patching.
}
/**
* Use the bundled non cairo filled icon instead of a custom icon.
* Use the old non cairo filled icon, which is almost identical to
* the what would be the filled cairo icon.
*/
private static final int fillBellCairoBlack = Utils.getResourceIdentifier(
"yt_fill_bell_black_24", "drawable");
/**
* Injection point.
* Fixes missing drawable.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressWarnings({"unchecked", "rawtypes"})
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
if (fillBellCairoBlack != 0) {
// Show a popup informing this fix is no longer needed to those who might care.
if (BaseSettings.DEBUG.get() && enumMap.containsKey(tabActivityCairo)) {
Logger.printException(() -> "YouTube fixed the cairo notification icons");
}
enumMap.putIfAbsent(tabActivityCairo, fillBellCairoBlack);
}
}
public enum NavigationButton {
HOME("PIVOT_HOME", "TAB_HOME_CAIRO"),
SHORTS("TAB_SHORTS", "TAB_SHORTS_CAIRO"),
@@ -246,6 +277,10 @@ public final class NavigationBar {
* This tab will never be in a selected state, even if the create video UI is on screen.
*/
CREATE("CREATION_TAB_LARGE", "CREATION_TAB_LARGE_CAIRO"),
/**
* Only shown to automotive layout.
*/
EXPLORE("TAB_EXPLORE"),
SUBSCRIPTIONS("PIVOT_SUBSCRIPTIONS", "TAB_SUBSCRIPTIONS_CAIRO"),
/**
* Notifications tab. Only present when
@@ -283,8 +318,8 @@ public final class NavigationBar {
*
* All code calling this method should handle a null return value.
*
* <b>Due to issues with how YT processes physical back button events,
* this patch uses workarounds that can cause this method to take up to 75ms
* <b>Due to issues with how YT processes physical back button/gesture events,
* this patch uses workarounds that can cause this method to take up to 120ms
* if the device back button was recently pressed.</b>
*
* @return The active navigation tab.

View File

@@ -73,7 +73,7 @@ enum class PlayerType {
onChange(currentPlayerType)
}
@Volatile // value is read/write from different threads
@Volatile // Read/write from different threads.
private var currentPlayerType = NONE
/**

View File

@@ -46,6 +46,7 @@ enum class VideoState {
currentVideoState = value
}
@Volatile // Read/write from different threads.
private var currentVideoState: VideoState? = null
}
}

View File

@@ -363,6 +363,16 @@ public class SponsorBlockUtils {
}
}
public static void showErrorDialog(String dialogMessage) {
Utils.runOnMainThreadNowOrLater(() ->
new AlertDialog.Builder(SponsorBlockViewController.getOverLaysViewGroupContext())
.setMessage(dialogMessage)
.setPositiveButton(android.R.string.ok, null)
.setCancelable(false)
.show()
);
}
public static void onEditByHandClicked() {
try {
Utils.verifyOnMainThread();

View File

@@ -19,6 +19,7 @@ import java.util.concurrent.TimeUnit;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
import app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment.SegmentVote;
@@ -142,44 +143,44 @@ public class SBRequester {
public static void submitSegments(@NonNull String videoId, @NonNull String category,
long startTime, long endTime, long videoLength) {
Utils.verifyOffMainThread();
try {
String privateUserId = SponsorBlockSettings.getSBPrivateUserID();
String start = String.format(Locale.US, TIME_TEMPLATE, startTime / 1000f);
String end = String.format(Locale.US, TIME_TEMPLATE, endTime / 1000f);
String duration = String.format(Locale.US, TIME_TEMPLATE, videoLength / 1000f);
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, privateUserId, videoId, category, start, end, duration);
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS,
privateUserId, videoId, category, start, end, duration);
final int responseCode = connection.getResponseCode();
final String messageToToast;
switch (responseCode) {
case HTTP_STATUS_CODE_SUCCESS:
messageToToast = str("revanced_sb_submit_succeeded");
break;
case 409:
messageToToast = str("revanced_sb_submit_failed_duplicate");
break;
case 403:
messageToToast = str("revanced_sb_submit_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection));
break;
case 429:
messageToToast = str("revanced_sb_submit_failed_rate_limit");
break;
case 400:
messageToToast = str("revanced_sb_submit_failed_invalid", Requester.parseErrorStringAndDisconnect(connection));
break;
default:
messageToToast = str("revanced_sb_submit_failed_unknown_error", responseCode, connection.getResponseMessage());
break;
if (responseCode == HTTP_STATUS_CODE_SUCCESS) {
Utils.showToastLong(str("revanced_sb_submit_succeeded"));
return;
}
Utils.showToastLong(messageToToast);
String userErrorMessage = switch (responseCode) {
case 409 -> str("revanced_sb_submit_failed_duplicate");
case 403 -> str("revanced_sb_submit_failed_forbidden",
Requester.parseErrorStringAndDisconnect(connection));
case 429 -> str("revanced_sb_submit_failed_rate_limit");
case 400 -> str("revanced_sb_submit_failed_invalid",
Requester.parseErrorStringAndDisconnect(connection));
default -> str("revanced_sb_submit_failed_unknown_error",
responseCode, connection.getResponseMessage());
};
// Message might be about the users account or an error too large to show in a toast.
// Use a dialog instead.
SponsorBlockUtils.showErrorDialog(userErrorMessage);
} catch (SocketTimeoutException ex) {
// Always show, even if show connection toasts is turned off
Logger.printDebug(() -> "Timeout", ex);
Utils.showToastLong(str("revanced_sb_submit_failed_timeout"));
} catch (IOException ex) {
Logger.printDebug(() -> "IOException", ex);
Utils.showToastLong(str("revanced_sb_submit_failed_unknown_error", 0, ex.getMessage()));
} catch (Exception ex) {
Logger.printException(() -> "failed to submit segments", ex);
Logger.printException(() -> "failed to submit segments", ex); // Should never happen.
}
}
@@ -218,19 +219,22 @@ public class SBRequester {
: getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, uuid, segmentUuid, String.valueOf(voteOption.apiVoteType));
final int responseCode = connection.getResponseCode();
String userMessage;
switch (responseCode) {
case HTTP_STATUS_CODE_SUCCESS:
Logger.printDebug(() -> "Vote success for segment: " + segment);
break;
return;
case 403:
Utils.showToastLong(
str("revanced_sb_vote_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection)));
userMessage = str("revanced_sb_vote_failed_forbidden",
Requester.parseErrorStringAndDisconnect(connection));
break;
default:
Utils.showToastLong(
str("revanced_sb_vote_failed_unknown_error", responseCode, connection.getResponseMessage()));
userMessage = str("revanced_sb_vote_failed_unknown_error",
responseCode, connection.getResponseMessage());
break;
}
SponsorBlockUtils.showErrorDialog(userMessage);
} catch (SocketTimeoutException ex) {
Utils.showToastShort(str("revanced_sb_vote_failed_timeout"));
} catch (IOException ex) {

View File

@@ -8,6 +8,7 @@ import android.view.MotionEvent
import android.view.ViewGroup
import app.revanced.extension.shared.Logger.printDebug
import app.revanced.extension.shared.Logger.printException
import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerType
import app.revanced.extension.youtube.swipecontrols.controller.AudioVolumeController
import app.revanced.extension.youtube.swipecontrols.controller.ScreenBrightnessController
@@ -232,5 +233,12 @@ class SwipeControlsHostActivity : Activity() {
@JvmStatic
var currentHost: WeakReference<SwipeControlsHostActivity> = WeakReference(null)
private set
/**
* Injection point.
*/
@Suppress("unused")
@JvmStatic
fun allowSwipeChangeVideo(original: Boolean): Boolean = Settings.SWIPE_CHANGE_VIDEO.get()
}
}

View File

@@ -1,5 +0,0 @@
package com.google.protos.youtube.api.innertube;
public class InnertubeContext$ClientInfo {
public int r;
}

View File

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

View File

@@ -324,8 +324,12 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt {
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatchKt {
public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public final class app/revanced/patches/music/misc/spoof/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPatchKt {
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
@@ -457,10 +461,6 @@ public final class app/revanced/patches/reddit/customclients/joeyforreddit/detec
public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/redditisfun/api/FingerprintsKt {
public static final fun baseClientIdFingerprint (Ljava/lang/String;)Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -544,7 +544,6 @@ public final class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentP
}
public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
public final fun getFingerprint ()Lapp/revanced/patcher/Fingerprint;
public final fun invoke (Lapp/revanced/patcher/patch/BytecodePatchContext;Ljava/lang/String;)V
}
@@ -604,16 +603,19 @@ public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatch
}
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
public static synthetic fun settingsPatch$default (Lkotlin/Pair;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
public static synthetic fun settingsPatch$default (Ljava/util/List;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
}
public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreference {
public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getIcon ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
public final fun getLayout ()Ljava/lang/String;
public final fun getSummaryKey ()Ljava/lang/String;
public final fun getTag ()Ljava/lang/String;
public final fun getTitleKey ()Ljava/lang/String;
@@ -636,17 +638,19 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP
public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getIcon ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
public final fun getLayout ()Ljava/lang/String;
public final fun getPreferences ()Ljava/util/Set;
public final fun getTitleKey ()Ljava/lang/String;
public abstract fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
}
public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V
public final fun getCategories ()Ljava/util/Set;
public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
@@ -654,8 +658,8 @@ public class app/revanced/patches/shared/misc/settings/preference/BasePreference
}
public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen$Category : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V
public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceCategory;
@@ -663,6 +667,7 @@ public class app/revanced/patches/shared/misc/settings/preference/BasePreference
public final class app/revanced/patches/shared/misc/settings/preference/InputType : java/lang/Enum {
public static final field NUMBER Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field NUMBER_DECIMAL Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT_CAP_CHARACTERS Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT_MULTI_LINE Lapp/revanced/patches/shared/misc/settings/preference/InputType;
@@ -673,8 +678,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/InputTyp
}
public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getIntent ()Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
public fun hashCode ()I
@@ -694,8 +699,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getEntries ()Lapp/revanced/util/resource/ArrayResource;
public final fun getEntriesKey ()Ljava/lang/String;
public final fun getEntryValues ()Lapp/revanced/util/resource/ArrayResource;
@@ -704,22 +709,22 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
}
public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSelectable ()Z
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
public class app/revanced/patches/shared/misc/settings/preference/PreferenceCategory : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getPreferences ()Ljava/util/Set;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getPreferences ()Ljava/util/Set;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
@@ -728,6 +733,7 @@ public final class app/revanced/patches/shared/misc/settings/preference/Preferen
public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public final fun appendSortType (Ljava/lang/String;)Ljava/lang/String;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public final fun getKeySuffix ()Ljava/lang/String;
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
@@ -746,8 +752,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SummaryT
public final class app/revanced/patches/shared/misc/settings/preference/SwitchPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSummaryOffKey ()Ljava/lang/String;
public final fun getSummaryOnKey ()Ljava/lang/String;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
@@ -755,8 +761,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SwitchPr
public final class app/revanced/patches/shared/misc/settings/preference/TextPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getInputType ()Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
@@ -766,6 +772,10 @@ public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt {
public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1098,6 +1108,10 @@ public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlaye
public static final fun getHidePlayerOverlayButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/formfactor/ChangeFormFactorPatchKt {
public static final fun getChangeFormFactorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatchKt {
public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1169,6 +1183,10 @@ public final class app/revanced/patches/youtube/layout/player/background/PlayerC
}
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenKt {
public static final fun getOpenVideosFullscreen ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatchKt {
public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1202,6 +1220,10 @@ public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAuto
public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatchKt {
public static final fun getOpenShortsInRegularPlayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt {
public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1219,7 +1241,6 @@ public final class app/revanced/patches/youtube/layout/startupshortsreset/Disabl
}
public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatchKt {
public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
public static final fun getEnableTabletLayoutPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1252,10 +1273,6 @@ public final class app/revanced/patches/youtube/misc/backgroundplayback/Backgrou
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatchKt {
public static final fun getCheckEnvironmentPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt {
public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1280,6 +1297,10 @@ public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClien
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatchKt {
public static final fun getFixPlaybackSpeedWhilePlayingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatchKt {
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1344,6 +1365,7 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
public static final fun is_19_43_or_greater ()Z
public static final fun is_19_46_or_greater ()Z
public static final fun is_19_47_or_greater ()Z
public static final fun is_19_49_or_greater ()Z
}
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
@@ -1388,8 +1410,12 @@ public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatc
public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/shared/FingerprintsKt {
public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt {
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/hdr/DisableHdrPatchKt {
public static final fun getDisableHdrPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt {
@@ -1433,10 +1459,6 @@ public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeed
public static final fun getPlaybackSpeedButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatchKt {
public static final fun getSpeedUnavailableId ()J
}
public final class app/revanced/patches/youtube/video/videoid/VideoIdPatchKt {
public static final fun getVideoIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun hookBackgroundPlayVideoId (Ljava/lang/String;)V

View File

@@ -1,22 +1,16 @@
package app.revanced.patches.music.misc.androidauto
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val bypassCertificateChecksPatch = bytecodePatch(
name = "Bypass certificate checks",
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
) {
compatibleWith("com.google.android.apps.youtube.music")
compatibleWith("com.google.android.apps.youtube.music"("7.29.52"))
execute {
checkCertificateFingerprint.method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
)
checkCertificateFingerprint.method.returnEarly(true)
}
}

View File

@@ -4,5 +4,6 @@ import app.revanced.patches.music.misc.extension.hooks.applicationInitHook
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch(
"music",
applicationInitHook,
)

View File

@@ -4,7 +4,7 @@ import app.revanced.patcher.patch.Option
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch
import app.revanced.patches.music.misc.spoof.spoofClientPatch
import app.revanced.patches.shared.castContextFetchFingerprint
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
import app.revanced.patches.shared.primeMethodFingerprint
@@ -21,7 +21,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
extensionPatch = sharedExtensionPatch,
gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
) {
dependsOn(spoofVideoStreamsPatch)
dependsOn(spoofClientPatch)
compatibleWith(MUSIC_PACKAGE_NAME)
}

View File

@@ -0,0 +1,39 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val playerRequestConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
strings("player")
}
/**
* Matches using the class found in [playerRequestConstructorFingerprint].
*/
internal val createPlayerRequestBodyFingerprint = fingerprint {
parameters("L")
returns("V")
opcodes(
Opcode.CHECK_CAST,
Opcode.IGET,
Opcode.AND_INT_LIT16,
)
strings("ms")
}
/**
* Used to get a reference to other clientInfo fields.
*/
internal val setClientInfoFieldsFingerprint = fingerprint {
returns("L")
strings("Google Inc.")
}
/**
* Used to get a reference to the clientInfo and clientInfo.clientVersion field.
*/
internal val setClientInfoClientVersionFingerprint = fingerprint {
strings("10.29")
}

View File

@@ -0,0 +1,105 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.
val spoofClientPatch = bytecodePatch(
name = "Spoof client",
description = "Spoofs the client to fix playback.",
) {
compatibleWith("com.google.android.apps.youtube.music")
dependsOn(
sharedExtensionPatch,
// TODO: Add settingsPatch
userAgentClientSpoofPatch,
)
execute {
val playerRequestClass = playerRequestConstructorFingerprint.classDef
val createPlayerRequestBodyMatch = createPlayerRequestBodyFingerprint.match(playerRequestClass)
val clientInfoContainerClass = createPlayerRequestBodyMatch.method
.getInstruction(createPlayerRequestBodyMatch.patternMatch!!.startIndex)
.getReference<TypeReference>()!!.type
val clientInfoField = setClientInfoClientVersionFingerprint.method.instructions.first {
it.opcode == Opcode.IPUT_OBJECT && it.getReference<FieldReference>()!!.definingClass == clientInfoContainerClass
}.getReference<FieldReference>()!!
val setClientInfoFieldInstructions = setClientInfoFieldsFingerprint.method.instructions.filter {
(it.opcode == Opcode.IPUT_OBJECT || it.opcode == Opcode.IPUT) &&
it.getReference<FieldReference>()!!.definingClass == clientInfoField.type
}.map { it.getReference<FieldReference>()!! }
// Offsets are known for the fields in the clientInfo object.
val clientIdField = setClientInfoFieldInstructions[0]
val clientModelField = setClientInfoFieldInstructions[5]
val osVersionField = setClientInfoFieldInstructions[7]
val clientVersionField = setClientInfoClientVersionFingerprint.method
.getInstruction(setClientInfoClientVersionFingerprint.stringMatches!!.first().index + 1)
.getReference<FieldReference>()
// Helper method to spoof the client info.
val spoofClientInfoMethod = ImmutableMethod(
playerRequestClass.type,
"spoofClientInfo",
listOf(ImmutableMethodParameter(clientInfoContainerClass, null, null)),
"V",
AccessFlags.PRIVATE.value or AccessFlags.STATIC.value,
null,
null,
MutableMethodImplementation(3),
).toMutable().also(playerRequestClass.methods::add).apply {
addInstructions(
"""
iget-object v0, p0, $clientInfoField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientId()I
move-result v1
iput v1, v0, $clientIdField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientModel()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientModelField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientVersion()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientVersionField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getOsVersion()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $osVersionField
return-void
""",
)
}
createPlayerRequestBodyMatch.method.apply {
val checkCastIndex = createPlayerRequestBodyMatch.patternMatch!!.startIndex
val clientInfoContainerRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(checkCastIndex + 1, "invoke-static {v$clientInfoContainerRegister}, $spoofClientInfoMethod")
}
}
}

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
compatibleWith("com.google.android.apps.youtube.music")
})

View File

@@ -0,0 +1,5 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.apps.youtube.music")

View File

@@ -4,7 +4,7 @@ import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
fun baseClientIdFingerprint(string: String) = fingerprint {
internal fun baseClientIdFingerprint(string: String) = fingerprint {
strings("yyOCBp.RHJhDKd", string)
}

View File

@@ -2,7 +2,7 @@ package app.revanced.patches.shared.misc.checks
import android.os.Build.*
import app.revanced.patcher.Fingerprint
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
@@ -82,7 +82,7 @@ fun checkEnvironmentPatch(
}
}
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstructions(
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstruction(
0,
"invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
)

View File

@@ -92,7 +92,7 @@ fun sharedExtensionPatch(
}
class ExtensionHook internal constructor(
val fingerprint: Fingerprint,
private val fingerprint: Fingerprint,
private val insertIndexResolver: ((Method) -> Int),
private val contextRegisterResolver: (Method) -> String,
) {

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.shared.misc.settings
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResource
import app.revanced.patches.all.misc.resources.addResources
@@ -12,15 +13,22 @@ import app.revanced.util.getNode
import app.revanced.util.insertFirst
import org.w3c.dom.Node
// TODO: Delete this on next major version bump.
@Deprecated("Use non deprecated settings patch function")
fun settingsPatch (
rootPreference: Pair<IntentPreference, String>,
preferences: Set<BasePreference>,
) = settingsPatch(listOf(rootPreference), preferences)
/**
* A resource patch that adds settings to a settings fragment.
*
* @param rootPreference A pair of an intent preference and the name of the fragment file to add it to.
* If null, no preference will be added.
* @param rootPreferences List of intent preferences and the name of the fragment file to add it to.
* File names that do not exist are ignored and not processed.
* @param preferences A set of preferences to add to the ReVanced fragment.
*/
fun settingsPatch(
rootPreference: Pair<IntentPreference, String>? = null,
fun settingsPatch (
rootPreferences: List<Pair<BasePreference, String>>? = null,
preferences: Set<BasePreference>,
) = resourcePatch {
dependsOn(addResourcesPatch)
@@ -46,10 +54,20 @@ fun settingsPatch(
}
// Add the root preference to an existing fragment if needed.
rootPreference?.let { (intentPreference, fragment) ->
document("res/xml/$fragment.xml").use { document ->
document.getNode("PreferenceScreen").addPreference(intentPreference, true)
rootPreferences?.let {
var modified = false
it.forEach { (intent, fileName) ->
val preferenceFileName = "res/xml/$fileName.xml"
if (get(preferenceFileName).exists()) {
document(preferenceFileName).use { document ->
document.getNode("PreferenceScreen").addPreference(intent, true)
}
modified = true
}
}
if (!modified) throw PatchException("No declared preference files exists: $rootPreferences")
}
// Add all preferences to the ReVanced fragment.

View File

@@ -9,6 +9,8 @@ import org.w3c.dom.Element
*
* @param key The key of the preference. If null, other parameters must be specified.
* @param titleKey The key of the preference title.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param summaryKey The key of the preference summary.
* @param tag The tag or full class name of the preference.
*/
@@ -17,6 +19,8 @@ abstract class BasePreference(
val key: String? = null,
val titleKey: String = "${key}_title",
val summaryKey: String? = "${key}_summary",
val icon: String? = null,
val layout: String? = null,
val tag: String
) {
/**
@@ -33,6 +37,11 @@ abstract class BasePreference(
key?.let { setAttribute("android:key", it) }
setAttribute("android:title", "@string/${titleKey}")
summaryKey?.let { addSummary(it) }
icon?.let {
setAttribute("android:icon", it)
setAttribute("app:iconSpaceReserved", "true")
}
layout?.let { setAttribute("android:layout", layout) }
}
override fun hashCode(): Int {

View File

@@ -24,16 +24,20 @@ abstract class BasePreferenceScreen(
key: String? = null,
titleKey: String = "${key}_title",
private val summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
preferences: MutableSet<BasePreference> = mutableSetOf(),
val categories: MutableSet<Category> = mutableSetOf(),
private val sorting: Sorting = Sorting.BY_TITLE,
) : BasePreferenceCollection(key, titleKey, preferences) {
) : BasePreferenceCollection(key, titleKey, icon, layout, preferences) {
override fun transform(): PreferenceScreenPreference {
return PreferenceScreenPreference(
key,
titleKey,
summaryKey,
icon,
layout,
sorting,
// Screens and preferences are sorted at runtime by extension code,
// so title sorting uses the localized language in use.
@@ -56,12 +60,17 @@ abstract class BasePreferenceScreen(
open inner class Category(
key: String? = null,
titleKey: String = "${key}_title",
icon: String? = null,
layout: String? = null,
preferences: MutableSet<BasePreference> = mutableSetOf(),
) : BasePreferenceCollection(key, titleKey, preferences) {
) : BasePreferenceCollection(key, titleKey, icon, layout, preferences) {
override fun transform(): PreferenceCategory {
return PreferenceCategory(
key,
titleKey,
icon,
layout,
sorting,
preferences = preferences,
)
}
@@ -82,6 +91,8 @@ abstract class BasePreferenceScreen(
abstract class BasePreferenceCollection(
val key: String? = null,
val titleKey: String = "${key}_title",
val icon: String? = null,
val layout: String? = null,
val preferences: MutableSet<BasePreference> = mutableSetOf(),
) {
abstract fun transform(): BasePreference

View File

@@ -5,4 +5,5 @@ enum class InputType(val type: String) {
TEXT_CAP_CHARACTERS("textCapCharacters"),
TEXT_MULTI_LINE("textMultiLine"),
NUMBER("number"),
NUMBER_DECIMAL("numberDecimal"),
}

View File

@@ -9,6 +9,8 @@ import org.w3c.dom.Document
* @param key Optional preference key.
* @param titleKey The preference title key.
* @param summaryKey The preference summary key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The preference tag.
* @param intent The intent to open.
*/
@@ -16,9 +18,11 @@ class IntentPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
tag: String = "Preference",
val intent: Intent,
) : BasePreference(key, titleKey, summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {

View File

@@ -10,6 +10,8 @@ import org.w3c.dom.Document
* @param key The preference key. If null, other parameters must be specified.
* @param titleKey The preference title key.
* @param summaryKey The preference summary key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The preference tag.
* @param entriesKey The entries array key.
* @param entryValuesKey The entry values array key.
@@ -19,10 +21,12 @@ class ListPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
tag: String = "ListPreference",
val entriesKey: String? = "${key}_entries",
val entryValuesKey: String? = "${key}_entry_values"
) : BasePreference(key, titleKey, summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
var entries: ArrayResource? = null
private set
var entryValues: ArrayResource? = null

View File

@@ -10,6 +10,8 @@ import org.w3c.dom.Document
*
* @param key The preference key.
* @param summaryKey The preference summary key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The tag or full class name of the preference.
* @param selectable If the preference is selectable and responds to tap events.
*/
@@ -18,9 +20,11 @@ class NonInteractivePreference(
key: String,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
tag: String = "Preference",
val selectable: Boolean = false,
) : BasePreference(key, titleKey, summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {
setAttribute("android:selectable", selectable.toString())

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.shared.misc.settings.preference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.util.resource.BaseResource
import org.w3c.dom.Document
@@ -8,6 +9,8 @@ import org.w3c.dom.Document
*
* @param key The key of the preference. If null, other parameters must be specified.
* @param titleKey The key of the preference title.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The tag or full class name of the preference.
* @param preferences The preferences in this category.
*/
@@ -15,9 +18,12 @@ import org.w3c.dom.Document
open class PreferenceCategory(
key: String? = null,
titleKey: String = "${key}_title",
icon: String? = null,
layout: String? = null,
sorting: Sorting = Sorting.BY_TITLE,
tag: String = "PreferenceCategory",
val preferences: Set<BasePreference>
) : BasePreference(key, titleKey, null, tag) {
) : BasePreference(sorting.appendSortType(key), titleKey, null, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {

View File

@@ -9,6 +9,8 @@ import org.w3c.dom.Document
* @param key The key of the preference. If null, other parameters must be specified.
* @param titleKey The key of the preference title.
* @param summaryKey The key of the preference summary.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param sorting Sorting to use. If the sorting is not [Sorting.UNSORTED],
* then the key parameter will be modified to include the sort type.
* @param tag The tag or full class name of the preference.
@@ -19,6 +21,8 @@ open class PreferenceScreenPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
sorting: Sorting = Sorting.BY_TITLE,
tag: String = "PreferenceScreen",
val preferences: Set<BasePreference>,
@@ -28,7 +32,7 @@ open class PreferenceScreenPreference(
// or adding new attributes to the attrs.xml file.
// Since the key value is not currently used by the extensions,
// for now it's much simpler to modify the key to include the sort parameter.
) : BasePreference(if (sorting == Sorting.UNSORTED) key else (key + sorting.keySuffix), titleKey, summaryKey, tag) {
) : BasePreference(sorting.appendSortType(key), titleKey, summaryKey, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {
preferences.forEach {
@@ -53,6 +57,16 @@ open class PreferenceScreenPreference(
/**
* Unspecified sorting.
*/
UNSORTED("_sort_by_unsorted"),
UNSORTED("_sort_by_unsorted");
/**
* @return The key with this sort type appended to to the end,
* or if key is null then null is returned.
*/
fun appendSortType(key: String?): String? {
if (key == null) return null
if (this == UNSORTED) return key
return key + keySuffix
}
}
}

View File

@@ -8,6 +8,8 @@ import org.w3c.dom.Document
*
* @param key The preference key. If null, other parameters must be specified.
* @param titleKey The preference title key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The preference tag.
* @param summaryOnKey The preference summary-on key.
* @param summaryOffKey The preference summary-off key.
@@ -17,9 +19,11 @@ class SwitchPreference(
key: String? = null,
titleKey: String = "${key}_title",
tag: String = "SwitchPreference",
icon: String? = null,
layout: String? = null,
val summaryOnKey: String = "${key}_summary_on",
val summaryOffKey: String = "${key}_summary_off"
) : BasePreference(key, titleKey, null, tag) {
) : BasePreference(key, titleKey, null, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {
addSummary(summaryOnKey, SummaryType.ON)

View File

@@ -9,6 +9,8 @@ import org.w3c.dom.Document
* @param key The preference key. If null, other parameters must be specified.
* @param titleKey The preference title key.
* @param summaryKey The preference summary key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The preference tag.
* @param inputType The preference input type.
*/
@@ -17,9 +19,11 @@ class TextPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
tag: String = "app.revanced.extension.shared.settings.preference.ResettableEditTextPreference",
val inputType: InputType = InputType.TEXT
) : BasePreference(key, titleKey, summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -110,3 +111,29 @@ internal val buildMediaDataSourceFingerprint = fingerprint {
"Ljava/lang/Object;",
)
}
internal const val HLS_CURRENT_TIME_FEATURE_FLAG = 45355374L
internal val hlsCurrentTimeFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("Z", "L")
literal {
HLS_CURRENT_TIME_FEATURE_FLAG
}
}
internal val nerdsStatsVideoFormatBuilderFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Ljava/lang/String;")
parameters("L")
strings("codecs=\"")
}
internal val patchIncludedExtensionMethodFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("Z")
parameters()
custom { method, classDef ->
classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded"
}
}

View File

@@ -10,8 +10,11 @@ import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertFeatureFlagBooleanOverride
import app.revanced.util.returnEarly
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
@@ -23,7 +26,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
private const val EXTENSION_CLASS_DESCRIPTOR =
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/shared/spoof/SpoofVideoStreamsPatch;"
fun spoofVideoStreamsPatch(
@@ -38,6 +41,12 @@ fun spoofVideoStreamsPatch(
dependsOn(addResourcesPatch)
execute {
// region Enable extension helper method used by other patches
patchIncludedExtensionMethodFingerprint.method.returnEarly(true)
// endregion
// region Block /initplayback requests to fall back to /get_watch requests.
val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex
@@ -199,6 +208,34 @@ fun spoofVideoStreamsPatch(
""",
)
}
// endregion
// region Append spoof info.
nerdsStatsVideoFormatBuilderFingerprint.method.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->appendSpoofedClient(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$register
"""
)
}
}
// endregion
// region Fix iOS livestream current time.
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride(
HLS_CURRENT_TIME_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
)
// endregion
executeBlock()

View File

@@ -0,0 +1,81 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
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
import com.android.tools.smali.dexlib2.iface.reference.StringReference
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
fun userAgentClientSpoofPatch(originalPackageName: String) = transformInstructionsPatch(
filterMap = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<MethodCall>(
"Lapp/revanced/extension",
classDef,
instruction,
instructionIndex,
)
},
transform = transform@{ mutableMethod, entry ->
val (_, _, instructionIndex) = entry
// Replace the result of context.getPackageName(), if it is used in a user agent string.
mutableMethod.apply {
// After context.getPackageName() the result is moved to a register.
val targetRegister = (
getInstruction(instructionIndex + 1)
as? OneRegisterInstruction ?: return@transform
).registerA
// IndexOutOfBoundsException is technically possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
// Only replace string builder usage.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return@transform
}
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
// Changing these package names will result in playback limitations,
// particularly Android VR background audio only playback.
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
opcode == Opcode.CONST_STRING &&
(reference?.string == "android.resource://" || reference?.string == "gcore_")
}
if (resourceOrGmsStringInstructionIndex >= 0) {
return@transform
}
// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"$originalPackageName\"",
)
}
},
)
@Suppress("unused")
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
) : IMethodCall {
GetPackageName(
"Landroid/content/Context;",
"getPackageName",
emptyArray(),
"Ljava/lang/String;",
),
}

View File

@@ -21,7 +21,7 @@ val audioAdsPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "ad.audio.audioAdsPatch")

View File

@@ -19,7 +19,7 @@ val embeddedAdsPatch = bytecodePatch(
settingsPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "ad.embedded.embeddedAdsPatch")

View File

@@ -4,6 +4,6 @@ import app.revanced.patcher.fingerprint
internal val createsUsherClientFingerprint = fingerprint {
custom { method, _ ->
method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;") && method.name == "buildOkHttpClient"
method.name == "buildOkHttpClient" && method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;")
}
}

View File

@@ -141,24 +141,21 @@ val videoAdsPatch = bytecodePatch(
)
// Spoof showAds JSON field.
contentConfigShowAdsFingerprint.method.addInstructions(
// Late versions of the app don't have the method anymore.
contentConfigShowAdsFingerprint.methodOrNull?.addInstructions(
0,
"""
${createConditionInstructions("v0")}
const/4 v0, 0
:$skipLabelName
return v0
""",
${createConditionInstructions("v0")}
const/4 v0, 0
:$skipLabelName
return v0
""",
)
}
},
)
compatibleWith(
"tv.twitch.android.app"(
"15.4.1",
"16.1.0",
"16.9.1",
),
"tv.twitch.android.app",
)
}

View File

@@ -22,7 +22,7 @@ val showDeletedMessagesPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
fun createSpoilerConditionInstructions(register: String = "v0") = """
invoke-static {}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->shouldUseSpoiler()Z

View File

@@ -20,7 +20,7 @@ val autoClaimChannelPointsPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch")

View File

@@ -48,13 +48,7 @@ val settingsPatch = bytecodePatch(
settingsPatch(preferences = preferences),
)
compatibleWith(
"tv.twitch.android.app"(
"15.4.1",
"16.1.0",
"16.9.1",
),
)
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "misc.settings.settingsPatch")

View File

@@ -11,8 +11,8 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.twitter.misc.extension.sharedExtensionPatch
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
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.formats.Instruction35c
internal var tweetShareLinkTemplateId = -1L
private set
@@ -25,15 +25,7 @@ internal val changeLinkSharingDomainResourcePatch = resourcePatch {
}
}
// This method is used to build the link that is shared when the "Share via..." button is pressed.
private const val FORMAT_METHOD_RESOURCE_REFERENCE =
"Lapp/revanced/extension/twitter/patches/links/ChangeLinkSharingDomainPatch;->" +
"formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;"
// This method is used to build the link that is shared when the "Copy link" button is pressed.
private const val FORMAT_METHOD_REFERENCE =
"Lapp/revanced/extension/twitter/patches/links/ChangeLinkSharingDomainPatch;->" +
"formatLink(JLjava/lang/String;)Ljava/lang/String;"
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/twitter/patches/links/ChangeLinkSharingDomainPatch;"
@Suppress("unused")
val changeLinkSharingDomainPatch = bytecodePatch(
@@ -71,7 +63,7 @@ val changeLinkSharingDomainPatch = bytecodePatch(
addInstructions(
0,
"""
invoke-static { p0, p1, p2 }, $FORMAT_METHOD_REFERENCE
invoke-static { p0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->formatLink(JLjava/lang/String;)Ljava/lang/String;
move-result-object p0
return-object p0
""",
@@ -84,12 +76,12 @@ val changeLinkSharingDomainPatch = bytecodePatch(
// Format the link with the new domain name register (1 instruction below the const).
val formatLinkCallIndex = templateIdConstIndex + 1
val formatLinkCall = getInstruction<Instruction35c>(formatLinkCallIndex)
val register = getInstruction<FiveRegisterInstruction>(formatLinkCallIndex).registerE
// Replace the original method call with the new method call.
replaceInstruction(
formatLinkCallIndex,
"invoke-static { v${formatLinkCall.registerE} }, $FORMAT_METHOD_RESOURCE_REFERENCE",
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;",
)
}
}

View File

@@ -0,0 +1,25 @@
package app.revanced.patches.youtube.ad.general
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversed
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val fullScreenEngagementAdContainerFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters()
custom { method, _ ->
method.containsLiteralInstruction(fullScreenEngagementAdContainer)
&& indexOfAddListInstruction(method) >= 0
}
}
internal fun indexOfAddListInstruction(method: Method) =
method.indexOfFirstInstructionReversed {
getReference<MethodReference>()?.name == "add"
}

View File

@@ -1,5 +1,7 @@
package app.revanced.patches.youtube.ad.general
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.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
@@ -18,11 +20,16 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.findMutableMethodOf
import app.revanced.util.injectHideViewCall
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
internal var adAttributionId = -1L
private set
internal var fullScreenEngagementAdContainer = -1L
private set
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/AdsFilter;"
private val hideAdsResourcePatch = resourcePatch {
dependsOn(
@@ -37,6 +44,7 @@ private val hideAdsResourcePatch = resourcePatch {
PreferenceScreen.ADS.addPreferences(
SwitchPreference("revanced_hide_general_ads"),
SwitchPreference("revanced_hide_end_screen_store_banner"),
SwitchPreference("revanced_hide_fullscreen_ads"),
SwitchPreference("revanced_hide_buttoned_ads"),
SwitchPreference("revanced_hide_paid_promotion_label"),
@@ -52,6 +60,7 @@ private val hideAdsResourcePatch = resourcePatch {
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/AdsFilter;")
adAttributionId = resourceMappings["id", "ad_attribution"]
fullScreenEngagementAdContainer = resourceMappings["id", "fullscreen_engagement_ad_container"]
}
}
@@ -77,10 +86,28 @@ val hideAdsPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
// Hide end screen store banner
fullScreenEngagementAdContainerFingerprint.method.apply {
val addListIndex = indexOfAddListInstruction(this)
val addListInstruction = getInstruction<FiveRegisterInstruction>(addListIndex)
val listRegister = addListInstruction.registerC
val objectRegister = addListInstruction.registerD
replaceInstruction(
addListIndex,
"invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR" +
"->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V"
)
}
// Hide ad views
classes.forEach { classDef ->
classDef.methods.forEach { method ->
with(method.implementation) {
@@ -109,7 +136,7 @@ val hideAdsPatch = bytecodePatch(
.injectHideViewCall(
insertIndex,
viewRegister,
"Lapp/revanced/extension/youtube/patches/components/AdsFilter;",
EXTENSION_CLASS_DESCRIPTOR,
"hideAdAttributionView",
)
}

View File

@@ -33,6 +33,7 @@ val hideGetPremiumPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)

View File

@@ -31,6 +31,7 @@ val videoAdsPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)

View File

@@ -61,6 +61,7 @@ val copyVideoUrlPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)

View File

@@ -32,6 +32,7 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)

View File

@@ -76,6 +76,7 @@ val downloadsPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)

View File

@@ -31,6 +31,7 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)

View File

@@ -34,6 +34,7 @@ val enableSeekbarTappingPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)

View File

@@ -44,6 +44,7 @@ val enableSlideToSeekPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)

View File

@@ -37,6 +37,7 @@ val seekbarThumbnailsPatch = bytecodePatch(
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
)
)

View File

@@ -1,12 +1,23 @@
package app.revanced.patches.youtube.interaction.swipecontrols
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
internal val swipeControlsHostActivityFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters()
custom { method, _ ->
method.definingClass == "Lapp/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity;"
method.definingClass == EXTENSION_CLASS_DESCRIPTOR
}
}
internal const val SWIPE_CHANGE_VIDEO_FEATURE_FLAG = 45631116L
internal val swipeChangeVideoFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters("L")
literal {
SWIPE_CHANGE_VIDEO_FEATURE_FLAG
}
}

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