Compare commits

...

202 Commits

Author SHA1 Message Date
semantic-release-bot
66a2ee2416 chore: Release v5.16.0-dev.1 [skip ci]
# [5.16.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.15.0...v5.16.0-dev.1) (2025-03-24)

### Bug Fixes

* **YouTube - Settings:** System navigation bar is located above the settings ui on Android 15+ ([54eef22](54eef22ce7))

### Features

* **YouTube - Comments:** Add `Hide AI Comments summary` ([#4634](https://github.com/ReVanced/revanced-patches/issues/4634)) ([d5845ab](d5845abd08))
* **YouTube - Video description:** Add `Hide AI-generated video summary` ([#4636](https://github.com/ReVanced/revanced-patches/issues/4636)) ([d8c276c](d8c276cf96))
2025-03-24 16:26:47 +00:00
ILoveOpenSourceApplications
d8c276cf96 feat(YouTube - Video description): Add Hide AI-generated video summary (#4636) 2025-03-24 17:23:57 +01:00
ILoveOpenSourceApplications
d5845abd08 feat(YouTube - Comments): Add Hide AI Comments summary (#4634) 2025-03-24 17:23:12 +01:00
LisoUseInAIKyrios
54eef22ce7 fix(YouTube - Settings): System navigation bar is located above the settings ui on Android 15+ 2025-03-24 17:21:36 +01:00
ILoveOpenSourceApplications
e287bdc59d chore(YouTube): Use consistent strings (#4637) 2025-03-24 17:12:11 +01:00
LisoUseInAIKyrios
20a82ef956 chore: Remove duplicate language entry 2025-03-22 11:01:19 +01:00
semantic-release-bot
1e29da9e06 chore: Release v5.15.0 [skip ci]
# [5.15.0](https://github.com/ReVanced/revanced-patches/compare/v5.14.0...v5.15.0) (2025-03-21)

### Bug Fixes

* **YouTube - Spoof app version:** Change oldest spoof target to 19.01.34 ([54a7afa](54a7afa540))
* **YouTube - Spoof app version:** Remove broken spoof targets that YouTube no longer supports ([#4610](https://github.com/ReVanced/revanced-patches/issues/4610)) ([04a1700](04a170054e))
* **YouTube:** Do not show restart prompt more than once if setting change is canceled ([df838ed](df838ed91d))

### Features

* **YouTube - SponsorBlock:** Add opacity setting to category segment colors ([#4582](https://github.com/ReVanced/revanced-patches/issues/4582)) ([bbf3a34](bbf3a34a2f))
2025-03-21 10:54:27 +00:00
LisoUseInAIKyrios
56e6a90a90 chore: Merge branch dev to main (#4615) 2025-03-21 11:51:03 +01:00
semantic-release-bot
76d32e21c2 chore: Release v5.15.0-dev.4 [skip ci]
# [5.15.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.3...v5.15.0-dev.4) (2025-03-21)

### Bug Fixes

* **YouTube - Spoof app version:** Change oldest spoof target to 19.01.34 ([54a7afa](54a7afa540))
2025-03-21 09:26:53 +00:00
LisoUseInAIKyrios
54a7afa540 fix(YouTube - Spoof app version): Change oldest spoof target to 19.01.34 2025-03-21 10:23:26 +01:00
LisoUseInAIKyrios
ef86438bac ci: Pull Crowdin strings less often 2025-03-21 10:21:16 +01:00
github-actions[bot]
0683cedac0 chore: Sync translations (#4626) 2025-03-21 10:18:28 +01:00
semantic-release-bot
35753410aa chore: Release v5.15.0-dev.3 [skip ci]
# [5.15.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.2...v5.15.0-dev.3) (2025-03-20)

### Bug Fixes

* **YouTube:** Do not show restart prompt more than once if setting change is canceled ([df838ed](df838ed91d))
2025-03-20 11:17:31 +00:00
LisoUseInAIKyrios
df838ed91d fix(YouTube): Do not show restart prompt more than once if setting change is canceled 2025-03-20 12:14:31 +01:00
github-actions[bot]
8e494d26d4 chore: Sync translations (#4623) 2025-03-20 12:08:20 +01:00
LisoUseInAIKyrios
7d834e5421 refactor(YouTube - Spoof app version): Allow manually spoofing to 19.01 - 19.25 2025-03-20 09:57:29 +01:00
semantic-release-bot
60a31cf4e1 chore: Release v5.15.0-dev.2 [skip ci]
# [5.15.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.1...v5.15.0-dev.2) (2025-03-19)

### Bug Fixes

* **YouTube - Spoof app version:** Remove broken spoof targets that YouTube no longer supports ([#4610](https://github.com/ReVanced/revanced-patches/issues/4610)) ([04a1700](04a170054e))
2025-03-19 17:19:19 +00:00
github-actions[bot]
edb8bd66bc chore: Sync translations (#4616) 2025-03-19 18:16:05 +01:00
LisoUseInAIKyrios
04a170054e fix(YouTube - Spoof app version): Remove broken spoof targets that YouTube no longer supports (#4610) 2025-03-19 18:08:51 +01:00
semantic-release-bot
79e6349a69 chore: Release v5.15.0-dev.1 [skip ci]
# [5.15.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.14.0...v5.15.0-dev.1) (2025-03-19)

### Features

* **YouTube - SponsorBlock:** Add opacity setting to category segment colors ([#4582](https://github.com/ReVanced/revanced-patches/issues/4582)) ([bbf3a34](bbf3a34a2f))
2025-03-19 17:06:12 +00:00
LisoUseInAIKyrios
bbf3a34a2f feat(YouTube - SponsorBlock): Add opacity setting to category segment colors (#4582) 2025-03-19 18:02:06 +01:00
github-actions[bot]
1db7c49514 chore: Sync translations (#4614) 2025-03-19 18:00:52 +01:00
semantic-release-bot
ef0506a4f8 chore: Release v5.14.0 [skip ci]
# [5.14.0](https://github.com/ReVanced/revanced-patches/compare/v5.13.0...v5.14.0) (2025-03-09)

### Bug Fixes

* **Boost for reddit - Client spoof:** Use a different user agent to combat Reddit's API issues ([8d0bca3](8d0bca3b03))
* **YouTube - Change form factor:** Restore Automotive form factor watch history menu, channel pages, and community posts ([#4541](https://github.com/ReVanced/revanced-patches/issues/4541)) ([e2de2d8](e2de2d8d44))
* **YouTube - Hide ads:** Hide new type of buttoned ad ([#4528](https://github.com/ReVanced/revanced-patches/issues/4528)) ([67dcd09](67dcd091c4))
* **YouTube - Hide layout components:** Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled ([99879f6](99879f6e0a))
* **YouTube - Theme:** Resolve dark mode startup crash with Android 9.0 ([7adfc63](7adfc637dc))
* **YouTube:** Change language settings menu to use native language names ([#4568](https://github.com/ReVanced/revanced-patches/issues/4568)) ([e9bc201](e9bc201641))
* **YouTube:** Combine `Restore old video quality menu` and `Remember video quality` into `Video quality` patch ([#4552](https://github.com/ReVanced/revanced-patches/issues/4552)) ([ee5c830](ee5c830df8))

### Features

* **Infinity for Reddit:** Add support for package name on IzzyOnDroid ([#4554](https://github.com/ReVanced/revanced-patches/issues/4554)) ([df3dc1c](df3dc1c0b2))
* **Spotify:** Add `Spoof signature` patch ([#4576](https://github.com/ReVanced/revanced-patches/issues/4576)) ([f39e70c](f39e70c648))
* **YouTube - Remember video quality:** Add separate Shorts default quality settings ([#4543](https://github.com/ReVanced/revanced-patches/issues/4543)) ([2a67c31](2a67c312e1))
2025-03-09 12:21:22 +00:00
oSumAtrIX
9b38da35ff chore: Merge branch dev to main (#4540)
Co-authored-by: ILoveOpenSourceApplications <117499019+ILoveOpenSourceApplications@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
Co-authored-by: alieRN <45766489+aliernfrog@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-03-09 13:17:57 +01:00
github-actions[bot]
afdb771066 chore: Sync translations (#4577) 2025-03-09 14:14:53 +02:00
semantic-release-bot
1b2b536d2e chore: Release v5.14.0-dev.9 [skip ci]
# [5.14.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.8...v5.14.0-dev.9) (2025-03-09)

### Features

* **Spotify:** Add `Spoof signature` patch ([#4576](https://github.com/ReVanced/revanced-patches/issues/4576)) ([f39e70c](f39e70c648))
2025-03-09 12:10:11 +00:00
oSumAtrIX
f39e70c648 feat(Spotify): Add Spoof signature patch (#4576) 2025-03-09 13:06:53 +01:00
semantic-release-bot
556acdd9c1 chore: Release v5.14.0-dev.8 [skip ci]
# [5.14.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.7...v5.14.0-dev.8) (2025-03-09)

### Bug Fixes

* **YouTube - Theme:** Resolve dark mode startup crash with Android 9.0 ([7adfc63](7adfc637dc))
2025-03-09 09:32:12 +00:00
LisoUseInAIKyrios
7adfc637dc fix(YouTube - Theme): Resolve dark mode startup crash with Android 9.0 2025-03-09 11:29:17 +02:00
github-actions[bot]
9cc0c075ad chore: Sync translations (#4575) 2025-03-09 11:28:52 +02:00
semantic-release-bot
ead11e7f46 chore: Release v5.14.0-dev.7 [skip ci]
# [5.14.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.6...v5.14.0-dev.7) (2025-03-08)

### Bug Fixes

* **YouTube:** Change language settings menu to use native language names ([#4568](https://github.com/ReVanced/revanced-patches/issues/4568)) ([e9bc201](e9bc201641))
2025-03-08 18:45:38 +00:00
LisoUseInAIKyrios
e9bc201641 fix(YouTube): Change language settings menu to use native language names (#4568) 2025-03-08 20:42:48 +02:00
github-actions[bot]
99baedf355 chore: Sync translations (#4573) 2025-03-08 20:42:13 +02:00
semantic-release-bot
0338d0acd3 chore: Release v5.14.0-dev.6 [skip ci]
# [5.14.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.5...v5.14.0-dev.6) (2025-03-07)

### Bug Fixes

* **YouTube - Hide layout components:** Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled ([99879f6](99879f6e0a))
2025-03-07 17:40:21 +00:00
LisoUseInAIKyrios
99879f6e0a fix(YouTube - Hide layout components): Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled 2025-03-07 19:37:23 +02:00
github-actions[bot]
f0c70de602 chore: Sync translations (#4562) 2025-03-07 19:35:55 +02:00
LisoUseInAIKyrios
737ae07a06 refactor(YouTube): Sort no title preference group by first sub preference title 2025-03-06 21:15:29 +02:00
semantic-release-bot
2c51de59de chore: Release v5.14.0-dev.5 [skip ci]
# [5.14.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.4...v5.14.0-dev.5) (2025-03-06)

### Features

* **Infinity for Reddit:** Add support for package name on IzzyOnDroid ([#4554](https://github.com/ReVanced/revanced-patches/issues/4554)) ([df3dc1c](df3dc1c0b2))
2025-03-06 18:27:44 +00:00
ILoveOpenSourceApplications
df3dc1c0b2 feat(Infinity for Reddit): Add support for package name on IzzyOnDroid (#4554) 2025-03-06 20:24:54 +02:00
github-actions[bot]
074c948581 chore: Sync translations (#4556) 2025-03-06 20:24:22 +02:00
semantic-release-bot
2a88b1f895 chore: Release v5.14.0-dev.4 [skip ci]
# [5.14.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.3...v5.14.0-dev.4) (2025-03-06)

### Bug Fixes

* **YouTube:** Combine `Restore old video quality menu` and `Remember video quality` into `Video quality` patch ([#4552](https://github.com/ReVanced/revanced-patches/issues/4552)) ([ee5c830](ee5c830df8))
2025-03-06 12:59:39 +00:00
LisoUseInAIKyrios
ee5c830df8 fix(YouTube): Combine Restore old video quality menu and Remember video quality into Video quality patch (#4552) 2025-03-06 14:56:32 +02:00
semantic-release-bot
e63a4b31f3 chore: Release v5.14.0-dev.3 [skip ci]
# [5.14.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.2...v5.14.0-dev.3) (2025-03-06)

### Bug Fixes

* **Boost for reddit - Client spoof:** Use a different user agent to combat Reddit's API issues ([8d0bca3](8d0bca3b03))
2025-03-06 10:36:52 +00:00
oSumAtrIX
8d0bca3b03 fix(Boost for reddit - Client spoof): Use a different user agent to combat Reddit's API issues 2025-03-06 11:33:06 +01:00
semantic-release-bot
c162d65d5b chore: Release v5.14.0-dev.2 [skip ci]
# [5.14.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.1...v5.14.0-dev.2) (2025-03-06)

### Bug Fixes

* **YouTube - Hide ads:** Hide new type of buttoned ad ([#4528](https://github.com/ReVanced/revanced-patches/issues/4528)) ([67dcd09](67dcd091c4))
2025-03-06 09:30:51 +00:00
ILoveOpenSourceApplications
67dcd091c4 fix(YouTube - Hide ads): Hide new type of buttoned ad (#4528) 2025-03-06 11:27:37 +02:00
github-actions[bot]
ac5ce2d67f chore: Sync translations (#4553) 2025-03-06 11:26:20 +02:00
LisoUseInAIKyrios
4b78d056fd ci: Pull Crowdin strings less often
Crowdin is starting to give errors and pulling less often may help.
2025-03-06 11:25:22 +02:00
semantic-release-bot
f8c901b2c1 chore: Release v5.14.0-dev.1 [skip ci]
# [5.14.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.1-dev.1...v5.14.0-dev.1) (2025-03-06)

### Features

* **YouTube - Remember video quality:** Add separate Shorts default quality settings ([#4543](https://github.com/ReVanced/revanced-patches/issues/4543)) ([2a67c31](2a67c312e1))
2025-03-06 06:49:39 +00:00
alieRN
2a67c312e1 feat(YouTube - Remember video quality): Add separate Shorts default quality settings (#4543) 2025-03-06 08:46:33 +02:00
semantic-release-bot
a7eed30f46 chore: Release v5.13.1-dev.1 [skip ci]
## [5.13.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.0...v5.13.1-dev.1) (2025-03-06)

### Bug Fixes

* **YouTube - Change form factor:** Restore Automotive form factor watch history menu, channel pages, and community posts ([#4541](https://github.com/ReVanced/revanced-patches/issues/4541)) ([e2de2d8](e2de2d8d44))
2025-03-06 06:28:57 +00:00
LisoUseInAIKyrios
e2de2d8d44 fix(YouTube - Change form factor): Restore Automotive form factor watch history menu, channel pages, and community posts (#4541) 2025-03-06 08:26:09 +02:00
github-actions[bot]
7ebbf356c0 chore: Sync translations (#4550) 2025-03-06 08:25:49 +02:00
ILoveOpenSourceApplications
2ced5c6e2a refactor(YouTube): Use more consistent strings (#4526) 2025-03-05 08:55:44 +02:00
semantic-release-bot
4a090ba659 chore: Release v5.13.0 [skip ci]
# [5.13.0](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0) (2025-03-03)

### Bug Fixes

* **TikTok:** Resolve startup app crash ([3c52ab8](3c52ab8017))
* **TikTok:** Resolve startup app crash ([c817977](c8179776ed))
* **TikTok:** Resolve startup app crash ([d5aab3d](d5aab3d464))
* **TikTok:** Resolve startup app crash ([348f7e1](348f7e12cb))
* **YouTube - Copy video URL:** Use correct button ordering ([d77d5bf](d77d5bfbdd))
* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([1b60a72](1b60a72ede))
* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([94fb367](94fb367618))
* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([7cc939a](7cc939ab03))
* **YouTube - Hide video action buttons:** Move 'Disable Like and Subscribe glow' to action buttons settings menu ([7991c80](7991c80129))
* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([4ae1155](4ae1155e51))
* **YouTube - Spoof app version:** Force old settings menus if spoofing to older app targets ([#4490](https://github.com/ReVanced/revanced-patches/issues/4490)) ([0c0bbb8](0c0bbb8713))
* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([cbbf474](cbbf474c50))
* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([329f993](329f993024))
* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([e664a24](e664a24f73))
* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([a2c79f1](a2c79f1349))
* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([f5dd902](f5dd902915))

### Features

* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([fb8dbb4](fb8dbb4723))
* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([93ea250](93ea250bf3))
* **YouTube - Navigation buttons:** Add 'Hide notifications' setting ([#4485](https://github.com/ReVanced/revanced-patches/issues/4485)) ([d6eae01](d6eae01e12))
* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([3548359](354835966d))
2025-03-03 07:01:16 +00:00
LisoUseInAIKyrios
cb609a6d9d chore: Merge branch dev to main (#4470) 2025-03-03 08:57:51 +02:00
github-actions[bot]
42e6de9e8f chore: Sync translations (#4525) 2025-03-03 08:55:55 +02:00
semantic-release-bot
c4a5b9a28c chore: Release v5.13.0-dev.19 [skip ci]
# [5.13.0-dev.19](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.18...v5.13.0-dev.19) (2025-03-02)

### Bug Fixes

* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([cbbf474](cbbf474c50))
2025-03-02 15:44:18 +00:00
github-actions[bot]
c86c85947f chore: Sync translations (#4523) 2025-03-02 17:40:33 +02:00
LisoUseInAIKyrios
cbbf474c50 fix(YouTube - Spoof video streams): Resolve playback issues with dynamic player config (#4521) 2025-03-02 17:38:43 +02:00
semantic-release-bot
f147b7b73d chore: Release v5.13.0-dev.18 [skip ci]
# [5.13.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.17...v5.13.0-dev.18) (2025-02-28)

### Features

* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([fb8dbb4](fb8dbb4723))
2025-02-28 08:33:53 +00:00
tillcash
fb8dbb4723 feat(Infinity for Reddit): Add support for Infinity for Reddit Plus (#4511) 2025-02-28 10:31:02 +02:00
github-actions[bot]
1e0d27e689 chore: Sync translations (#4517) 2025-02-28 10:30:30 +02:00
semantic-release-bot
a2185bce09 chore: Release v5.13.0-dev.17 [skip ci]
# [5.13.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.16...v5.13.0-dev.17) (2025-02-27)

### Bug Fixes

* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([1b60a72](1b60a72ede))
2025-02-27 13:23:12 +00:00
ILoveOpenSourceApplications
1b60a72ede fix(YouTube - Hide filter bar): Fix Hide in feed not working in subscriptions feed (#4512) 2025-02-27 15:20:30 +02:00
semantic-release-bot
12b4ee04ad chore: Release v5.13.0-dev.16 [skip ci]
# [5.13.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.15...v5.13.0-dev.16) (2025-02-27)

### Features

* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([93ea250](93ea250bf3))
2025-02-27 06:12:28 +00:00
github-actions[bot]
f9a6cc96de chore: Sync translations (#4510) 2025-02-27 08:09:23 +02:00
Jasper Abbink
93ea250bf3 feat(NU.nl): Add Hide ads and Spoof Certificate patch (#4368)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-02-27 08:07:54 +02:00
semantic-release-bot
fdb946a2cc chore: Release v5.13.0-dev.15 [skip ci]
# [5.13.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.14...v5.13.0-dev.15) (2025-02-25)

### Bug Fixes

* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([7cc939a](7cc939ab03))
2025-02-25 15:42:36 +00:00
ILoveOpenSourceApplications
7cc939ab03 fix(YouTube - Hide player components): Show correct end video thumbnail if Hide end screen suggested video is enabled (#4502)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-02-25 17:38:16 +02:00
github-actions[bot]
228d72428d chore: Sync translations (#4505) 2025-02-25 16:55:10 +02:00
semantic-release-bot
4db7ab4207 chore: Release v5.13.0-dev.14 [skip ci]
# [5.13.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.13...v5.13.0-dev.14) (2025-02-25)

### Bug Fixes

* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([329f993](329f993024))
2025-02-25 13:37:45 +00:00
MarcaD
329f993024 fix(YouTube - Swipe controls): Adjust the overlay text size (#4503) 2025-02-25 15:34:37 +02:00
github-actions[bot]
7cd1fb22d8 chore: Sync translations (#4504) 2025-02-25 15:34:19 +02:00
LisoUseInAIKyrios
ae111bc0b9 refactor(YouTube - Force original audio): Adjust settings text 2025-02-25 11:35:44 +02:00
semantic-release-bot
79f1dfd3e8 chore: Release v5.13.0-dev.13 [skip ci]
# [5.13.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.12...v5.13.0-dev.13) (2025-02-24)

### Bug Fixes

* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([f5dd902](f5dd902915))
2025-02-24 18:37:58 +00:00
MarcaD
f5dd902915 fix(YouTube): Resolve button flickering when taping seekbar (#4500) 2025-02-24 20:34:28 +02:00
semantic-release-bot
10e2b08eb2 chore: Release v5.13.0-dev.12 [skip ci]
# [5.13.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.11...v5.13.0-dev.12) (2025-02-24)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([4ae1155](4ae1155e51))
2025-02-24 10:01:52 +00:00
LisoUseInAIKyrios
4ae1155e51 fix(YouTube - Return YouTube Dislike): Use correct number formatting if using a different ReVanced language 2025-02-24 11:58:57 +02:00
github-actions[bot]
69fbfaea19 chore: Sync translations (#4499)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-02-24 11:56:46 +02:00
semantic-release-bot
f44fede67c chore: Release v5.13.0-dev.11 [skip ci]
# [5.13.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.10...v5.13.0-dev.11) (2025-02-23)

### Bug Fixes

* **TikTok:** Resolve startup app crash ([3c52ab8](3c52ab8017))
2025-02-23 07:31:27 +00:00
LisoUseInAIKyrios
3c52ab8017 fix(TikTok): Resolve startup app crash 2025-02-23 09:28:37 +02:00
github-actions[bot]
d1641a6e3d chore: Sync translations (#4495) 2025-02-23 09:26:22 +02:00
semantic-release-bot
09773e8934 chore: Release v5.13.0-dev.10 [skip ci]
# [5.13.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.9...v5.13.0-dev.10) (2025-02-22)

### Bug Fixes

* **YouTube - Copy video URL:** Use correct button ordering ([d77d5bf](d77d5bfbdd))
2025-02-22 16:31:11 +00:00
LisoUseInAIKyrios
d77d5bfbdd fix(YouTube - Copy video URL): Use correct button ordering
Fixes refactoring oversight of button fade fix
2025-02-22 18:28:47 +02:00
semantic-release-bot
a84bded9e7 chore: Release v5.13.0-dev.9 [skip ci]
# [5.13.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.8...v5.13.0-dev.9) (2025-02-22)

### Bug Fixes

* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([e664a24](e664a24f73))
2025-02-22 15:48:25 +00:00
LisoUseInAIKyrios
e664a24f73 fix(YouTube): Do not hide player controls when using double tap to skip forward (#4487)
Co-authored-by: MarcaDian <tolan.sheremeev@gmail.com>
2025-02-22 17:44:53 +02:00
semantic-release-bot
5bf964fff6 chore: Release v5.13.0-dev.8 [skip ci]
# [5.13.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.7...v5.13.0-dev.8) (2025-02-22)

### Bug Fixes

* **YouTube - Spoof app version:** Force old settings menus if spoofing to older app targets ([#4490](https://github.com/ReVanced/revanced-patches/issues/4490)) ([0c0bbb8](0c0bbb8713))
2025-02-22 09:43:04 +00:00
LisoUseInAIKyrios
0c0bbb8713 fix(YouTube - Spoof app version): Force old settings menus if spoofing to older app targets (#4490) 2025-02-22 11:40:06 +02:00
github-actions[bot]
8afe48cd92 chore: Sync translations (#4492) 2025-02-22 11:39:54 +02:00
github-actions[bot]
dde8ea31cb chore: Sync translations (#4491) 2025-02-22 11:34:59 +02:00
semantic-release-bot
d3abbe3e93 chore: Release v5.13.0-dev.7 [skip ci]
# [5.13.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.6...v5.13.0-dev.7) (2025-02-22)

### Bug Fixes

* **TikTok:** Resolve startup app crash ([c817977](c8179776ed))
2025-02-22 06:25:32 +00:00
LisoUseInAIKyrios
c8179776ed fix(TikTok): Resolve startup app crash 2025-02-22 08:20:19 +02:00
semantic-release-bot
c6c6516b12 chore: Release v5.13.0-dev.6 [skip ci]
# [5.13.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.5...v5.13.0-dev.6) (2025-02-21)

### Features

* **YouTube - Navigation buttons:** Add 'Hide notifications' setting ([#4485](https://github.com/ReVanced/revanced-patches/issues/4485)) ([d6eae01](d6eae01e12))
2025-02-21 12:13:01 +00:00
LisoUseInAIKyrios
d6eae01e12 feat(YouTube - Navigation buttons): Add 'Hide notifications' setting (#4485) 2025-02-21 14:09:01 +02:00
semantic-release-bot
ba88603f4b chore: Release v5.13.0-dev.5 [skip ci]
# [5.13.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.4...v5.13.0-dev.5) (2025-02-19)

### Bug Fixes

* **TikTok:** Resolve startup app crash ([d5aab3d](d5aab3d464))
2025-02-19 14:39:51 +00:00
LisoUseInAIKyrios
d5aab3d464 fix(TikTok): Resolve startup app crash 2025-02-19 16:36:07 +02:00
semantic-release-bot
fca2f70c0e chore: Release v5.13.0-dev.4 [skip ci]
# [5.13.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.3...v5.13.0-dev.4) (2025-02-19)

### Bug Fixes

* **TikTok:** Resolve startup app crash ([348f7e1](348f7e12cb))
2025-02-19 13:57:05 +00:00
LisoUseInAIKyrios
348f7e12cb fix(TikTok): Resolve startup app crash 2025-02-19 15:53:12 +02:00
semantic-release-bot
b6b7208eeb chore: Release v5.13.0-dev.3 [skip ci]
# [5.13.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.2...v5.13.0-dev.3) (2025-02-19)

### Bug Fixes

* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([a2c79f1](a2c79f1349))
2025-02-19 11:25:29 +00:00
MarcaD
a2c79f1349 fix(YouTube): Fix player button fade out animations (#4469) 2025-02-19 13:22:46 +02:00
github-actions[bot]
4f5bb3c915 chore: Sync translations (#4478) 2025-02-19 13:22:17 +02:00
semantic-release-bot
4b77d27c77 chore: Release v5.13.0-dev.2 [skip ci]
# [5.13.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.1...v5.13.0-dev.2) (2025-02-18)

### Bug Fixes

* **YouTube - Hide video action buttons:** Move 'Disable Like and Subscribe glow' to action buttons settings menu ([7991c80](7991c80129))
2025-02-18 07:29:55 +00:00
LisoUseInAIKyrios
7991c80129 fix(YouTube - Hide video action buttons): Move 'Disable Like and Subscribe glow' to action buttons settings menu 2025-02-18 09:26:30 +02:00
github-actions[bot]
6baf4ea2ac chore: Sync translations (#4473) 2025-02-18 09:24:59 +02:00
semantic-release-bot
c89538c8f5 chore: Release v5.13.0-dev.1 [skip ci]
# [5.13.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0-dev.1) (2025-02-18)

### Bug Fixes

* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([94fb367](94fb367618))

### Features

* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([3548359](354835966d))
2025-02-18 07:11:45 +00:00
LisoUseInAIKyrios
94fb367618 fix(YouTube - Hide layout components): Do not hide 'Show anyway' button in search results 2025-02-18 09:08:37 +02:00
MarcaD
354835966d feat(YouTube - Swipe controls): Swipe controls UI improvements (#4422) 2025-02-18 09:07:28 +02:00
github-actions[bot]
168f9b769e chore: Sync translations (#4472) 2025-02-18 09:06:39 +02:00
ILoveOpenSourceApplications
e4c4b3a73a refactor(YouTube): Use more consistent strings (#4376) 2025-02-17 10:07:24 +02:00
semantic-release-bot
fce98b4960 chore: Release v5.12.0 [skip ci]
# [5.12.0](https://github.com/ReVanced/revanced-patches/compare/v5.11.0...v5.12.0) (2025-02-17)

### Bug Fixes

* Allow changing default settings for existing app installs ([#4464](https://github.com/ReVanced/revanced-patches/issues/4464)) ([a959d79](a959d798e8))
* **Windy.app:** Remove obsolete `Unlock pro` patch ([#4428](https://github.com/ReVanced/revanced-patches/issues/4428)) ([421af92](421af92f4c))
* **YouTube - Spoof video streams:** Change default client to `Android TV` ([#4465](https://github.com/ReVanced/revanced-patches/issues/4465)) ([04b37dd](04b37dd55a))
* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([92c38b2](92c38b2cb4))

### Features

* **Return YouTube Dislike:** add `Show estimated likes` setting ([#4443](https://github.com/ReVanced/revanced-patches/issues/4443)) ([7c4285e](7c4285e3e6))
* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([0079ece](0079eceb87))
* **YouTube Music:** Support version `8.05.50` ([#4439](https://github.com/ReVanced/revanced-patches/issues/4439)) ([bcd157d](bcd157dd2b))
* **YouTube Music:** Support version `8.05.51` ([2382e9d](2382e9d09e))
2025-02-17 06:23:44 +00:00
LisoUseInAIKyrios
839aa81e9c chore: Merge branch dev to main (#4437) 2025-02-17 08:20:23 +02:00
semantic-release-bot
905bb0ea5f chore: Release v5.12.0-dev.7 [skip ci]
# [5.12.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.6...v5.12.0-dev.7) (2025-02-16)

### Bug Fixes

* **YouTube - Spoof video streams:** Change default client to `Android TV` ([#4465](https://github.com/ReVanced/revanced-patches/issues/4465)) ([04b37dd](04b37dd55a))

### Features

* **YouTube Music:** Support version `8.05.51` ([2382e9d](2382e9d09e))
2025-02-16 16:41:43 +00:00
github-actions[bot]
a94a663859 chore: Sync translations (#4468) 2025-02-16 18:38:36 +02:00
LisoUseInAIKyrios
04b37dd55a fix(YouTube - Spoof video streams): Change default client to Android TV (#4465) 2025-02-16 18:34:12 +02:00
LisoUseInAIKyrios
2382e9d09e feat(YouTube Music): Support version 8.05.51 2025-02-16 18:31:52 +02:00
github-actions[bot]
97f504976a chore: Sync translations (#4467) 2025-02-16 18:30:04 +02:00
semantic-release-bot
0a6c5158e0 chore: Release v5.12.0-dev.6 [skip ci]
# [5.12.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.5...v5.12.0-dev.6) (2025-02-16)

### Bug Fixes

* Allow changing default settings for existing app installs ([#4464](https://github.com/ReVanced/revanced-patches/issues/4464)) ([a959d79](a959d798e8))
2025-02-16 13:19:31 +00:00
LisoUseInAIKyrios
a959d798e8 fix: Allow changing default settings for existing app installs (#4464) 2025-02-16 15:16:24 +02:00
semantic-release-bot
39a0b9bda6 chore: Release v5.12.0-dev.5 [skip ci]
# [5.12.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.4...v5.12.0-dev.5) (2025-02-13)

### Bug Fixes

* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([92c38b2](92c38b2cb4))
2025-02-13 12:41:16 +00:00
LisoUseInAIKyrios
92c38b2cb4 fix(YouTube): Remove obsolete 18.x targets (#4454) 2025-02-13 14:38:23 +02:00
github-actions[bot]
4732210d4b chore: Sync translations (#4455) 2025-02-13 14:35:32 +02:00
semantic-release-bot
f30a49f1cb chore: Release v5.12.0-dev.4 [skip ci]
# [5.12.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.3...v5.12.0-dev.4) (2025-02-11)

### Features

* **YouTube Music:** Support version `8.05.50` ([#4439](https://github.com/ReVanced/revanced-patches/issues/4439)) ([bcd157d](bcd157dd2b))
2025-02-11 18:40:51 +00:00
Alberto Ponces
bcd157dd2b feat(YouTube Music): Support version 8.05.50 (#4439) 2025-02-11 20:37:58 +02:00
LisoUseInAIKyrios
d299ea5973 chore(deps): Remove unused dependency 2025-02-11 17:41:40 +02:00
dependabot[bot]
a20021e290 chore(deps): bump com.google.code.gson:gson from 2.11.0 to 2.12.1 (#4398)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-11 17:19:52 +02:00
dependabot[bot]
373ca966f3 chore(deps): Bump com.google.guava:guava from 33.2.1-jre to 33.4.0-jre (#4252) 2025-02-11 17:17:35 +02:00
semantic-release-bot
12de922afa chore: Release v5.12.0-dev.3 [skip ci]
# [5.12.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.2...v5.12.0-dev.3) (2025-02-11)

### Bug Fixes

* **Windy.app:** Remove obsolete `Unlock pro` patch ([#4428](https://github.com/ReVanced/revanced-patches/issues/4428)) ([421af92](421af92f4c))
2025-02-11 15:11:34 +00:00
dependabot[bot]
580bb3cf6c chore(deps-dev): bump semantic-release from 24.1.2 to 24.2.1 (#4397) 2025-02-11 17:07:28 +02:00
LisoUseInAIKyrios
421af92f4c fix(Windy.app): Remove obsolete Unlock pro patch (#4428) 2025-02-11 17:05:46 +02:00
github-actions[bot]
4d03e1b5a1 chore: Sync translations (#4446) 2025-02-11 17:05:17 +02:00
LisoUseInAIKyrios
24d68df6cd refactor: Improve XML performance 2025-02-11 15:24:29 +02:00
semantic-release-bot
e9aee17746 chore: Release v5.12.0-dev.2 [skip ci]
# [5.12.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.1...v5.12.0-dev.2) (2025-02-11)

### Features

* **Return YouTube Dislike:** add `Show estimated likes` setting ([#4443](https://github.com/ReVanced/revanced-patches/issues/4443)) ([7c4285e](7c4285e3e6))
2025-02-11 10:15:24 +00:00
LisoUseInAIKyrios
7c4285e3e6 feat(Return YouTube Dislike): add Show estimated likes setting (#4443) 2025-02-11 12:12:24 +02:00
semantic-release-bot
e3110271a7 chore: Release v5.12.0-dev.1 [skip ci]
# [5.12.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.11.0...v5.12.0-dev.1) (2025-02-10)

### Features

* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([0079ece](0079eceb87))
2025-02-10 18:33:12 +00:00
MarcaD
0079eceb87 feat(YouTube - SponsorBlock): Redesign skip buttons (#4427)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-02-10 20:30:06 +02:00
github-actions[bot]
af2a97cb16 chore: Sync translations (#4436) 2025-02-10 20:29:28 +02:00
semantic-release-bot
aeb552e8f2 chore: Release v5.11.0 [skip ci]
# [5.11.0](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.11.0) (2025-02-07)

### Bug Fixes

* Fix broken `Remove screen capture restriction`,  `Remove screenshot restriction`, `Spoof Wi-Fi connection`, and `Export internal data documents provider` patch ([#4405](https://github.com/ReVanced/revanced-patches/issues/4405)) ([399889c](399889c6fa))
* **YouTube - Enable slide to seek:** Change patch to default include ([76fd33c](76fd33ca54))
* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([a06c031](a06c0318bf))
* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([f81b658](f81b658fb7))

### Features

* **YouTube - Change start page:** Add additional start pages ([#4413](https://github.com/ReVanced/revanced-patches/issues/4413)) ([b7ebfdd](b7ebfddf65))
2025-02-07 07:05:36 +00:00
LisoUseInAIKyrios
6e936fea42 chore: Merge branch dev to main (#4388) 2025-02-07 09:02:22 +02:00
github-actions[bot]
f63769f39f chore: Sync translations (#4421) 2025-02-07 08:58:04 +02:00
semantic-release-bot
1c9ab20a63 chore: Release v5.11.0-dev.2 [skip ci]
# [5.11.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.11.0-dev.1...v5.11.0-dev.2) (2025-02-06)

### Bug Fixes

* Fix broken `Remove screen capture restriction`,  `Remove screenshot restriction`, `Spoof Wi-Fi connection`, and `Export internal data documents provider` patch ([#4405](https://github.com/ReVanced/revanced-patches/issues/4405)) ([399889c](399889c6fa))
2025-02-06 12:29:29 +00:00
github-actions[bot]
cdeccad908 chore: Sync translations (#4417) 2025-02-06 14:25:50 +02:00
LisoUseInAIKyrios
399889c6fa fix: Fix broken Remove screen capture restriction, Remove screenshot restriction, Spoof Wi-Fi connection, and Export internal data documents provider patch (#4405) 2025-02-06 14:24:40 +02:00
github-actions[bot]
ec77861410 chore: Sync translations (#4415) 2025-02-05 20:42:07 +02:00
semantic-release-bot
b5afc6d827 chore: Release v5.11.0-dev.1 [skip ci]
# [5.11.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.3...v5.11.0-dev.1) (2025-02-05)

### Features

* **YouTube - Change start page:** Add additional start pages ([#4413](https://github.com/ReVanced/revanced-patches/issues/4413)) ([b7ebfdd](b7ebfddf65))
2025-02-05 18:36:04 +00:00
LisoUseInAIKyrios
b7ebfddf65 feat(YouTube - Change start page): Add additional start pages (#4413) 2025-02-05 20:32:42 +02:00
github-actions[bot]
2742aca48b chore: Sync translations (#4414) 2025-02-05 20:32:21 +02:00
semantic-release-bot
14ca4d3288 chore: Release v5.10.1-dev.3 [skip ci]
## [5.10.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.2...v5.10.1-dev.3) (2025-02-03)

### Bug Fixes

* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([a06c031](a06c0318bf))
2025-02-03 10:15:53 +00:00
ILoveOpenSourceApplications
a06c0318bf fix(YouTube - Hide layout components): Hide new type of community post (#4404) 2025-02-03 12:13:15 +02:00
semantic-release-bot
7f9f668435 chore: Release v5.10.1-dev.2 [skip ci]
## [5.10.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.1...v5.10.1-dev.2) (2025-02-03)

### Bug Fixes

* **YouTube - Enable slide to seek:** Change patch to default include ([76fd33c](76fd33ca54))
2025-02-03 10:11:28 +00:00
LisoUseInAIKyrios
76fd33ca54 fix(YouTube - Enable slide to seek): Change patch to default include 2025-02-03 12:08:28 +02:00
semantic-release-bot
9a653e9c5a chore: Release v5.10.1-dev.1 [skip ci]
## [5.10.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.10.1-dev.1) (2025-02-02)

### Bug Fixes

* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([f81b658](f81b658fb7))
2025-02-02 09:14:21 +00:00
LisoUseInAIKyrios
f81b658fb7 fix(YouTube - Theme): Use custom seekbar color for cairo startup animation (#4399) 2025-02-02 11:10:57 +02:00
LisoUseInAIKyrios
7ff39d89d6 refactor(YouTube - Spoof app version): Use more concise description of 19.26.42 2025-01-31 12:51:49 +02:00
LisoUseInAIKyrios
78ab0ec2bd refactor(YouTube - Swipe controls): Use more consistent settings language of 'opacity' and 0-100 scale 2025-01-31 12:40:45 +02:00
semantic-release-bot
3ab67f1539 chore: Release v5.10.0 [skip ci]
# [5.10.0](https://github.com/ReVanced/revanced-patches/compare/v5.9.0...v5.10.0) (2025-01-31)

### Bug Fixes

* **SwissId - Play integrity Removal:** Add recommended app version ([#4370](https://github.com/ReVanced/revanced-patches/issues/4370)) ([6fa2dee](6fa2deea69))
* Use correct path to fix invalid file paths ([043ebbb](043ebbb6d4))
* **YouTube - Hide ads:** fix 'Hide the Visit store button on channel pages' not working ([#4364](https://github.com/ReVanced/revanced-patches/issues/4364)) ([a73db03](a73db03671))
* **YouTube - Hide Ads:** Hide end screen store banner without leaving empty space ([#4367](https://github.com/ReVanced/revanced-patches/issues/4367)) ([aaeee4a](aaeee4a895))
* **YouTube - Hide ads:** Hide new types of tablet ads ([f844a1c](f844a1cd76))
* **YouTube - Hide layout components:** Hide new kind of community post ([#4341](https://github.com/ReVanced/revanced-patches/issues/4341)) ([6721a28](6721a284cd))
* **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))
* **YouTube - Hide video description components:** Use correct string key names ([64cdce2](64cdce28a6))
* **YouTube - Spoof video streams:** Update settings side effects summary text ([#4369](https://github.com/ReVanced/revanced-patches/issues/4369)) ([6802529](680252967e))
* **YouTube - Theme:** Fix 19.25 - 19.45 patch error ([df2d070](df2d070a43))
* **YouTube - Theme:** Replace custom seekbar gradient colors instead of disabling ([#4329](https://github.com/ReVanced/revanced-patches/issues/4329)) ([f4989ed](f4989ed0a5))

### Features

* **YouTube - Hide ads:** Add `Hide end screen store banner` ([#4351](https://github.com/ReVanced/revanced-patches/issues/4351)) ([76bbd7e](76bbd7ed2f))
* **YouTube - Hide video description components:** Add `Hide How this content was made section` ([#4355](https://github.com/ReVanced/revanced-patches/issues/4355)) ([a72404e](a72404eeab))
* **YouTube - Theme:** Add option to use custom seekbar accent color ([#4337](https://github.com/ReVanced/revanced-patches/issues/4337)) ([8104bbd](8104bbd7d7))
* **YouTube:** Add patch `Disable HDR video` ([#4347](https://github.com/ReVanced/revanced-patches/issues/4347)) ([1d12c41](1d12c4156d))
2025-01-31 09:18:31 +00:00
LisoUseInAIKyrios
8652cd613f chore: Merge branch dev to main (#4330) 2025-01-31 11:15:01 +02:00
github-actions[bot]
bc8388713c chore: Sync translations (#4386) 2025-01-31 11:14:34 +02:00
github-actions[bot]
d4b2e3be3e chore: Sync translations (#4385) 2025-01-31 10:55:59 +02:00
LisoUseInAIKyrios
57c48b7829 ci: Fix Crowdin pull 2025-01-31 10:51:42 +02:00
LisoUseInAIKyrios
aaa7523ee4 chore: Add translatable string tags 2025-01-31 10:03:31 +02:00
LisoUseInAIKyrios
785df4fe69 ci: Preprocess strings before pushing to Crowdin (#4383) 2025-01-31 09:58:26 +02:00
github-actions[bot]
83208eb50d chore: Sync translations (#4382) 2025-01-30 09:36:37 +02:00
github-actions[bot]
9437db11eb chore: Sync translations (#4381) 2025-01-30 09:32:21 +02:00
github-actions[bot]
1843c8bf70 chore: Sync translations (#4380) 2025-01-30 09:27:20 +02:00
LisoUseInAIKyrios
778b51fbff ci: Fix Crowdin cron pull strings? 2025-01-30 09:25:20 +02:00
github-actions[bot]
ee0fdcdf86 chore: Sync translations (#4379) 2025-01-30 09:18:15 +02:00
semantic-release-bot
57cc73d9c4 chore: Release v5.10.0-dev.11 [skip ci]
# [5.10.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.10...v5.10.0-dev.11) (2025-01-30)

### Bug Fixes

* Use correct path to fix invalid file paths ([043ebbb](043ebbb6d4))
2025-01-30 00:58:00 +00:00
oSumAtrIX
043ebbb6d4 fix: Use correct path to fix invalid file paths 2025-01-30 01:53:44 +01:00
semantic-release-bot
d5551923fc chore: Release v5.10.0-dev.10 [skip ci]
# [5.10.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.9...v5.10.0-dev.10) (2025-01-29)

### Bug Fixes

* **YouTube - Hide ads:** Hide new types of tablet ads ([f844a1c](f844a1cd76))
2025-01-29 18:57:01 +00:00
LisoUseInAIKyrios
f844a1cd76 fix(YouTube - Hide ads): Hide new types of tablet ads 2025-01-29 20:52:57 +02:00
semantic-release-bot
a7e3277cc1 chore: Release v5.10.0-dev.9 [skip ci]
# [5.10.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.8...v5.10.0-dev.9) (2025-01-29)

### Bug Fixes

* **SwissId - Play integrity Removal:** Add recommended app version ([#4370](https://github.com/ReVanced/revanced-patches/issues/4370)) ([6fa2dee](6fa2deea69))
2025-01-29 17:47:51 +00:00
Corentin C
6fa2deea69 fix(SwissId - Play integrity Removal): Add recommended app version (#4370) 2025-01-29 19:44:27 +02:00
github-actions[bot]
dcca2a3697 chore: Sync translations (#4374) 2025-01-29 19:43:28 +02:00
semantic-release-bot
018160fd9c chore: Release v5.10.0-dev.8 [skip ci]
# [5.10.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.7...v5.10.0-dev.8) (2025-01-29)

### Bug Fixes

* **YouTube - Spoof video streams:** Update settings side effects summary text ([#4369](https://github.com/ReVanced/revanced-patches/issues/4369)) ([6802529](680252967e))
2025-01-29 14:04:01 +00:00
LisoUseInAIKyrios
680252967e fix(YouTube - Spoof video streams): Update settings side effects summary text (#4369) 2025-01-29 16:00:22 +02:00
semantic-release-bot
e79eba81d9 chore: Release v5.10.0-dev.7 [skip ci]
# [5.10.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.6...v5.10.0-dev.7) (2025-01-29)

### Bug Fixes

* **YouTube - Hide ads:** fix 'Hide the Visit store button on channel pages' not working ([#4364](https://github.com/ReVanced/revanced-patches/issues/4364)) ([a73db03](a73db03671))
2025-01-29 08:31:07 +00:00
ILoveOpenSourceApplications
a73db03671 fix(YouTube - Hide ads): fix 'Hide the Visit store button on channel pages' not working (#4364) 2025-01-29 10:28:26 +02:00
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
341 changed files with 17117 additions and 13565 deletions

View File

@@ -2,7 +2,7 @@ name: Pull strings
on: on:
schedule: schedule:
- cron: "0 */8 * * *" - cron: "0 */12 * * *"
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -26,6 +26,7 @@ jobs:
config: crowdin.yml config: crowdin.yml
upload_sources: false upload_sources: false
download_translations: true download_translations: true
skip_ref_checkout: true
localization_branch_name: feat/translations localization_branch_name: feat/translations
create_pull_request: false create_pull_request: false
env: env:

View File

@@ -18,6 +18,11 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Preprocess strings
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew clean preprocessCrowdinStrings
- name: Push strings - name: Push strings
uses: crowdin/github-action@v2 uses: crowdin/github-action@v2
with: with:

View File

@@ -1,3 +1,583 @@
# [5.16.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.15.0...v5.16.0-dev.1) (2025-03-24)
### Bug Fixes
* **YouTube - Settings:** System navigation bar is located above the settings ui on Android 15+ ([f7497be](https://github.com/ReVanced/revanced-patches/commit/f7497be2c5e4abcde6eb55b84955124a28f55cae))
### Features
* **YouTube - Comments:** Add `Hide AI Comments summary` ([#4634](https://github.com/ReVanced/revanced-patches/issues/4634)) ([e9b7f26](https://github.com/ReVanced/revanced-patches/commit/e9b7f263f739bd130f6ea79913851a52355977c5))
* **YouTube - Video description:** Add `Hide AI-generated video summary` ([#4636](https://github.com/ReVanced/revanced-patches/issues/4636)) ([521fd48](https://github.com/ReVanced/revanced-patches/commit/521fd48602432ab436d8711c19d7130b2b05af12))
# [5.15.0](https://github.com/ReVanced/revanced-patches/compare/v5.14.0...v5.15.0) (2025-03-21)
### Bug Fixes
* **YouTube - Spoof app version:** Change oldest spoof target to 19.01.34 ([5012439](https://github.com/ReVanced/revanced-patches/commit/5012439a8e53b2a4ab5e85c47976e1ab28a51208))
* **YouTube - Spoof app version:** Remove broken spoof targets that YouTube no longer supports ([#4610](https://github.com/ReVanced/revanced-patches/issues/4610)) ([883fbe7](https://github.com/ReVanced/revanced-patches/commit/883fbe71233c57cb1241e57c122b43f40722acc7))
* **YouTube:** Do not show restart prompt more than once if setting change is canceled ([49797fe](https://github.com/ReVanced/revanced-patches/commit/49797fe8d0c4a0981ef621a31356c4315ae3777b))
### Features
* **YouTube - SponsorBlock:** Add opacity setting to category segment colors ([#4582](https://github.com/ReVanced/revanced-patches/issues/4582)) ([6e8ffba](https://github.com/ReVanced/revanced-patches/commit/6e8ffbade9e03658f725622631e44dabf2995861))
# [5.15.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.3...v5.15.0-dev.4) (2025-03-21)
### Bug Fixes
* **YouTube - Spoof app version:** Change oldest spoof target to 19.01.34 ([5012439](https://github.com/ReVanced/revanced-patches/commit/5012439a8e53b2a4ab5e85c47976e1ab28a51208))
# [5.15.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.2...v5.15.0-dev.3) (2025-03-20)
### Bug Fixes
* **YouTube:** Do not show restart prompt more than once if setting change is canceled ([49797fe](https://github.com/ReVanced/revanced-patches/commit/49797fe8d0c4a0981ef621a31356c4315ae3777b))
# [5.15.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.1...v5.15.0-dev.2) (2025-03-19)
### Bug Fixes
* **YouTube - Spoof app version:** Remove broken spoof targets that YouTube no longer supports ([#4610](https://github.com/ReVanced/revanced-patches/issues/4610)) ([883fbe7](https://github.com/ReVanced/revanced-patches/commit/883fbe71233c57cb1241e57c122b43f40722acc7))
# [5.15.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.14.0...v5.15.0-dev.1) (2025-03-19)
### Features
* **YouTube - SponsorBlock:** Add opacity setting to category segment colors ([#4582](https://github.com/ReVanced/revanced-patches/issues/4582)) ([6e8ffba](https://github.com/ReVanced/revanced-patches/commit/6e8ffbade9e03658f725622631e44dabf2995861))
# [5.14.0](https://github.com/ReVanced/revanced-patches/compare/v5.13.0...v5.14.0) (2025-03-09)
### Bug Fixes
* **Boost for reddit - Client spoof:** Use a different user agent to combat Reddit's API issues ([5d3c817](https://github.com/ReVanced/revanced-patches/commit/5d3c8175b34a3f6ae2732b25db0851773a8c000d))
* **YouTube - Change form factor:** Restore Automotive form factor watch history menu, channel pages, and community posts ([#4541](https://github.com/ReVanced/revanced-patches/issues/4541)) ([aa5c001](https://github.com/ReVanced/revanced-patches/commit/aa5c001968446e5270c756256724e917009612cd))
* **YouTube - Hide ads:** Hide new type of buttoned ad ([#4528](https://github.com/ReVanced/revanced-patches/issues/4528)) ([4387a7b](https://github.com/ReVanced/revanced-patches/commit/4387a7b131f49729e902e008bb4cec073635c040))
* **YouTube - Hide layout components:** Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled ([62a6164](https://github.com/ReVanced/revanced-patches/commit/62a6164b88b64200b517a5ba6b800d8214dbbad8))
* **YouTube - Theme:** Resolve dark mode startup crash with Android 9.0 ([741c2d5](https://github.com/ReVanced/revanced-patches/commit/741c2d59406f5d602554bb3a3c0b8982f42848b4))
* **YouTube:** Change language settings menu to use native language names ([#4568](https://github.com/ReVanced/revanced-patches/issues/4568)) ([6f3f8fd](https://github.com/ReVanced/revanced-patches/commit/6f3f8fdce05501e4fa4423c2170a916fbea3b199))
* **YouTube:** Combine `Restore old video quality menu` and `Remember video quality` into `Video quality` patch ([#4552](https://github.com/ReVanced/revanced-patches/issues/4552)) ([ee67b76](https://github.com/ReVanced/revanced-patches/commit/ee67b763d5c5947a5b1ef4420b1efa820ed6af83))
### Features
* **Infinity for Reddit:** Add support for package name on IzzyOnDroid ([#4554](https://github.com/ReVanced/revanced-patches/issues/4554)) ([cf9f959](https://github.com/ReVanced/revanced-patches/commit/cf9f959923076c10a7f0a29f6ba277f5a055ec07))
* **Spotify:** Add `Spoof signature` patch ([#4576](https://github.com/ReVanced/revanced-patches/issues/4576)) ([3646c70](https://github.com/ReVanced/revanced-patches/commit/3646c70556b67a6b7ecf9b86869ebf03c3611333))
* **YouTube - Remember video quality:** Add separate Shorts default quality settings ([#4543](https://github.com/ReVanced/revanced-patches/issues/4543)) ([88142ab](https://github.com/ReVanced/revanced-patches/commit/88142ab464192b564b1b8d56a6b45663f77f5e00))
# [5.14.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.8...v5.14.0-dev.9) (2025-03-09)
### Features
* **Spotify:** Add `Spoof signature` patch ([#4576](https://github.com/ReVanced/revanced-patches/issues/4576)) ([3646c70](https://github.com/ReVanced/revanced-patches/commit/3646c70556b67a6b7ecf9b86869ebf03c3611333))
# [5.14.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.7...v5.14.0-dev.8) (2025-03-09)
### Bug Fixes
* **YouTube - Theme:** Resolve dark mode startup crash with Android 9.0 ([741c2d5](https://github.com/ReVanced/revanced-patches/commit/741c2d59406f5d602554bb3a3c0b8982f42848b4))
# [5.14.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.6...v5.14.0-dev.7) (2025-03-08)
### Bug Fixes
* **YouTube:** Change language settings menu to use native language names ([#4568](https://github.com/ReVanced/revanced-patches/issues/4568)) ([6f3f8fd](https://github.com/ReVanced/revanced-patches/commit/6f3f8fdce05501e4fa4423c2170a916fbea3b199))
# [5.14.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.5...v5.14.0-dev.6) (2025-03-07)
### Bug Fixes
* **YouTube - Hide layout components:** Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled ([62a6164](https://github.com/ReVanced/revanced-patches/commit/62a6164b88b64200b517a5ba6b800d8214dbbad8))
# [5.14.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.4...v5.14.0-dev.5) (2025-03-06)
### Features
* **Infinity for Reddit:** Add support for package name on IzzyOnDroid ([#4554](https://github.com/ReVanced/revanced-patches/issues/4554)) ([cf9f959](https://github.com/ReVanced/revanced-patches/commit/cf9f959923076c10a7f0a29f6ba277f5a055ec07))
# [5.14.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.3...v5.14.0-dev.4) (2025-03-06)
### Bug Fixes
* **YouTube:** Combine `Restore old video quality menu` and `Remember video quality` into `Video quality` patch ([#4552](https://github.com/ReVanced/revanced-patches/issues/4552)) ([ee67b76](https://github.com/ReVanced/revanced-patches/commit/ee67b763d5c5947a5b1ef4420b1efa820ed6af83))
# [5.14.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.2...v5.14.0-dev.3) (2025-03-06)
### Bug Fixes
* **Boost for reddit - Client spoof:** Use a different user agent to combat Reddit's API issues ([5d3c817](https://github.com/ReVanced/revanced-patches/commit/5d3c8175b34a3f6ae2732b25db0851773a8c000d))
# [5.14.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.1...v5.14.0-dev.2) (2025-03-06)
### Bug Fixes
* **YouTube - Hide ads:** Hide new type of buttoned ad ([#4528](https://github.com/ReVanced/revanced-patches/issues/4528)) ([4387a7b](https://github.com/ReVanced/revanced-patches/commit/4387a7b131f49729e902e008bb4cec073635c040))
# [5.14.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.1-dev.1...v5.14.0-dev.1) (2025-03-06)
### Features
* **YouTube - Remember video quality:** Add separate Shorts default quality settings ([#4543](https://github.com/ReVanced/revanced-patches/issues/4543)) ([88142ab](https://github.com/ReVanced/revanced-patches/commit/88142ab464192b564b1b8d56a6b45663f77f5e00))
## [5.13.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.0...v5.13.1-dev.1) (2025-03-06)
### Bug Fixes
* **YouTube - Change form factor:** Restore Automotive form factor watch history menu, channel pages, and community posts ([#4541](https://github.com/ReVanced/revanced-patches/issues/4541)) ([aa5c001](https://github.com/ReVanced/revanced-patches/commit/aa5c001968446e5270c756256724e917009612cd))
# [5.13.0](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0) (2025-03-03)
### Bug Fixes
* **TikTok:** Resolve startup app crash ([18c0fc2](https://github.com/ReVanced/revanced-patches/commit/18c0fc2a7f186f50a904fd25dbaa739abdd24993))
* **TikTok:** Resolve startup app crash ([6466398](https://github.com/ReVanced/revanced-patches/commit/64663983b84de1f28636205f61bf0a24c83968d1))
* **TikTok:** Resolve startup app crash ([c14bc24](https://github.com/ReVanced/revanced-patches/commit/c14bc244550de30eca975ca7c09e8eb0c47534b5))
* **TikTok:** Resolve startup app crash ([d700076](https://github.com/ReVanced/revanced-patches/commit/d7000768a5e5a688c9f4e48858ac34e352222c1e))
* **YouTube - Copy video URL:** Use correct button ordering ([5e622cc](https://github.com/ReVanced/revanced-patches/commit/5e622ccf66d34af31c6026fa7f4d332460c6ecb0))
* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([634d0ee](https://github.com/ReVanced/revanced-patches/commit/634d0ee12e31491c7ee1d4ceb002daf8366a3c15))
* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([4ac8854](https://github.com/ReVanced/revanced-patches/commit/4ac8854b99808a8957f3b0b7438e1e0cdedffbaf))
* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([6c4885a](https://github.com/ReVanced/revanced-patches/commit/6c4885a1d5dfff50100b01840b5552d92e83ee4a))
* **YouTube - Hide video action buttons:** Move 'Disable Like and Subscribe glow' to action buttons settings menu ([29b265d](https://github.com/ReVanced/revanced-patches/commit/29b265d8fdaa48502650be9623bfc518a57a0bb1))
* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([edf66f4](https://github.com/ReVanced/revanced-patches/commit/edf66f4e16d46156cb8b8e31d18cb8dbcb87737e))
* **YouTube - Spoof app version:** Force old settings menus if spoofing to older app targets ([#4490](https://github.com/ReVanced/revanced-patches/issues/4490)) ([45e7c46](https://github.com/ReVanced/revanced-patches/commit/45e7c46dd9c70c926b8b1a97ada668f90f5f6f8c))
* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([647e764](https://github.com/ReVanced/revanced-patches/commit/647e7642efc0c00db17ccb6a620d1c96ccf4afed))
* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([6dc4bf7](https://github.com/ReVanced/revanced-patches/commit/6dc4bf75e09ed6f05534919d7b769b720043abce))
* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([63fe870](https://github.com/ReVanced/revanced-patches/commit/63fe870d48ca2217327b952bde241b7f16ced850))
* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([bf8e775](https://github.com/ReVanced/revanced-patches/commit/bf8e7759f9bdbdfef419a879fb3dd7cf0dff0098))
* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([1f08047](https://github.com/ReVanced/revanced-patches/commit/1f08047b48cc9555a4887d16ec7219a55a77251f))
### Features
* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([d74732b](https://github.com/ReVanced/revanced-patches/commit/d74732b7596104321bde263201d95649e4bd0eee))
* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([f3268fb](https://github.com/ReVanced/revanced-patches/commit/f3268fb03ca25fb5465e36015b6c9dec2c84a655))
* **YouTube - Navigation buttons:** Add 'Hide notifications' setting ([#4485](https://github.com/ReVanced/revanced-patches/issues/4485)) ([506d241](https://github.com/ReVanced/revanced-patches/commit/506d2414bbc760e764e5a514b32926083d6ecb6b))
* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([198e4d2](https://github.com/ReVanced/revanced-patches/commit/198e4d2a2315c24a09eb9ecfefbd131a75384d2c))
# [5.13.0-dev.19](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.18...v5.13.0-dev.19) (2025-03-02)
### Bug Fixes
* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([647e764](https://github.com/ReVanced/revanced-patches/commit/647e7642efc0c00db17ccb6a620d1c96ccf4afed))
# [5.13.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.17...v5.13.0-dev.18) (2025-02-28)
### Features
* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([d74732b](https://github.com/ReVanced/revanced-patches/commit/d74732b7596104321bde263201d95649e4bd0eee))
# [5.13.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.16...v5.13.0-dev.17) (2025-02-27)
### Bug Fixes
* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([634d0ee](https://github.com/ReVanced/revanced-patches/commit/634d0ee12e31491c7ee1d4ceb002daf8366a3c15))
# [5.13.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.15...v5.13.0-dev.16) (2025-02-27)
### Features
* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([f3268fb](https://github.com/ReVanced/revanced-patches/commit/f3268fb03ca25fb5465e36015b6c9dec2c84a655))
# [5.13.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.14...v5.13.0-dev.15) (2025-02-25)
### Bug Fixes
* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([6c4885a](https://github.com/ReVanced/revanced-patches/commit/6c4885a1d5dfff50100b01840b5552d92e83ee4a))
# [5.13.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.13...v5.13.0-dev.14) (2025-02-25)
### Bug Fixes
* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([6dc4bf7](https://github.com/ReVanced/revanced-patches/commit/6dc4bf75e09ed6f05534919d7b769b720043abce))
# [5.13.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.12...v5.13.0-dev.13) (2025-02-24)
### Bug Fixes
* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([1f08047](https://github.com/ReVanced/revanced-patches/commit/1f08047b48cc9555a4887d16ec7219a55a77251f))
# [5.13.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.11...v5.13.0-dev.12) (2025-02-24)
### Bug Fixes
* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([edf66f4](https://github.com/ReVanced/revanced-patches/commit/edf66f4e16d46156cb8b8e31d18cb8dbcb87737e))
# [5.13.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.10...v5.13.0-dev.11) (2025-02-23)
### Bug Fixes
* **TikTok:** Resolve startup app crash ([18c0fc2](https://github.com/ReVanced/revanced-patches/commit/18c0fc2a7f186f50a904fd25dbaa739abdd24993))
# [5.13.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.9...v5.13.0-dev.10) (2025-02-22)
### Bug Fixes
* **YouTube - Copy video URL:** Use correct button ordering ([5e622cc](https://github.com/ReVanced/revanced-patches/commit/5e622ccf66d34af31c6026fa7f4d332460c6ecb0))
# [5.13.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.8...v5.13.0-dev.9) (2025-02-22)
### Bug Fixes
* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([63fe870](https://github.com/ReVanced/revanced-patches/commit/63fe870d48ca2217327b952bde241b7f16ced850))
# [5.13.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.7...v5.13.0-dev.8) (2025-02-22)
### Bug Fixes
* **YouTube - Spoof app version:** Force old settings menus if spoofing to older app targets ([#4490](https://github.com/ReVanced/revanced-patches/issues/4490)) ([45e7c46](https://github.com/ReVanced/revanced-patches/commit/45e7c46dd9c70c926b8b1a97ada668f90f5f6f8c))
# [5.13.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.6...v5.13.0-dev.7) (2025-02-22)
### Bug Fixes
* **TikTok:** Resolve startup app crash ([6466398](https://github.com/ReVanced/revanced-patches/commit/64663983b84de1f28636205f61bf0a24c83968d1))
# [5.13.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.5...v5.13.0-dev.6) (2025-02-21)
### Features
* **YouTube - Navigation buttons:** Add 'Hide notifications' setting ([#4485](https://github.com/ReVanced/revanced-patches/issues/4485)) ([506d241](https://github.com/ReVanced/revanced-patches/commit/506d2414bbc760e764e5a514b32926083d6ecb6b))
# [5.13.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.4...v5.13.0-dev.5) (2025-02-19)
### Bug Fixes
* **TikTok:** Resolve startup app crash ([c14bc24](https://github.com/ReVanced/revanced-patches/commit/c14bc244550de30eca975ca7c09e8eb0c47534b5))
# [5.13.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.3...v5.13.0-dev.4) (2025-02-19)
### Bug Fixes
* **TikTok:** Resolve startup app crash ([d700076](https://github.com/ReVanced/revanced-patches/commit/d7000768a5e5a688c9f4e48858ac34e352222c1e))
# [5.13.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.2...v5.13.0-dev.3) (2025-02-19)
### Bug Fixes
* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([bf8e775](https://github.com/ReVanced/revanced-patches/commit/bf8e7759f9bdbdfef419a879fb3dd7cf0dff0098))
# [5.13.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.1...v5.13.0-dev.2) (2025-02-18)
### Bug Fixes
* **YouTube - Hide video action buttons:** Move 'Disable Like and Subscribe glow' to action buttons settings menu ([29b265d](https://github.com/ReVanced/revanced-patches/commit/29b265d8fdaa48502650be9623bfc518a57a0bb1))
# [5.13.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0-dev.1) (2025-02-18)
### Bug Fixes
* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([4ac8854](https://github.com/ReVanced/revanced-patches/commit/4ac8854b99808a8957f3b0b7438e1e0cdedffbaf))
### Features
* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([198e4d2](https://github.com/ReVanced/revanced-patches/commit/198e4d2a2315c24a09eb9ecfefbd131a75384d2c))
# [5.12.0](https://github.com/ReVanced/revanced-patches/compare/v5.11.0...v5.12.0) (2025-02-17)
### Bug Fixes
* Allow changing default settings for existing app installs ([#4464](https://github.com/ReVanced/revanced-patches/issues/4464)) ([1bd7986](https://github.com/ReVanced/revanced-patches/commit/1bd7986823e774a929c8a9102a7cc96e245d5274))
* **Windy.app:** Remove obsolete `Unlock pro` patch ([#4428](https://github.com/ReVanced/revanced-patches/issues/4428)) ([83d116e](https://github.com/ReVanced/revanced-patches/commit/83d116e8fd3935ee431cfdf0b8e095d04ee77259))
* **YouTube - Spoof video streams:** Change default client to `Android TV` ([#4465](https://github.com/ReVanced/revanced-patches/issues/4465)) ([0412c79](https://github.com/ReVanced/revanced-patches/commit/0412c7901dc8599b6079d9c3ba26452f88af642b))
* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([a006758](https://github.com/ReVanced/revanced-patches/commit/a0067581d0f877e1b4eb1f888a25786f09676b2e))
### Features
* **Return YouTube Dislike:** add `Show estimated likes` setting ([#4443](https://github.com/ReVanced/revanced-patches/issues/4443)) ([9a88b42](https://github.com/ReVanced/revanced-patches/commit/9a88b4239fd63d5f91105fec8e7d59d318a5d09a))
* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([8f4883f](https://github.com/ReVanced/revanced-patches/commit/8f4883fc002420bfb4056401e23445c99e1d3fce))
* **YouTube Music:** Support version `8.05.50` ([#4439](https://github.com/ReVanced/revanced-patches/issues/4439)) ([b31fed9](https://github.com/ReVanced/revanced-patches/commit/b31fed98901fcda1bce6f05eb0de63280c689fa0))
* **YouTube Music:** Support version `8.05.51` ([128441e](https://github.com/ReVanced/revanced-patches/commit/128441e78bc0d096c3fc2f57782ab90c39c3ae4b))
# [5.12.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.6...v5.12.0-dev.7) (2025-02-16)
### Bug Fixes
* **YouTube - Spoof video streams:** Change default client to `Android TV` ([#4465](https://github.com/ReVanced/revanced-patches/issues/4465)) ([0412c79](https://github.com/ReVanced/revanced-patches/commit/0412c7901dc8599b6079d9c3ba26452f88af642b))
### Features
* **YouTube Music:** Support version `8.05.51` ([128441e](https://github.com/ReVanced/revanced-patches/commit/128441e78bc0d096c3fc2f57782ab90c39c3ae4b))
# [5.12.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.5...v5.12.0-dev.6) (2025-02-16)
### Bug Fixes
* Allow changing default settings for existing app installs ([#4464](https://github.com/ReVanced/revanced-patches/issues/4464)) ([1bd7986](https://github.com/ReVanced/revanced-patches/commit/1bd7986823e774a929c8a9102a7cc96e245d5274))
# [5.12.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.4...v5.12.0-dev.5) (2025-02-13)
### Bug Fixes
* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([a006758](https://github.com/ReVanced/revanced-patches/commit/a0067581d0f877e1b4eb1f888a25786f09676b2e))
# [5.12.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.3...v5.12.0-dev.4) (2025-02-11)
### Features
* **YouTube Music:** Support version `8.05.50` ([#4439](https://github.com/ReVanced/revanced-patches/issues/4439)) ([b31fed9](https://github.com/ReVanced/revanced-patches/commit/b31fed98901fcda1bce6f05eb0de63280c689fa0))
# [5.12.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.2...v5.12.0-dev.3) (2025-02-11)
### Bug Fixes
* **Windy.app:** Remove obsolete `Unlock pro` patch ([#4428](https://github.com/ReVanced/revanced-patches/issues/4428)) ([83d116e](https://github.com/ReVanced/revanced-patches/commit/83d116e8fd3935ee431cfdf0b8e095d04ee77259))
# [5.12.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.1...v5.12.0-dev.2) (2025-02-11)
### Features
* **Return YouTube Dislike:** add `Show estimated likes` setting ([#4443](https://github.com/ReVanced/revanced-patches/issues/4443)) ([9a88b42](https://github.com/ReVanced/revanced-patches/commit/9a88b4239fd63d5f91105fec8e7d59d318a5d09a))
# [5.12.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.11.0...v5.12.0-dev.1) (2025-02-10)
### Features
* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([8f4883f](https://github.com/ReVanced/revanced-patches/commit/8f4883fc002420bfb4056401e23445c99e1d3fce))
# [5.11.0](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.11.0) (2025-02-07)
### Bug Fixes
* Fix broken `Remove screen capture restriction`, `Remove screenshot restriction`, `Spoof Wi-Fi connection`, and `Export internal data documents provider` patch ([#4405](https://github.com/ReVanced/revanced-patches/issues/4405)) ([1d52b74](https://github.com/ReVanced/revanced-patches/commit/1d52b7478d34e699d8c629eeaa9fdbb470b7d5c8))
* **YouTube - Enable slide to seek:** Change patch to default include ([50358cd](https://github.com/ReVanced/revanced-patches/commit/50358cddea3eef4051d248040d23f774521dce00))
* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([f67ab2b](https://github.com/ReVanced/revanced-patches/commit/f67ab2baf25d543ceb55fcec48bda441ebf2b998))
* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([1cba294](https://github.com/ReVanced/revanced-patches/commit/1cba2948a6787118eb380ffcec35ee4fb99447ea))
### Features
* **YouTube - Change start page:** Add additional start pages ([#4413](https://github.com/ReVanced/revanced-patches/issues/4413)) ([b434182](https://github.com/ReVanced/revanced-patches/commit/b434182df69313c4eb5f0dfd98101cb80e46ead2))
# [5.11.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.11.0-dev.1...v5.11.0-dev.2) (2025-02-06)
### Bug Fixes
* Fix broken `Remove screen capture restriction`, `Remove screenshot restriction`, `Spoof Wi-Fi connection`, and `Export internal data documents provider` patch ([#4405](https://github.com/ReVanced/revanced-patches/issues/4405)) ([1d52b74](https://github.com/ReVanced/revanced-patches/commit/1d52b7478d34e699d8c629eeaa9fdbb470b7d5c8))
# [5.11.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.3...v5.11.0-dev.1) (2025-02-05)
### Features
* **YouTube - Change start page:** Add additional start pages ([#4413](https://github.com/ReVanced/revanced-patches/issues/4413)) ([b434182](https://github.com/ReVanced/revanced-patches/commit/b434182df69313c4eb5f0dfd98101cb80e46ead2))
## [5.10.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.2...v5.10.1-dev.3) (2025-02-03)
### Bug Fixes
* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([f67ab2b](https://github.com/ReVanced/revanced-patches/commit/f67ab2baf25d543ceb55fcec48bda441ebf2b998))
## [5.10.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.1...v5.10.1-dev.2) (2025-02-03)
### Bug Fixes
* **YouTube - Enable slide to seek:** Change patch to default include ([50358cd](https://github.com/ReVanced/revanced-patches/commit/50358cddea3eef4051d248040d23f774521dce00))
## [5.10.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.10.1-dev.1) (2025-02-02)
### Bug Fixes
* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([1cba294](https://github.com/ReVanced/revanced-patches/commit/1cba2948a6787118eb380ffcec35ee4fb99447ea))
# [5.10.0](https://github.com/ReVanced/revanced-patches/compare/v5.9.0...v5.10.0) (2025-01-31)
### Bug Fixes
* **SwissId - Play integrity Removal:** Add recommended app version ([#4370](https://github.com/ReVanced/revanced-patches/issues/4370)) ([d8ed474](https://github.com/ReVanced/revanced-patches/commit/d8ed474b165f094fdedc32caaae1f82ebc99eb3d))
* Use correct path to fix invalid file paths ([5ff4ee8](https://github.com/ReVanced/revanced-patches/commit/5ff4ee823da55c7b135eab8b62e07be465612b55))
* **YouTube - Hide ads:** fix 'Hide the Visit store button on channel pages' not working ([#4364](https://github.com/ReVanced/revanced-patches/issues/4364)) ([9d63ea9](https://github.com/ReVanced/revanced-patches/commit/9d63ea9a10ab5128ce18a1f53a946e84550da258))
* **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))
* **YouTube - Hide ads:** Hide new types of tablet ads ([574bcc8](https://github.com/ReVanced/revanced-patches/commit/574bcc844746b7445ec3e93b47daceafefad85e7))
* **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))
* **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))
* **YouTube - Hide video description components:** Use correct string key names ([0f28c2b](https://github.com/ReVanced/revanced-patches/commit/0f28c2b44c0051ea7ab3136433b84c73321cf5bd))
* **YouTube - Spoof video streams:** Update settings side effects summary text ([#4369](https://github.com/ReVanced/revanced-patches/issues/4369)) ([e5b3aa1](https://github.com/ReVanced/revanced-patches/commit/e5b3aa1cc6a2465cb006487d528de888bc7cd430))
* **YouTube - Theme:** Fix 19.25 - 19.45 patch error ([5b47a5f](https://github.com/ReVanced/revanced-patches/commit/5b47a5f0f6299daaae209341064fd85f16ca18a6))
* **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))
### 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))
* **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))
* **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))
* **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.11](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.10...v5.10.0-dev.11) (2025-01-30)
### Bug Fixes
* Use correct path to fix invalid file paths ([5ff4ee8](https://github.com/ReVanced/revanced-patches/commit/5ff4ee823da55c7b135eab8b62e07be465612b55))
# [5.10.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.9...v5.10.0-dev.10) (2025-01-29)
### Bug Fixes
* **YouTube - Hide ads:** Hide new types of tablet ads ([574bcc8](https://github.com/ReVanced/revanced-patches/commit/574bcc844746b7445ec3e93b47daceafefad85e7))
# [5.10.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.8...v5.10.0-dev.9) (2025-01-29)
### Bug Fixes
* **SwissId - Play integrity Removal:** Add recommended app version ([#4370](https://github.com/ReVanced/revanced-patches/issues/4370)) ([d8ed474](https://github.com/ReVanced/revanced-patches/commit/d8ed474b165f094fdedc32caaae1f82ebc99eb3d))
# [5.10.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.7...v5.10.0-dev.8) (2025-01-29)
### Bug Fixes
* **YouTube - Spoof video streams:** Update settings side effects summary text ([#4369](https://github.com/ReVanced/revanced-patches/issues/4369)) ([e5b3aa1](https://github.com/ReVanced/revanced-patches/commit/e5b3aa1cc6a2465cb006487d528de888bc7cd430))
# [5.10.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.6...v5.10.0-dev.7) (2025-01-29)
### Bug Fixes
* **YouTube - Hide ads:** fix 'Hide the Visit store button on channel pages' not working ([#4364](https://github.com/ReVanced/revanced-patches/issues/4364)) ([9d63ea9](https://github.com/ReVanced/revanced-patches/commit/9d63ea9a10ab5128ce18a1f53a946e84550da258))
# [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) # [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)

View File

@@ -1,4 +1,11 @@
android.namespace = "app.revanced.extension" android {
namespace = "app.revanced.extension"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies { dependencies {
compileOnly(libs.annotation) compileOnly(libs.annotation)

View File

@@ -12,7 +12,7 @@ import android.os.Handler;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
/** @noinspection deprecation, unused */ @SuppressWarnings({"deprecation", "unused"})
public class SpoofWifiPatch { public class SpoofWifiPatch {
// Used to check what the (real or fake) active network is (take a look at `hasTransport`). // Used to check what the (real or fake) active network is (take a look at `hasTransport`).

View File

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

View File

@@ -1,5 +1,6 @@
package app.revanced.extension.all.misc.directory.documentsprovider; package app.revanced.extension.all.misc.directory.documentsprovider;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo; import android.content.pm.ProviderInfo;
@@ -23,6 +24,7 @@ import java.util.Objects;
/** /**
* A DocumentsProvider that allows access to the app's internal data directory. * A DocumentsProvider that allows access to the app's internal data directory.
*/ */
@SuppressLint("LongLogTag")
public class InternalDataDocumentsProvider extends DocumentsProvider { public class InternalDataDocumentsProvider extends DocumentsProvider {
private static final String[] rootColumns = private static final String[] rootColumns =
{"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"}; {"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"};

View File

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

View File

@@ -5,7 +5,8 @@ import android.os.Build;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
public final class RemoveScreencaptureRestrictionPatch { @SuppressWarnings("unused")
public final class RemoveScreenCaptureRestrictionPatch {
// Member of AudioAttributes.Builder // Member of AudioAttributes.Builder
@RequiresApi(api = Build.VERSION_CODES.Q) @RequiresApi(api = Build.VERSION_CODES.Q)
public static AudioAttributes.Builder setAllowedCapturePolicy(final AudioAttributes.Builder builder, final int capturePolicy) { public static AudioAttributes.Builder setAllowedCapturePolicy(final AudioAttributes.Builder builder, final int capturePolicy) {

View File

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

View File

@@ -3,6 +3,7 @@ package app.revanced.extension.all.screenshot.removerestriction;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
@SuppressWarnings("unused")
public class RemoveScreenshotRestrictionPatch { public class RemoveScreenshotRestrictionPatch {
public static void addFlags(Window window, int flags) { public static void addFlags(Window window, int flags) {

View File

@@ -4,7 +4,7 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24

View File

@@ -1 +1,5 @@
// Do not remove. Necessary for the extension plugin to be applied to the project. android {
defaultConfig {
minSdk = 26
}
}

View File

@@ -0,0 +1,4 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:nunl:stub"))
}

View File

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

View File

@@ -0,0 +1,114 @@
package app.revanced.extension.nunl.ads;
import nl.nu.performance.api.client.interfaces.Block;
import nl.nu.performance.api.client.unions.SmallArticleLinkFlavor;
import nl.nu.performance.api.client.objects.*;
import java.util.ArrayList;
import java.util.List;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class HideAdsPatch {
private static final String[] blockedHeaderBlocks = {
"Aanbiedingen (Adverteerders)",
"Aangeboden door NUshop"
};
// "Rubrieken" menu links to ads.
private static final String[] blockedLinkBlocks = {
"Van onze adverteerders"
};
public static void filterAds(List<Block> blocks) {
try {
ArrayList<Block> cleanedList = new ArrayList<>();
boolean skipFullHeader = false;
boolean skipUntilDivider = false;
int index = 0;
while (index < blocks.size()) {
Block currentBlock = blocks.get(index);
// Because of pagination, we might not see the Divider in front of it.
// Just remove it as is and leave potential extra spacing visible on the screen.
if (currentBlock instanceof DpgBannerBlock) {
index++;
continue;
}
if (index + 1 < blocks.size()) {
// Filter Divider -> DpgMediaBanner -> Divider.
if (currentBlock instanceof DividerBlock
&& blocks.get(index + 1) instanceof DpgBannerBlock) {
index += 2;
continue;
}
// Filter Divider -> LinkBlock (... -> LinkBlock -> LinkBlock-> LinkBlock -> Divider).
if (currentBlock instanceof DividerBlock
&& blocks.get(index + 1) instanceof LinkBlock linkBlock) {
Link link = linkBlock.getLink();
if (link != null && link.getTitle() != null) {
for (String blockedLinkBlock : blockedLinkBlocks) {
if (blockedLinkBlock.equals(link.getTitle().getText())) {
skipUntilDivider = true;
break;
}
}
if (skipUntilDivider) {
index++;
continue;
}
}
}
}
// Skip LinkBlocks with a "flavor" claiming to be "isPartner" (sponsored inline ads).
if (currentBlock instanceof LinkBlock linkBlock
&& linkBlock.getLink() != null
&& linkBlock.getLink().getLinkFlavor() instanceof SmallArticleLinkFlavor smallArticleLinkFlavor
&& smallArticleLinkFlavor.isPartner() != null
&& smallArticleLinkFlavor.isPartner()) {
index++;
continue;
}
if (currentBlock instanceof DividerBlock) {
skipUntilDivider = false;
}
// Filter HeaderBlock with known ads until next HeaderBlock.
if (currentBlock instanceof HeaderBlock headerBlock) {
StyledText headerText = headerBlock.component20();
if (headerText != null) {
skipFullHeader = false;
for (String blockedHeaderBlock : blockedHeaderBlocks) {
if (blockedHeaderBlock.equals(headerText.getText())) {
skipFullHeader = true;
break;
}
}
if (skipFullHeader) {
index++;
continue;
}
}
}
if (!skipFullHeader && !skipUntilDivider) {
cleanedList.add(currentBlock);
}
index++;
}
// Replace list in-place to not deal with moving the result to the correct register in smali.
blocks.clear();
blocks.addAll(cleanedList);
} catch (Exception ex) {
Logger.printException(() -> "filterAds failure", ex);
}
}
}

View File

@@ -0,0 +1,17 @@
plugins {
id(libs.plugins.android.library.get().pluginId)
}
android {
namespace = "app.revanced.extension"
compileSdk = 34
defaultConfig {
minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}

View File

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

View File

@@ -0,0 +1,5 @@
package nl.nu.performance.api.client.interfaces;
public class Block {
}

View File

@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;
import nl.nu.performance.api.client.interfaces.Block;
public class DividerBlock extends Block {
}

View File

@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;
import nl.nu.performance.api.client.interfaces.Block;
public class DpgBannerBlock extends Block {
}

View File

@@ -0,0 +1,10 @@
package nl.nu.performance.api.client.objects;
import nl.nu.performance.api.client.interfaces.Block;
public class HeaderBlock extends Block {
// returns title
public final StyledText component20() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,13 @@
package nl.nu.performance.api.client.objects;
import nl.nu.performance.api.client.unions.LinkFlavor;
public class Link {
public final StyledText getTitle() {
throw new UnsupportedOperationException("Stub");
}
public final LinkFlavor getLinkFlavor() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,10 @@
package nl.nu.performance.api.client.objects;
import android.os.Parcelable;
import nl.nu.performance.api.client.interfaces.Block;
public abstract class LinkBlock extends Block implements Parcelable {
public final Link getLink() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.objects;
public class StyledText {
public final String getText() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,4 @@
package nl.nu.performance.api.client.unions;
public interface LinkFlavor {
}

View File

@@ -0,0 +1,7 @@
package nl.nu.performance.api.client.unions;
public class SmallArticleLinkFlavor implements LinkFlavor {
public final Boolean isPartner() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -4,7 +4,7 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24

View File

@@ -356,33 +356,24 @@ public class Utils {
public static Context getContext() { public static Context getContext() {
if (context == null) { if (context == null) {
Logger.initializationException(Utils.class, "Context is null, returning null!", null); Logger.initializationException(Utils.class, "Context is not set by extension hook, returning null", null);
} }
return context; return context;
} }
public static void setContext(Context appContext) { public static void setContext(Context appContext) {
// Must initially set context as the language settings needs it. // Must initially set context to check the app language.
context = appContext; context = appContext;
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get(); AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language != AppLanguage.DEFAULT) { if (language != AppLanguage.DEFAULT) {
// Create a new context with the desired language. // Create a new context with the desired language.
Logger.printDebug(() -> "Using app language: " + language);
Configuration config = appContext.getResources().getConfiguration(); Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale()); config.setLocale(language.getLocale());
context = appContext.createConfigurationContext(config); 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.
//
// The initialization logger methods do not directly or indirectly
// reference the Context or any Settings and are unaffected by this problem.
//
// Info level also helps debug if a patch hook is called before
// the context is set since debug logging is off by default.
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
} }
public static void setClipboard(@NonNull String text) { public static void setClipboard(@NonNull String text) {

View File

@@ -8,6 +8,9 @@ public enum AppLanguage {
*/ */
DEFAULT, DEFAULT,
// Languages codes not included with YouTube, but are translated on Crowdin
GA,
// Language codes found in locale_config.xml // Language codes found in locale_config.xml
// All region specific variants have been removed. // All region specific variants have been removed.
AF, AF,

View File

@@ -29,6 +29,6 @@ public class BaseSettings {
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true, 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 SpoofiOSAvailability()); "revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
// Client type must be last spoof setting due to cyclic references. // 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)); public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_UNPLUGGED, true, parent(SPOOF_VIDEO_STREAMS));
} }

View File

@@ -47,6 +47,10 @@ public class BooleanSetting extends Setting<Boolean> {
*/ */
public static void privateSetValue(@NonNull BooleanSetting setting, @NonNull Boolean newValue) { public static void privateSetValue(@NonNull BooleanSetting setting, @NonNull Boolean newValue) {
setting.value = Objects.requireNonNull(newValue); setting.value = Objects.requireNonNull(newValue);
if (setting.isSetToDefault()) {
setting.removeFromPreferences();
}
} }
@Override @Override
@@ -65,10 +69,8 @@ public class BooleanSetting extends Setting<Boolean> {
} }
@Override @Override
public void save(@NonNull Boolean newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveBoolean(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveBoolean(key, newValue);
} }
@NonNull @NonNull

View File

@@ -89,10 +89,8 @@ public class EnumSetting<T extends Enum<?>> extends Setting<T> {
} }
@Override @Override
public void save(@NonNull T newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveEnumAsString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveEnumAsString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -55,10 +55,8 @@ public class FloatSetting extends Setting<Float> {
} }
@Override @Override
public void save(@NonNull Float newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveFloatString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveFloatString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -55,10 +55,8 @@ public class IntegerSetting extends Setting<Integer> {
} }
@Override @Override
public void save(@NonNull Integer newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveIntegerString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveIntegerString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -55,10 +55,8 @@ public class LongSetting extends Setting<Long> {
} }
@Override @Override
public void save(@NonNull Long newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveLongString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveLongString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -14,7 +14,6 @@ import java.util.*;
import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.shared.StringRef.str;
@SuppressWarnings("unused")
public abstract class Setting<T> { public abstract class Setting<T> {
/** /**
@@ -288,6 +287,13 @@ public abstract class Setting<T> {
*/ */
public static void privateSetValueFromString(@NonNull Setting<?> setting, @NonNull String newValue) { public static void privateSetValueFromString(@NonNull Setting<?> setting, @NonNull String newValue) {
setting.setValueFromString(newValue); setting.setValueFromString(newValue);
// Clear the preference value since default is used, to allow changing
// the changing the default for a future release. Without this after upgrading
// the saved value will be whatever was the default when the app was first installed.
if (setting.isSetToDefault()) {
setting.removeFromPreferences();
}
} }
/** /**
@@ -303,7 +309,33 @@ public abstract class Setting<T> {
/** /**
* Persistently saves the value. * Persistently saves the value.
*/ */
public abstract void save(@NonNull T newValue); public final void save(@NonNull T newValue) {
if (value.equals(newValue)) {
return;
}
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
value = Objects.requireNonNull(newValue);
if (defaultValue.equals(newValue)) {
removeFromPreferences();
} else {
saveToPreferences();
}
}
/**
* Save {@link #value} to {@link #preferences}.
*/
protected abstract void saveToPreferences();
/**
* Remove {@link #value} from {@link #preferences}.
*/
protected final void removeFromPreferences() {
Logger.printDebug(() -> "Clearing stored preference value (reset to default): " + key);
preferences.removeKey(key);
}
@NonNull @NonNull
public abstract T get(); public abstract T get();

View File

@@ -55,10 +55,8 @@ public class StringSetting extends Setting<String> {
} }
@Override @Override
public void save(@NonNull String newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -22,12 +22,23 @@ import app.revanced.extension.shared.settings.Setting;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public abstract class AbstractPreferenceFragment extends PreferenceFragment { public abstract class AbstractPreferenceFragment extends PreferenceFragment {
/** /**
* Indicates that if a preference changes, * Indicates that if a preference changes,
* to apply the change from the Setting to the UI component. * to apply the change from the Setting to the UI component.
*/ */
public static boolean settingImportInProgress; public static boolean settingImportInProgress;
/**
* Prevents recursive calls during preference <-> UI syncing from showing extra dialogs.
*/
private static boolean updatingPreference;
/**
* Used to prevent showing reboot dialog, if user cancels a setting user dialog.
*/
private static boolean showingUserDialogMessage;
/** /**
* Confirm and restart dialog button text and title. * Confirm and restart dialog button text and title.
* Set by subclasses if Strings cannot be added as a resource. * Set by subclasses if Strings cannot be added as a resource.
@@ -35,13 +46,13 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
@Nullable @Nullable
protected static String restartDialogButtonText, restartDialogTitle, confirmDialogTitle; protected static String restartDialogButtonText, restartDialogTitle, confirmDialogTitle;
/**
* Used to prevent showing reboot dialog, if user cancels a setting user dialog.
*/
private boolean showingUserDialogMessage;
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> { private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
try { try {
if (updatingPreference) {
Logger.printDebug(() -> "Ignoring preference change as sync is in progress");
return;
}
Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str)); Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str));
if (setting == null) { if (setting == null) {
return; return;
@@ -63,10 +74,13 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
} }
} }
updatingPreference = true;
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'. // Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
// Updating here can can cause a recursive call back into this same method.
updatePreference(pref, setting, true, settingImportInProgress); updatePreference(pref, setting, true, settingImportInProgress);
// Update any other preference availability that may now be different. // Update any other preference availability that may now be different.
updateUIAvailability(); updateUIAvailability();
updatingPreference = false;
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "OnSharedPreferenceChangeListener failure", ex); Logger.printException(() -> "OnSharedPreferenceChangeListener failure", ex);
} }
@@ -97,7 +111,9 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
if (confirmDialogTitle == null) { if (confirmDialogTitle == null) {
confirmDialogTitle = str("revanced_settings_confirm_user_dialog_title"); confirmDialogTitle = str("revanced_settings_confirm_user_dialog_title");
} }
showingUserDialogMessage = true; showingUserDialogMessage = true;
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(confirmDialogTitle) .setTitle(confirmDialogTitle)
.setMessage(Objects.requireNonNull(setting.userDialogMessage).toString()) .setMessage(Objects.requireNonNull(setting.userDialogMessage).toString())
@@ -141,14 +157,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
* @return If the preference is currently set to the default value of the Setting. * @return If the preference is currently set to the default value of the Setting.
*/ */
protected boolean prefIsSetToDefault(Preference pref, Setting<?> setting) { protected boolean prefIsSetToDefault(Preference pref, Setting<?> setting) {
Object defaultValue = setting.defaultValue;
if (pref instanceof SwitchPreference switchPref) { if (pref instanceof SwitchPreference switchPref) {
return switchPref.isChecked() == (Boolean) setting.defaultValue; return switchPref.isChecked() == (Boolean) defaultValue;
} }
String defaultValueString = defaultValue.toString();
if (pref instanceof EditTextPreference editPreference) { if (pref instanceof EditTextPreference editPreference) {
return editPreference.getText().equals(setting.defaultValue.toString()); return editPreference.getText().equals(defaultValueString);
} }
if (pref instanceof ListPreference listPref) { if (pref instanceof ListPreference listPref) {
return listPref.getValue().equals(setting.defaultValue.toString()); return listPref.getValue().equals(defaultValueString);
} }
throw new IllegalStateException("Must override method to handle " throw new IllegalStateException("Must override method to handle "
@@ -158,16 +176,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
/** /**
* Syncs all UI Preferences to any {@link Setting} they represent. * Syncs all UI Preferences to any {@link Setting} they represent.
*/ */
private void updatePreferenceScreen(@NonNull PreferenceScreen screen, private void updatePreferenceScreen(@NonNull PreferenceGroup group,
boolean syncSettingValue, boolean syncSettingValue,
boolean applySettingToPreference) { boolean applySettingToPreference) {
// Alternatively this could iterate thru all Settings and check for any matching Preferences, // Alternatively this could iterate thru all Settings and check for any matching Preferences,
// but there are many more Settings than UI preferences so it's more efficient to only check // but there are many more Settings than UI preferences so it's more efficient to only check
// the Preferences. // the Preferences.
for (int i = 0, prefCount = screen.getPreferenceCount(); i < prefCount; i++) { for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
Preference pref = screen.getPreference(i); Preference pref = group.getPreference(i);
if (pref instanceof PreferenceScreen) { if (pref instanceof PreferenceGroup subGroup) {
updatePreferenceScreen((PreferenceScreen) pref, syncSettingValue, applySettingToPreference); updatePreferenceScreen(subGroup, syncSettingValue, applySettingToPreference);
} else if (pref.hasKey()) { } else if (pref.hasKey()) {
String key = pref.getKey(); String key = pref.getKey();
Setting<?> setting = Setting.getSettingFromPath(key); Setting<?> setting = Setting.getSettingFromPath(key);
@@ -255,7 +273,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
} }
} }
public static void showRestartDialog(@NonNull final Context context) { public static void showRestartDialog(Context context) {
Utils.verifyOnMainThread(); Utils.verifyOnMainThread();
if (restartDialogTitle == null) { if (restartDialogTitle == null) {
restartDialogTitle = str("revanced_settings_restart_title"); restartDialogTitle = str("revanced_settings_restart_title");
@@ -263,6 +281,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
if (restartDialogButtonText == null) { if (restartDialogButtonText == null) {
restartDialogButtonText = str("revanced_settings_restart"); restartDialogButtonText = str("revanced_settings_restart");
} }
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setMessage(restartDialogTitle) .setMessage(restartDialogTitle)
.setPositiveButton(restartDialogButtonText, (dialog, id) .setPositiveButton(restartDialogButtonText, (dialog, id)

View File

@@ -0,0 +1,54 @@
package app.revanced.extension.shared.settings.preference;
import android.annotation.SuppressLint;
import android.content.Context;
import android.preference.PreferenceCategory;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* Empty preference category with no title, used to organize and group related preferences together.
*/
@SuppressWarnings({"unused", "deprecation"})
public class NoTitlePreferenceCategory extends PreferenceCategory {
public NoTitlePreferenceCategory(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoTitlePreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public NoTitlePreferenceCategory(Context context) {
super(context);
}
@Override
@SuppressLint("MissingSuperCall")
protected View onCreateView(ViewGroup parent) {
// Return an zero-height view to eliminate empty title space.
return new View(getContext());
}
@Override
public CharSequence getTitle() {
// Title can be used for sorting. Return the first sub preference title.
if (getPreferenceCount() > 0) {
return getPreference(0).getTitle();
}
return super.getTitle();
}
@Override
public int getTitleRes() {
if (getPreferenceCount() > 0) {
return getPreference(0).getTitleRes();
}
return super.getTitleRes();
}
}

View File

@@ -1,5 +1,7 @@
package app.revanced.extension.shared.settings.preference; package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
@@ -8,17 +10,23 @@ import android.util.AttributeSet;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import app.revanced.extension.shared.Utils; import androidx.annotation.Nullable;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.Logger;
import java.util.Objects; import java.util.Objects;
import static app.revanced.extension.shared.StringRef.str; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting;
@SuppressWarnings({"unused", "deprecation"}) @SuppressWarnings({"unused", "deprecation"})
public class ResettableEditTextPreference extends EditTextPreference { public class ResettableEditTextPreference extends EditTextPreference {
/**
* Setting to reset.
*/
@Nullable
private Setting<?> setting;
public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
} }
@@ -32,12 +40,22 @@ public class ResettableEditTextPreference extends EditTextPreference {
super(context); super(context);
} }
public void setSetting(@Nullable Setting<?> setting) {
this.setting = setting;
}
@Override @Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder); super.onPrepareDialogBuilder(builder);
Utils.setEditTextDialogTheme(builder); Utils.setEditTextDialogTheme(builder);
Setting<?> setting = Setting.getSettingFromPath(getKey()); if (setting == null) {
String key = getKey();
if (key != null) {
setting = Setting.getSettingFromPath(key);
}
}
if (setting != null) { if (setting != null) {
builder.setNeutralButton(str("revanced_settings_reset"), null); builder.setNeutralButton(str("revanced_settings_reset"), null);
} }
@@ -54,8 +72,7 @@ public class ResettableEditTextPreference extends EditTextPreference {
} }
button.setOnClickListener(v -> { button.setOnClickListener(v -> {
try { try {
Setting<?> setting = Objects.requireNonNull(Setting.getSettingFromPath(getKey())); String defaultStringValue = Objects.requireNonNull(setting).defaultValue.toString();
String defaultStringValue = setting.defaultValue.toString();
EditText editText = getEditText(); EditText editText = getEditText();
editText.setText(defaultStringValue); editText.setText(defaultStringValue);
editText.setSelection(defaultStringValue.length()); // move cursor to end of text editText.setSelection(defaultStringValue.length()); // move cursor to end of text

View File

@@ -66,22 +66,6 @@ public enum ClientType {
true, true,
"Android Creator" "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( IOS_UNPLUGGED(
33, 33,
"IOS_UNPLUGGED", "IOS_UNPLUGGED",
@@ -112,6 +96,22 @@ public enum ClientType {
forceAVC() forceAVC()
? "iOS TV Force AVC" ? "iOS TV Force AVC"
: "iOS TV" : "iOS TV"
),
ANDROID_VR_AUTH(
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 Auth"
); );
private static boolean forceAVC() { private static boolean forceAVC() {

View File

@@ -107,6 +107,21 @@ public class SpoofVideoStreamsPatch {
return false; return false;
} }
/**
* Injection point.
* Turns off a feature flag that interferes with spoofing.
*/
public static boolean useMediaFetchHotConfigReplacement(boolean original) {
if (original) {
Logger.printDebug(() -> "useMediaFetchHotConfigReplacement is set on");
}
if (!SPOOF_STREAMING_DATA) {
return original;
}
return false;
}
/** /**
* Injection point. * Injection point.
*/ */

View File

@@ -4,7 +4,7 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24

View File

@@ -3,3 +3,14 @@ dependencies {
compileOnly(project(":extensions:tiktok:stub")) compileOnly(project(":extensions:tiktok:stub"))
compileOnly(libs.annotation) compileOnly(libs.annotation)
} }
android {
defaultConfig {
minSdk = 22
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View File

@@ -21,9 +21,11 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
protected void syncSettingWithPreference(@NonNull Preference pref, protected void syncSettingWithPreference(@NonNull Preference pref,
@NonNull Setting<?> setting, @NonNull Setting<?> setting,
boolean applySettingToPreference) { boolean applySettingToPreference) {
if (pref instanceof RangeValuePreference rangeValuePref) { if (pref instanceof RangeValuePreference) {
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
Setting.privateSetValueFromString(setting, rangeValuePref.getValue()); Setting.privateSetValueFromString(setting, rangeValuePref.getValue());
} else if (pref instanceof DownloadPathPreference downloadPathPref) { } else if (pref instanceof DownloadPathPreference) {
DownloadPathPreference downloadPathPref = (DownloadPathPreference) pref;
Setting.privateSetValueFromString(setting, downloadPathPref.getValue()); Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
} else { } else {
super.syncSettingWithPreference(pref, setting, applySettingToPreference); super.syncSettingWithPreference(pref, setting, applySettingToPreference);
@@ -32,7 +34,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
@Override @Override
protected void initialize() { protected void initialize() {
final var context = getContext(); final var context = getActivity();
// Currently no resources can be compiled for TikTok (fails with aapt error). // Currently no resources can be compiled for TikTok (fails with aapt error).
// So all TikTok Strings are hard coded in the extension. // So all TikTok Strings are hard coded in the extension.

View File

@@ -1,37 +1,60 @@
package app.revanced.extension.tiktok.spoof.sim; package app.revanced.extension.tiktok.spoof.sim;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.tiktok.settings.Settings; import app.revanced.extension.tiktok.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class SpoofSimPatch { public class SpoofSimPatch {
private static final boolean ENABLED = Settings.SIM_SPOOF.get(); /**
* During app startup native code can be called with no obvious way to set the context.
* Cannot check if sim spoofing is enabled or the app will crash since no context is set.
*/
private static boolean isContextNotSet(String fieldSpoofed) {
if (Utils.getContext() != null) {
return false;
}
Logger.initializationException(SpoofSimPatch.class,
"Context is not yet set, cannot spoof: " + fieldSpoofed, null);
return true;
}
public static String getCountryIso(String value) { public static String getCountryIso(String value) {
if (ENABLED) { if (isContextNotSet("countryIso")) return value;
if (Settings.SIM_SPOOF.get()) {
String iso = Settings.SIM_SPOOF_ISO.get(); String iso = Settings.SIM_SPOOF_ISO.get();
Logger.printDebug(() -> "Spoofing sim ISO from: " + value + " to: " + iso); Logger.printDebug(() -> "Spoofing countryIso from: " + value + " to: " + iso);
return iso; return iso;
} }
return value; return value;
} }
public static String getOperator(String value) { public static String getOperator(String value) {
if (ENABLED) { if (isContextNotSet("MCC-MNC")) return value;
if (Settings.SIM_SPOOF.get()) {
String mcc_mnc = Settings.SIMSPOOF_MCCMNC.get(); String mcc_mnc = Settings.SIMSPOOF_MCCMNC.get();
Logger.printDebug(() -> "Spoofing sim MCC-MNC from: " + value + " to: " + mcc_mnc); Logger.printDebug(() -> "Spoofing sim MCC-MNC from: " + value + " to: " + mcc_mnc);
return mcc_mnc; return mcc_mnc;
} }
return value; return value;
} }
public static String getOperatorName(String value) { public static String getOperatorName(String value) {
if (ENABLED) { if (isContextNotSet("operatorName")) return value;
if (Settings.SIM_SPOOF.get()) {
String operator = Settings.SIMSPOOF_OP_NAME.get(); String operator = Settings.SIMSPOOF_OP_NAME.get();
Logger.printDebug(() -> "Spoofing sim operator from: " + value + " to: " + operator); Logger.printDebug(() -> "Spoofing sim operatorName from: " + value + " to: " + operator);
return operator; return operator;
} }
return value; return value;
} }
} }

View File

@@ -4,14 +4,9 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 22
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
} }
} }

View File

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

View File

@@ -1,17 +1,14 @@
android.namespace = "app.revanced.extension"
plugins { plugins {
id(libs.plugins.android.library.get().pluginId) id(libs.plugins.android.library.get().pluginId)
} }
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
} }
} }

View File

@@ -6,3 +6,14 @@ dependencies {
compileOnly(libs.annotation) compileOnly(libs.annotation)
compileOnly(libs.appcompat) compileOnly(libs.appcompat)
} }
android {
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View File

@@ -4,14 +4,9 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
} }
} }

View File

@@ -1,8 +1,11 @@
//noinspection GradleDependency
android.compileSdk = 33
dependencies { dependencies {
compileOnly(project(":extensions:shared:library")) compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:youtube:stub")) compileOnly(project(":extensions:youtube:stub"))
compileOnly(libs.annotation) compileOnly(libs.annotation)
} }
android {
defaultConfig {
minSdk = 26
}
}

View File

@@ -1,7 +1,7 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class BackgroundPlaybackPatch { public class BackgroundPlaybackPatch {
@@ -23,16 +23,7 @@ public class BackgroundPlaybackPatch {
// 7. Close the Short // 7. Close the Short
// 8. Resume playing the regular video // 8. Resume playing the regular video
// 9. Minimize the app (PIP should appear) // 9. Minimize the app (PIP should appear)
if (!VideoInformation.lastVideoIdIsShort()) { return !ShortsPlayerState.isOpen();
return true; // Definitely is not a Short.
}
// TODO: Add better hook.
// Might be a Shorts, or might be a prior regular video on screen again after a Shorts was closed.
// This incorrectly prevents PIP if player is in WATCH_WHILE_MINIMIZED after closing a Shorts,
// But there's no way around this unless an additional hook is added to definitively detect
// the Shorts player is on screen. This use case is unusual anyways so it's not a huge concern.
return !PlayerType.getCurrent().isNoneHiddenOrMinimized();
} }
/** /**

View File

@@ -1,9 +1,17 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.shared.Utils; import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.NavigationBar;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ChangeFormFactorPatch { public class ChangeFormFactorPatch {
@@ -41,14 +49,57 @@ public class ChangeFormFactorPatch {
@Nullable @Nullable
private static final Integer FORM_FACTOR_TYPE = Settings.CHANGE_FORM_FACTOR.get().formFactorType; private static final Integer FORM_FACTOR_TYPE = Settings.CHANGE_FORM_FACTOR.get().formFactorType;
private static final boolean USING_AUTOMOTIVE_TYPE = Objects.requireNonNull(
FormFactor.AUTOMOTIVE.formFactorType).equals(FORM_FACTOR_TYPE);
/** /**
* Injection point. * Injection point.
*/ */
public static int getFormFactor(int original) { public static int getFormFactor(int original) {
return FORM_FACTOR_TYPE == null if (FORM_FACTOR_TYPE == null) return original;
? original
: FORM_FACTOR_TYPE; if (USING_AUTOMOTIVE_TYPE) {
// Do not change if the player is opening or is opened,
// otherwise the video description cannot be opened.
PlayerType current = PlayerType.getCurrent();
if (current.isMaximizedOrFullscreen() || current == PlayerType.WATCH_WHILE_SLIDING_MINIMIZED_MAXIMIZED) {
Logger.printDebug(() -> "Using original form factor for player");
return original;
}
if (!NavigationBar.isSearchBarActive()) {
// Automotive type shows error 400 when opening a channel page and using some explore tab.
// This is a bug in unpatched YouTube that occurs on actual Android Automotive devices.
// Work around the issue by using the original form factor if not in search and the
// navigation back button is present.
if (NavigationBar.isBackButtonVisible()) {
Logger.printDebug(() -> "Using original form factor, as back button is visible without search present");
return original;
}
// Do not change library tab otherwise watch history is hidden.
// Do this check last since the current navigation button is required.
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.LIBRARY) {
return original;
}
}
}
return FORM_FACTOR_TYPE;
} }
/**
* Injection point.
*/
public static void navigationTabCreated(NavigationButton button, View tabView) {
// On first startup of the app the navigation buttons are fetched and updated.
// If the user immediately opens the 'You' or opens a video, then the call to
// update the navigtation buttons will use the non automotive form factor
// and the explore tab is missing.
// Fixing this is not so simple because of the concurrent calls for the player and You tab.
// For now, always hide the explore tab.
if (USING_AUTOMOTIVE_TYPE && button == NavigationButton.EXPLORE) {
tabView.setVisibility(View.GONE);
}
}
} }

View File

@@ -23,21 +23,30 @@ public final class ChangeStartPagePatch {
/** /**
* Browse id. * Browse id.
*/ */
ALL_SUBSCRIPTIONS("FEchannels", TRUE),
BROWSE("FEguide_builder", TRUE), BROWSE("FEguide_builder", TRUE),
EXPLORE("FEexplore", TRUE), EXPLORE("FEexplore", TRUE),
HISTORY("FEhistory", TRUE), HISTORY("FEhistory", TRUE),
LIBRARY("FElibrary", TRUE), LIBRARY("FElibrary", TRUE),
MOVIE("FEstorefront", TRUE), MOVIE("FEstorefront", TRUE),
NOTIFICATIONS("FEactivity", TRUE),
PLAYLISTS("FEplaylist_aggregation", TRUE),
SUBSCRIPTIONS("FEsubscriptions", TRUE), SUBSCRIPTIONS("FEsubscriptions", TRUE),
TRENDING("FEtrending", TRUE), TRENDING("FEtrending", TRUE),
YOUR_CLIPS("FEclips", TRUE),
/** /**
* Channel id, this can be used as a browseId. * Channel id, this can be used as a browseId.
*/ */
COURSES("UCtFRv9O2AHqOZjjynzrv-xg", TRUE),
FASHION("UCrpQ4p1Ql_hG8rKXIKM1MOQ", TRUE),
GAMING("UCOpNcN46UbXVtpKMrmU4Abg", TRUE), GAMING("UCOpNcN46UbXVtpKMrmU4Abg", TRUE),
LIVE("UC4R8DWoMoI7CAwX8_LjQHig", TRUE), LIVE("UC4R8DWoMoI7CAwX8_LjQHig", TRUE),
MUSIC("UC-9-kyTW8ZkZNDHQJ6FgpwQ", TRUE), MUSIC("UC-9-kyTW8ZkZNDHQJ6FgpwQ", TRUE),
NEWS("UCYfdidRxbB8Qhf0Nx7ioOYw", TRUE),
SHOPPING("UCkYQyvc_i9hXEo4xic9Hh2g", TRUE),
SPORTS("UCEgdi0XIXXZ-qJOFPf4JSKw", TRUE), SPORTS("UCEgdi0XIXXZ-qJOFPf4JSKw", TRUE),
VIRTUAL_REALITY("UCzuqhhs6NWbgTzMuM09WKDQ", TRUE),
/** /**
* Playlist id, this can be used as a browseId. * Playlist id, this can be used as a browseId.
@@ -51,12 +60,12 @@ public final class ChangeStartPagePatch {
SEARCH("com.google.android.youtube.action.open.search", FALSE), SEARCH("com.google.android.youtube.action.open.search", FALSE),
SHORTS("com.google.android.youtube.action.open.shorts", FALSE); SHORTS("com.google.android.youtube.action.open.shorts", FALSE);
@Nullable
final Boolean isBrowseId;
@NonNull @NonNull
final String id; final String id;
@Nullable
final Boolean isBrowseId;
StartPage(@NonNull String id, @Nullable Boolean isBrowseId) { StartPage(@NonNull String id, @Nullable Boolean isBrowseId) {
this.id = id; this.id = id;
this.isBrowseId = isBrowseId; this.isBrowseId = isBrowseId;
@@ -122,7 +131,7 @@ public final class ChangeStartPagePatch {
} }
appLaunched = true; appLaunched = true;
final String intentAction = START_PAGE.id; String intentAction = START_PAGE.id;
Logger.printDebug(() -> "Changing intent action to " + intentAction); Logger.printDebug(() -> "Changing intent action to " + intentAction);
intent.setAction(intentAction); intent.setAction(intentAction);
} }

View File

@@ -1,7 +1,7 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DisableAutoCaptionsPatch { public class DisableAutoCaptionsPatch {
@@ -14,7 +14,7 @@ public class DisableAutoCaptionsPatch {
public static boolean autoCaptionsEnabled() { public static boolean autoCaptionsEnabled() {
return Settings.AUTO_CAPTIONS.get() return Settings.AUTO_CAPTIONS.get()
// Do not use auto captions for Shorts. // Do not use auto captions for Shorts.
&& !PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized(); && ShortsPlayerState.isOpen();
} }
} }

View File

@@ -0,0 +1,15 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class DisableHdrPatch {
/**
* Injection point.
*/
public static boolean disableHDRVideo() {
return !Settings.DISABLE_HDR_VIDEO.get();
}
}

View File

@@ -1,24 +0,0 @@
package app.revanced.extension.youtube.patches;
import android.annotation.SuppressLint;
import android.widget.ImageView;
import app.revanced.extension.youtube.settings.Settings;
/** @noinspection unused*/
public final class DisableSuggestedVideoEndScreenPatch {
@SuppressLint("StaticFieldLeak")
private static ImageView lastView;
public static void closeEndScreen(final ImageView imageView) {
if (!Settings.DISABLE_SUGGESTED_VIDEO_END_SCREEN.get()) return;
// Prevent adding the listener multiple times.
if (lastView == imageView) return;
lastView = imageView;
imageView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
if (imageView.isShown()) imageView.callOnClick();
});
}
}

View File

@@ -9,14 +9,23 @@ import app.revanced.extension.shared.settings.BaseSettings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class EnableDebuggingPatch { 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. * Injection point.
*/ */
public static boolean isBooleanFeatureFlagEnabled(boolean value, long flag) { public static boolean isBooleanFeatureFlagEnabled(boolean value, Long flag) {
if (value && BaseSettings.DEBUG.get()) { if (LOG_FEATURE_FLAGS && value) {
if (featureFlags.putIfAbsent(flag, true) == null) { if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> "boolean feature is enabled: " + flag); Logger.printDebug(() -> "boolean feature is enabled: " + flag);
} }
@@ -29,7 +38,7 @@ public final class EnableDebuggingPatch {
* Injection point. * Injection point.
*/ */
public static double isDoubleFeatureFlagEnabled(double value, long flag, double defaultValue) { 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) { if (featureFlags.putIfAbsent(flag, true) == null) {
// Align the log outputs to make post processing easier. // Align the log outputs to make post processing easier.
Logger.printDebug(() -> " double feature is enabled: " + flag Logger.printDebug(() -> " double feature is enabled: " + flag
@@ -44,7 +53,7 @@ public final class EnableDebuggingPatch {
* Injection point. * Injection point.
*/ */
public static long isLongFeatureFlagEnabled(long value, long flag, long defaultValue) { 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) { if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> " long feature is enabled: " + flag Logger.printDebug(() -> " long feature is enabled: " + flag
+ " value: " + value + (defaultValue == 0 ? "" : " default: " + defaultValue)); + " value: " + value + (defaultValue == 0 ? "" : " default: " + defaultValue));
@@ -58,7 +67,7 @@ public final class EnableDebuggingPatch {
* Injection point. * Injection point.
*/ */
public static String isStringFeatureFlagEnabled(String value, long flag, String defaultValue) { 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) { if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> " string feature is enabled: " + flag Logger.printDebug(() -> " string feature is enabled: " + flag
+ " value: " + value + (defaultValue.isEmpty() ? "" : " default: " + defaultValue)); + " value: " + value + (defaultValue.isEmpty() ? "" : " default: " + defaultValue));

View File

@@ -0,0 +1,13 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class HideEndScreenSuggestedVideoPatch {
/**
* Injection point.
*/
public static boolean hideEndScreenSuggestedVideo() {
return Settings.HIDE_END_SCREEN_SUGGESTED_VIDEO.get();
}
}

View File

@@ -21,6 +21,7 @@ public final class NavigationButtonsPatch {
{ {
put(NavigationButton.HOME, Settings.HIDE_HOME_BUTTON.get()); put(NavigationButton.HOME, Settings.HIDE_HOME_BUTTON.get());
put(NavigationButton.CREATE, Settings.HIDE_CREATE_BUTTON.get()); put(NavigationButton.CREATE, Settings.HIDE_CREATE_BUTTON.get());
put(NavigationButton.NOTIFICATIONS, Settings.HIDE_NOTIFICATIONS_BUTTON.get());
put(NavigationButton.SHORTS, Settings.HIDE_SHORTS_BUTTON.get()); put(NavigationButton.SHORTS, Settings.HIDE_SHORTS_BUTTON.get());
put(NavigationButton.SUBSCRIPTIONS, Settings.HIDE_SUBSCRIPTIONS_BUTTON.get()); put(NavigationButton.SUBSCRIPTIONS, Settings.HIDE_SUBSCRIPTIONS_BUTTON.get());
} }

View File

@@ -1,8 +1,11 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
import app.revanced.extension.youtube.shared.VideoState; import app.revanced.extension.youtube.shared.VideoState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -24,4 +27,26 @@ public class PlayerTypeHookPatch {
VideoState.setFromString(youTubeVideoState.name()); VideoState.setFromString(youTubeVideoState.name());
} }
/**
* Injection point.
*
* Add a listener to the shorts player overlay View.
* Triggered when a shorts player is attached or detached to Windows.
*
* @param view shorts player overlay (R.id.reel_watch_player).
*/
public static void onShortsCreate(View view) {
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(@Nullable View v) {
ShortsPlayerState.setOpen(true);
}
@Override
public void onViewDetachedFromWindow(@Nullable View v) {
ShortsPlayerState.setOpen(false);
}
});
}
} }

View File

@@ -4,7 +4,6 @@ import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeD
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
@@ -22,7 +21,6 @@ import java.util.Objects;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilterPatch; import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilterPatch;
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
import app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike; import app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi; import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
@@ -48,9 +46,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ReturnYouTubeDislikePatch { public class ReturnYouTubeDislikePatch {
public static final boolean IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER =
SpoofAppVersionPatch.isSpoofingToLessThan("18.34.00");
/** /**
* RYD data for the current video on screen. * RYD data for the current video on screen.
*/ */
@@ -348,139 +343,6 @@ public class ReturnYouTubeDislikePatch {
} }
} }
//
// Non litho Shorts player.
//
/**
* Replacement text to use for "Dislikes" while RYD is fetching.
*/
private static final Spannable SHORTS_LOADING_SPAN = new SpannableString("-");
/**
* Dislikes TextViews used by Shorts.
*
* Multiple TextViews are loaded at once (for the prior and next videos to swipe to).
* Keep track of all of them, and later pick out the correct one based on their on screen position.
*/
private static final List<WeakReference<TextView>> shortsTextViewRefs = new ArrayList<>();
private static void clearRemovedShortsTextViews() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // YouTube requires Android N or greater
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
}
}
/**
* Injection point. Called when a Shorts dislike is updated. Always on main thread.
* Handles update asynchronously, otherwise Shorts video will be frozen while the UI thread is blocked.
*
* @return if RYD is enabled and the TextView was updated.
*/
public static boolean setShortsDislikes(@NonNull View likeDislikeView) {
try {
if (!Settings.RYD_ENABLED.get()) {
return false;
}
if (!Settings.RYD_SHORTS.get() || Settings.HIDE_SHORTS_DISLIKE_BUTTON.get()) {
// Must clear the data here, in case a new video was loaded while PlayerType
// suggested the video was not a short (can happen when spoofing to an old app version).
clearData();
return false;
}
Logger.printDebug(() -> "setShortsDislikes");
TextView textView = (TextView) likeDislikeView;
textView.setText(SHORTS_LOADING_SPAN); // Change 'Dislike' text to the loading text.
shortsTextViewRefs.add(new WeakReference<>(textView));
if (likeDislikeView.isSelected() && isShortTextViewOnScreen(textView)) {
Logger.printDebug(() -> "Shorts dislike is already selected");
ReturnYouTubeDislike videoData = currentVideoData;
if (videoData != null) videoData.setUserVote(Vote.DISLIKE);
}
// For the first short played, the Shorts dislike hook is called after the video id hook.
// But for most other times this hook is called before the video id (which is not ideal).
// Must update the TextViews here, and also after the videoId changes.
updateOnScreenShortsTextViews(false);
return true;
} catch (Exception ex) {
Logger.printException(() -> "setShortsDislikes failure", ex);
return false;
}
}
/**
* @param forceUpdate if false, then only update the 'loading text views.
* If true, update all on screen text views.
*/
private static void updateOnScreenShortsTextViews(boolean forceUpdate) {
try {
clearRemovedShortsTextViews();
if (shortsTextViewRefs.isEmpty()) {
return;
}
ReturnYouTubeDislike videoData = currentVideoData;
if (videoData == null) {
return;
}
Logger.printDebug(() -> "updateShortsTextViews");
Runnable update = () -> {
Spanned shortsDislikesSpan = videoData.getDislikeSpanForShort(SHORTS_LOADING_SPAN);
Utils.runOnMainThreadNowOrLater(() -> {
String videoId = videoData.getVideoId();
if (!videoId.equals(VideoInformation.getVideoId())) {
// User swiped to new video before fetch completed
Logger.printDebug(() -> "Ignoring stale dislikes data for short: " + videoId);
return;
}
// Update text views that appear to be visible on screen.
// Only 1 will be the actual textview for the current Short,
// but discarded and not yet garbage collected views can remain.
// So must set the dislike span on all views that match.
for (WeakReference<TextView> textViewRef : shortsTextViewRefs) {
TextView textView = textViewRef.get();
if (textView == null) {
continue;
}
if (isShortTextViewOnScreen(textView)
&& (forceUpdate || textView.getText().toString().equals(SHORTS_LOADING_SPAN.toString()))) {
Logger.printDebug(() -> "Setting Shorts TextView to: " + shortsDislikesSpan);
textView.setText(shortsDislikesSpan);
}
}
});
};
if (videoData.fetchCompleted()) {
update.run(); // Network call is completed, no need to wait on background thread.
} else {
Utils.runOnBackgroundThread(update);
}
} catch (Exception ex) {
Logger.printException(() -> "updateOnScreenShortsTextViews failure", ex);
}
}
/**
* Check if a view is within the screen bounds.
*/
private static boolean isShortTextViewOnScreen(@NonNull View view) {
final int[] location = new int[2];
view.getLocationInWindow(location);
if (location[0] <= 0 && location[1] <= 0) { // Lower bound
return false;
}
Rect windowRect = new Rect();
view.getWindowVisibleDisplayFrame(windowRect); // Upper bound
return location[0] < windowRect.width() && location[1] < windowRect.height();
}
// //
// Video Id and voting hooks (all players). // Video Id and voting hooks (all players).
// //
@@ -506,8 +368,7 @@ public class ReturnYouTubeDislikePatch {
if (videoIdIsShort && (!isShortAndOpeningOrPlaying || !Settings.RYD_SHORTS.get())) { if (videoIdIsShort && (!isShortAndOpeningOrPlaying || !Settings.RYD_SHORTS.get())) {
return; return;
} }
final boolean waitForFetchToComplete = !IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER final boolean waitForFetchToComplete = videoIdIsShort && !lastPlayerResponseWasShort;
&& videoIdIsShort && !lastPlayerResponseWasShort;
Logger.printDebug(() -> "Prefetching RYD for video: " + videoId); Logger.printDebug(() -> "Prefetching RYD for video: " + videoId);
ReturnYouTubeDislike fetch = ReturnYouTubeDislike.getFetchForVideoId(videoId); ReturnYouTubeDislike fetch = ReturnYouTubeDislike.getFetchForVideoId(videoId);
@@ -560,12 +421,6 @@ public class ReturnYouTubeDislikePatch {
data.setVideoIdIsShort(true); data.setVideoIdIsShort(true);
} }
currentVideoData = data; currentVideoData = data;
// Current video id hook can be called out of order with the non litho Shorts text view hook.
// Must manually update again here.
if (isNoneHiddenOrSlidingMinimized) {
updateOnScreenShortsTextViews(true);
}
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "newVideoLoaded failure", ex); Logger.printException(() -> "newVideoLoaded failure", ex);
} }

View File

@@ -1,9 +1,6 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import android.app.Activity; import android.app.Activity;
import android.os.Build;
import androidx.annotation.RequiresApi;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Objects; import java.util.Objects;
@@ -58,7 +55,6 @@ public class ShortsAutoplayPatch {
/** /**
* @return If the app is currently in background PiP mode. * @return If the app is currently in background PiP mode.
*/ */
@RequiresApi(api = Build.VERSION_CODES.N)
private static boolean isAppInBackgroundPiPMode() { private static boolean isAppInBackgroundPiPMode() {
Activity activity = mainActivityRef.get(); Activity activity = mainActivityRef.get();
return activity != null && activity.isInPictureInPictureMode(); return activity != null && activity.isInPictureInPictureMode();
@@ -80,7 +76,6 @@ public class ShortsAutoplayPatch {
/** /**
* Injection point. * Injection point.
*/ */
@RequiresApi(api = Build.VERSION_CODES.N)
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) { public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
try { try {
final boolean autoplay; final boolean autoplay;

View File

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

View File

@@ -7,13 +7,10 @@ import static app.revanced.extension.youtube.patches.announcements.requests.Anno
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.os.Build;
import android.text.Html; import android.text.Html;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.RequiresApi;
import org.json.JSONArray; import org.json.JSONArray;
import java.io.IOException; import java.io.IOException;
@@ -31,7 +28,6 @@ public final class AnnouncementsPatch {
private AnnouncementsPatch() { private AnnouncementsPatch() {
} }
@RequiresApi(api = Build.VERSION_CODES.O)
private static boolean isLatestAlready() throws IOException { private static boolean isLatestAlready() throws IOException {
HttpURLConnection connection = HttpURLConnection connection =
AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS); AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS);
@@ -70,7 +66,6 @@ public final class AnnouncementsPatch {
return Settings.ANNOUNCEMENT_LAST_ID.get() == id; return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
} }
@RequiresApi(api = Build.VERSION_CODES.O)
public static void showAnnouncement(final Activity context) { public static void showAnnouncement(final Activity context) {
if (!Settings.ANNOUNCEMENTS.get()) return; if (!Settings.ANNOUNCEMENTS.get()) return;

View File

@@ -8,10 +8,12 @@ import android.view.View;
import androidx.annotation.Nullable; 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.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.StringTrieSearch; import app.revanced.extension.youtube.StringTrieSearch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class AdsFilter extends Filter { public final class AdsFilter extends Filter {
@@ -22,6 +24,11 @@ public final class AdsFilter extends Filter {
// endregion // 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 StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup playerShoppingShelf; private final StringFilterGroup playerShoppingShelf;
@@ -66,7 +73,10 @@ public final class AdsFilter extends Filter {
"full_width_square_image_layout", "full_width_square_image_layout",
"video_display_button_group_layout", "video_display_button_group_layout",
"landscape_image_wide_button_layout", "landscape_image_wide_button_layout",
"video_display_carousel_button_group_layout" "video_display_carousel_button_group_layout",
"video_display_full_buttoned_short_dr_layout",
"compact_landscape_image_layout", // Tablet layout search results.
"text_image_no_button_layout" // Tablet layout search results.
); );
final var generalAds = new StringFilterGroup( final var generalAds = new StringFilterGroup(
@@ -112,23 +122,24 @@ public final class AdsFilter extends Filter {
"expandable_list" "expandable_list"
); );
channelProfile = new StringFilterGroup(
null,
"channel_profile.eml"
);
playerShoppingShelf = new StringFilterGroup( playerShoppingShelf = new StringFilterGroup(
null, Settings.HIDE_PLAYER_STORE_SHELF,
"horizontal_shelf.eml" "horizontal_shelf.eml"
); );
playerShoppingShelfBuffer = new ByteArrayFilterGroup( playerShoppingShelfBuffer = new ByteArrayFilterGroup(
Settings.HIDE_PLAYER_STORE_SHELF, null,
"shopping_item_card_list.eml" "shopping_item_card_list.eml"
); );
visitStoreButton = new ByteArrayFilterGroup( channelProfile = new StringFilterGroup(
Settings.HIDE_VISIT_STORE_BUTTON, Settings.HIDE_VISIT_STORE_BUTTON,
"channel_profile.eml",
"page_header.eml"
);
visitStoreButton = new ByteArrayFilterGroup(
null,
"header_store_button" "header_store_button"
); );
@@ -172,6 +183,11 @@ public final class AdsFilter extends Filter {
return false; return false;
} }
// Check for the index because of likelihood of false positives.
if (matchedGroup == shoppingLinks && contentIndex != 0) {
return false;
}
if (exceptions.matches(path)) if (exceptions.matches(path))
return false; return false;
@@ -188,13 +204,25 @@ public final class AdsFilter extends Filter {
return false; 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); 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. * Hide the view, which shows ads in the homepage.
* *

View File

@@ -2,20 +2,20 @@ package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.youtube.patches.playback.quality.RestoreOldVideoQualityMenuPatch; import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
/** /**
* Abuse LithoFilter for {@link RestoreOldVideoQualityMenuPatch}. * Abuse LithoFilter for {@link AdvancedVideoQualityMenuPatch}.
*/ */
public final class VideoQualityMenuFilterPatch extends Filter { public final class AdvancedVideoQualityMenuFilter extends Filter {
// Must be volatile or synchronized, as litho filtering runs off main thread // Must be volatile or synchronized, as litho filtering runs off main thread
// and this field is then access from the main thread. // and this field is then access from the main thread.
public static volatile boolean isVideoQualityMenuVisible; public static volatile boolean isVideoQualityMenuVisible;
public VideoQualityMenuFilterPatch() { public AdvancedVideoQualityMenuFilter() {
addPathCallbacks(new StringFilterGroup( addPathCallbacks(new StringFilterGroup(
Settings.RESTORE_OLD_VIDEO_QUALITY_MENU, Settings.ADVANCED_VIDEO_QUALITY_MENU,
"quick_quality_sheet_content.eml-js" "quick_quality_sheet_content.eml-js"
)); ));
} }

View File

@@ -6,8 +6,12 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
final class ButtonsFilter extends Filter { final class ButtonsFilter extends Filter {
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml"; private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml";
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
private final StringFilterGroup likeSubscribeGlow;
private final StringFilterGroup actionBarGroup; private final StringFilterGroup actionBarGroup;
private final StringFilterGroup bufferFilterPathGroup; private final StringFilterGroup bufferFilterPathGroup;
private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList(); private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList();
@@ -20,11 +24,19 @@ final class ButtonsFilter extends Filter {
addIdentifierCallbacks(actionBarGroup); addIdentifierCallbacks(actionBarGroup);
likeSubscribeGlow = new StringFilterGroup(
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
"animated_button_border.eml"
);
bufferFilterPathGroup = new StringFilterGroup( bufferFilterPathGroup = new StringFilterGroup(
null, null,
"|ContainerType|button.eml|" "|ContainerType|button.eml|"
); );
addPathCallbacks( addPathCallbacks(
likeSubscribeGlow,
bufferFilterPathGroup,
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_LIKE_DISLIKE_BUTTON, Settings.HIDE_LIKE_DISLIKE_BUTTON,
"|segmented_like_dislike_button" "|segmented_like_dislike_button"
@@ -40,8 +52,7 @@ final class ButtonsFilter extends Filter {
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_CLIP_BUTTON, Settings.HIDE_CLIP_BUTTON,
"|clip_button.eml|" "|clip_button.eml|"
), )
bufferFilterPathGroup
); );
bufferButtonsGroupList.addAll( bufferButtonsGroupList.addAll(
@@ -83,6 +94,15 @@ final class ButtonsFilter extends Filter {
@Override @Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (matchedGroup == likeSubscribeGlow) {
if ((path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
&& path.contains(ANIMATED_VECTOR_TYPE_PATH)) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
// If the current matched group is the action bar group, // If the current matched group is the action bar group,
// in case every filter group is enabled, hide the action bar. // in case every filter group is enabled, hide the action bar.
if (matchedGroup == actionBarGroup) { if (matchedGroup == actionBarGroup) {

View File

@@ -12,10 +12,12 @@ final class CommentsFilter extends Filter {
private final StringFilterGroup commentComposer; private final StringFilterGroup commentComposer;
private final ByteArrayFilterGroup emojiPickerBufferGroup; private final ByteArrayFilterGroup emojiPickerBufferGroup;
private final StringFilterGroup filterChipBar;
private final ByteArrayFilterGroup aiCommentsSummary;
public CommentsFilter() { public CommentsFilter() {
var chatSummary = new StringFilterGroup( var chatSummary = new StringFilterGroup(
Settings.HIDE_COMMENTS_CHAT_SUMMARY, Settings.HIDE_COMMENTS_AI_CHAT_SUMMARY,
"live_chat_summary_banner.eml" "live_chat_summary_banner.eml"
); );
@@ -58,6 +60,16 @@ final class CommentsFilter extends Filter {
"id.comment.quick_emoji.button" "id.comment.quick_emoji.button"
); );
filterChipBar = new StringFilterGroup(
Settings.HIDE_COMMENTS_AI_SUMMARY,
"filter_chip_bar.eml"
);
aiCommentsSummary = new ByteArrayFilterGroup(
null,
"yt_fill_spark_"
);
addPathCallbacks( addPathCallbacks(
chatSummary, chatSummary,
commentsByMembers, commentsByMembers,
@@ -65,7 +77,8 @@ final class CommentsFilter extends Filter {
createAShort, createAShort,
previewComment, previewComment,
thanksButton, thanksButton,
commentComposer commentComposer,
filterChipBar
); );
} }
@@ -84,6 +97,13 @@ final class CommentsFilter extends Filter {
return false; return false;
} }
if (matchedGroup == filterChipBar) {
if (aiCommentsSummary.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex); return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
} }
} }

View File

@@ -23,6 +23,11 @@ final class DescriptionComponentsFilter extends Filter {
"metadata" "metadata"
); );
final StringFilterGroup aiGeneratedVideoSummarySection = new StringFilterGroup(
Settings.HIDE_AI_GENERATED_VIDEO_SUMMARY_SECTION,
"cell_expandable_metadata.eml"
);
final StringFilterGroup attributesSection = new StringFilterGroup( final StringFilterGroup attributesSection = new StringFilterGroup(
Settings.HIDE_ATTRIBUTES_SECTION, Settings.HIDE_ATTRIBUTES_SECTION,
"gaming_section", "gaming_section",
@@ -45,6 +50,11 @@ final class DescriptionComponentsFilter extends Filter {
"transcript_section" "transcript_section"
); );
final StringFilterGroup howThisWasMadeSection = new StringFilterGroup(
Settings.HIDE_HOW_THIS_WAS_MADE_SECTION,
"how_this_was_made_section"
);
macroMarkersCarousel = new StringFilterGroup( macroMarkersCarousel = new StringFilterGroup(
null, null,
"macro_markers_carousel.eml" "macro_markers_carousel.eml"
@@ -62,8 +72,10 @@ final class DescriptionComponentsFilter extends Filter {
); );
addPathCallbacks( addPathCallbacks(
aiGeneratedVideoSummarySection,
attributesSection, attributesSection,
infoCardsSection, infoCardsSection,
howThisWasMadeSection,
podcastSection, podcastSection,
transcriptSection, transcriptSection,
macroMarkersCarousel macroMarkersCarousel

View File

@@ -1,9 +1,6 @@
package app.revanced.extension.youtube.patches.components; package app.revanced.extension.youtube.patches.components;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -44,13 +41,11 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
return filterGroups.iterator(); return filterGroups.iterator();
} }
@RequiresApi(api = Build.VERSION_CODES.N)
@Override @Override
public void forEach(@NonNull Consumer<? super T> action) { public void forEach(@NonNull Consumer<? super T> action) {
filterGroups.forEach(action); filterGroups.forEach(action);
} }
@RequiresApi(api = Build.VERSION_CODES.N)
@NonNull @NonNull
@Override @Override
public Spliterator<T> spliterator() { public Spliterator<T> spliterator() {

View File

@@ -4,11 +4,8 @@ import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import static java.lang.Character.UnicodeBlock.*; import static java.lang.Character.UnicodeBlock.*;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -44,7 +41,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
* - When using whole word syntax, some keywords may need additional pluralized variations. * - When using whole word syntax, some keywords may need additional pluralized variations.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@RequiresApi(api = Build.VERSION_CODES.N)
final class KeywordContentFilter extends Filter { final class KeywordContentFilter extends Filter {
/** /**

View File

@@ -3,11 +3,9 @@ package app.revanced.extension.youtube.patches.components;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View; import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
@@ -18,10 +16,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class LayoutComponentsFilter extends Filter { public final class LayoutComponentsFilter extends Filter {
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch( private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch(
"V.ED", // Playlist browse id. "V.ED", // Playlist browse id.
"java.lang.ref.WeakReference" "java.lang.ref.WeakReference"
@@ -38,16 +32,15 @@ public final class LayoutComponentsFilter extends Filter {
private final StringTrieSearch exceptions = new StringTrieSearch(); private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup inFeedSurvey; private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup notifyMe; private final StringFilterGroup notifyMe;
private final StringFilterGroup singleItemInformationPanel;
private final StringFilterGroup expandableMetadata; private final StringFilterGroup expandableMetadata;
private final ByteArrayFilterGroup searchResultRecommendations; private final ByteArrayFilterGroup searchResultRecommendations;
private final StringFilterGroup searchResultVideo; private final StringFilterGroup searchResultVideo;
private final StringFilterGroup compactChannelBarInner; private final StringFilterGroup compactChannelBarInner;
private final StringFilterGroup compactChannelBarInnerButton; private final StringFilterGroup compactChannelBarInnerButton;
private final ByteArrayFilterGroup joinMembershipButton; private final ByteArrayFilterGroup joinMembershipButton;
private final StringFilterGroup likeSubscribeGlow;
private final StringFilterGroup horizontalShelves; private final StringFilterGroup horizontalShelves;
@RequiresApi(api = Build.VERSION_CODES.N)
public LayoutComponentsFilter() { public LayoutComponentsFilter() {
exceptions.addPatterns( exceptions.addPatterns(
"home_video_with_context", "home_video_with_context",
@@ -80,7 +73,9 @@ public final class LayoutComponentsFilter extends Filter {
"images_post_root_slim.eml", "images_post_root_slim.eml",
"text_post_root_slim.eml", "text_post_root_slim.eml",
"post_base_wrapper_slim.eml", "post_base_wrapper_slim.eml",
"poll_post_root.eml" "poll_post_root.eml",
"videos_post_root.eml",
"post_shelf_slim.eml"
); );
final var communityGuidelines = new StringFilterGroup( final var communityGuidelines = new StringFilterGroup(
@@ -103,6 +98,11 @@ public final class LayoutComponentsFilter extends Filter {
"compact_banner" "compact_banner"
); );
final var subscriptionsChipBar = new StringFilterGroup(
Settings.HIDE_FILTER_BAR_FEED_IN_FEED,
"subscriptions_chip_bar"
);
inFeedSurvey = new StringFilterGroup( inFeedSurvey = new StringFilterGroup(
Settings.HIDE_FEED_SURVEY, Settings.HIDE_FEED_SURVEY,
"in_feed_survey", "in_feed_survey",
@@ -121,8 +121,12 @@ public final class LayoutComponentsFilter extends Filter {
); );
final var infoPanel = new StringFilterGroup( final var infoPanel = new StringFilterGroup(
Settings.HIDE_HIDE_INFO_PANELS, Settings.HIDE_INFO_PANELS,
"publisher_transparency_panel", "publisher_transparency_panel"
);
singleItemInformationPanel = new StringFilterGroup(
Settings.HIDE_INFO_PANELS,
"single_item_information_panel" "single_item_information_panel"
); );
@@ -215,10 +219,6 @@ public final class LayoutComponentsFilter extends Filter {
"sponsorships" "sponsorships"
); );
likeSubscribeGlow = new StringFilterGroup(
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
"animated_button_border.eml"
);
final var channelWatermark = new StringFilterGroup( final var channelWatermark = new StringFilterGroup(
Settings.HIDE_VIDEO_CHANNEL_WATERMARK, Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
@@ -252,7 +252,6 @@ public final class LayoutComponentsFilter extends Filter {
expandableMetadata, expandableMetadata,
inFeedSurvey, inFeedSurvey,
notifyMe, notifyMe,
likeSubscribeGlow,
compactChannelBar, compactChannelBar,
communityPosts, communityPosts,
paidPromotion, paidPromotion,
@@ -267,8 +266,10 @@ public final class LayoutComponentsFilter extends Filter {
compactChannelBarInner, compactChannelBarInner,
medicalPanel, medicalPanel,
infoPanel, infoPanel,
singleItemInformationPanel,
emergencyBox, emergencyBox,
subscribersCommunityGuidelines, subscribersCommunityGuidelines,
subscriptionsChipBar,
channelGuidelines, channelGuidelines,
audioTrackButton, audioTrackButton,
artistCard, artistCard,
@@ -283,6 +284,19 @@ public final class LayoutComponentsFilter extends Filter {
@Override @Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
// This identifier is used not only in players but also in search results:
// https://github.com/ReVanced/revanced-patches/issues/3245
// Until 2024, medical information panels such as Covid 19 also used this identifier and were shown in the search results.
// From 2025, the medical information panel is no longer shown in the search results.
// Therefore, this identifier does not filter when the search bar is activated.
if (matchedGroup == singleItemInformationPanel) {
if (PlayerType.getCurrent().isMaximizedOrFullscreen() || !NavigationBar.isSearchBarActive()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
if (matchedGroup == searchResultVideo) { if (matchedGroup == searchResultVideo) {
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) { if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex); return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
@@ -290,15 +304,6 @@ public final class LayoutComponentsFilter extends Filter {
return false; return false;
} }
if (matchedGroup == likeSubscribeGlow) {
if ((path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
&& path.contains(ANIMATED_VECTOR_TYPE_PATH)) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
// The groups are excluded from the filter due to the exceptions list below. // The groups are excluded from the filter due to the exceptions list below.
// Filter them separately here. // Filter them separately here.
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata) if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
@@ -457,6 +462,12 @@ public final class LayoutComponentsFilter extends Filter {
return true; return true;
} }
// Do not hide if the navigation back button is visible,
// otherwise the content shelves in the YouTube Movie/Courses pages is hidden.
if (NavigationBar.isBackButtonVisible()) {
return false;
}
// Check navigation button last. // Check navigation button last.
// Only filter if the library tab is not selected. // Only filter if the library tab is not selected.
// This check is important as the shelf layout is used for the library tab playlists. // This check is important as the shelf layout is used for the library tab playlists.

View File

@@ -1,9 +1,6 @@
package app.revanced.extension.youtube.patches.components; package app.revanced.extension.youtube.patches.components;
import android.os.Build;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.PlayerType;
@@ -16,7 +13,6 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
private final ByteArrayFilterGroup exception; private final ByteArrayFilterGroup exception;
private final StringFilterGroup videoQualityMenuFooter; private final StringFilterGroup videoQualityMenuFooter;
@RequiresApi(api = Build.VERSION_CODES.N)
public PlayerFlyoutMenuItemsFilter() { public PlayerFlyoutMenuItemsFilter() {
exception = new ByteArrayFilterGroup( exception = new ByteArrayFilterGroup(
// Whitelist Quality menu item when "Hide Additional settings menu" is enabled // Whitelist Quality menu item when "Hide Additional settings menu" is enabled

View File

@@ -8,30 +8,30 @@ import android.widget.ListView;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.components.VideoQualityMenuFilterPatch; import app.revanced.extension.youtube.patches.components.AdvancedVideoQualityMenuFilter;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
/** /**
* This patch contains the logic to show the old video quality menu. * This patch contains the logic to always open the advanced video quality menu.
* Two methods are required, because the quality menu is a RecyclerView in the new YouTube version * Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
* and a ListView in the old one. * and a ListView in the old one.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class RestoreOldVideoQualityMenuPatch { public final class AdvancedVideoQualityMenuPatch {
/** /**
* Injection point. * Injection point.
*/ */
public static void onFlyoutMenuCreate(RecyclerView recyclerView) { public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return; if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
recyclerView.getViewTreeObserver().addOnDrawListener(() -> { recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
try { try {
// Check if the current view is the quality menu. // Check if the current view is the quality menu.
if (!VideoQualityMenuFilterPatch.isVideoQualityMenuVisible || recyclerView.getChildCount() == 0) { if (!AdvancedVideoQualityMenuFilter.isVideoQualityMenuVisible || recyclerView.getChildCount() == 0) {
return; return;
} }
VideoQualityMenuFilterPatch.isVideoQualityMenuVisible = false; AdvancedVideoQualityMenuFilter.isVideoQualityMenuVisible = false;
ViewParent quickQualityViewParent = Utils.getParentView(recyclerView, 3); ViewParent quickQualityViewParent = Utils.getParentView(recyclerView, 3);
if (!(quickQualityViewParent instanceof ViewGroup)) { if (!(quickQualityViewParent instanceof ViewGroup)) {
@@ -39,16 +39,15 @@ public final class RestoreOldVideoQualityMenuPatch {
} }
View firstChild = recyclerView.getChildAt(0); View firstChild = recyclerView.getChildAt(0);
if (!(firstChild instanceof ViewGroup)) { if (!(firstChild instanceof ViewGroup firstChildGroup)) {
return; return;
} }
ViewGroup advancedQualityParentView = (ViewGroup) firstChild; if (firstChildGroup.getChildCount() < 4) {
if (advancedQualityParentView.getChildCount() < 4) {
return; return;
} }
View advancedQualityView = advancedQualityParentView.getChildAt(3); View advancedQualityView = firstChildGroup.getChildAt(3);
if (advancedQualityView == null) { if (advancedQualityView == null) {
return; return;
} }
@@ -71,7 +70,7 @@ public final class RestoreOldVideoQualityMenuPatch {
* Used to force the creation of the advanced menu item for the Shorts quality flyout. * Used to force the creation of the advanced menu item for the Shorts quality flyout.
*/ */
public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) { public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) {
return Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get() || original; return Settings.ADVANCED_VIDEO_QUALITY_MENU.get() || original;
} }
/** /**
@@ -79,8 +78,8 @@ public final class RestoreOldVideoQualityMenuPatch {
* *
* Used if spoofing to an old app version, and also used for the Shorts video quality flyout. * Used if spoofing to an old app version, and also used for the Shorts video quality flyout.
*/ */
public static void showOldVideoQualityMenu(final ListView listView) { public static void showAdvancedVideoQualityMenu(ListView listView) {
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return; if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override @Override

View File

@@ -12,15 +12,19 @@ import java.util.List;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.IntegerSetting; import app.revanced.extension.shared.settings.IntegerSetting;
import app.revanced.extension.youtube.patches.VideoInformation; import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class RememberVideoQualityPatch { public class RememberVideoQualityPatch {
private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2; private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
private static final IntegerSetting wifiQualitySetting = Settings.VIDEO_QUALITY_DEFAULT_WIFI; private static final IntegerSetting videoQualityWifi = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting mobileQualitySetting = Settings.VIDEO_QUALITY_DEFAULT_MOBILE; private static final IntegerSetting videoQualityMobile = Settings.VIDEO_QUALITY_DEFAULT_MOBILE;
private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE;
private static boolean qualityNeedsUpdating; private static boolean qualityNeedsUpdating;
@@ -41,17 +45,29 @@ public class RememberVideoQualityPatch {
@Nullable @Nullable
private static List<Integer> videoQualities; private static List<Integer> videoQualities;
private static boolean shouldRememberVideoQuality() {
BooleanSetting preference = ShortsPlayerState.isOpen() ?
Settings.REMEMBER_SHORTS_QUALITY_LAST_SELECTED
: Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED;
return preference.get();
}
private static void changeDefaultQuality(int defaultQuality) { private static void changeDefaultQuality(int defaultQuality) {
String networkTypeMessage; String networkTypeMessage;
boolean useShortsPreference = ShortsPlayerState.isOpen();
if (Utils.getNetworkType() == NetworkType.MOBILE) { if (Utils.getNetworkType() == NetworkType.MOBILE) {
mobileQualitySetting.save(defaultQuality); if (useShortsPreference) shortsQualityMobile.save(defaultQuality);
else videoQualityMobile.save(defaultQuality);
networkTypeMessage = str("revanced_remember_video_quality_mobile"); networkTypeMessage = str("revanced_remember_video_quality_mobile");
} else { } else {
wifiQualitySetting.save(defaultQuality); if (useShortsPreference) shortsQualityWifi.save(defaultQuality);
else videoQualityWifi.save(defaultQuality);
networkTypeMessage = str("revanced_remember_video_quality_wifi"); networkTypeMessage = str("revanced_remember_video_quality_wifi");
} }
Utils.showToastShort( Utils.showToastShort(str(
str("revanced_remember_video_quality_toast", networkTypeMessage, (defaultQuality + "p"))); useShortsPreference ? "revanced_remember_video_quality_toast_shorts" : "revanced_remember_video_quality_toast",
networkTypeMessage, (defaultQuality + "p")
));
} }
/** /**
@@ -62,9 +78,10 @@ public class RememberVideoQualityPatch {
*/ */
public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) { public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) {
try { try {
boolean useShortsPreference = ShortsPlayerState.isOpen();
final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE
? mobileQualitySetting.get() ? (useShortsPreference ? shortsQualityMobile : videoQualityMobile).get()
: wifiQualitySetting.get(); : (useShortsPreference ? shortsQualityWifi : videoQualityWifi).get();
if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) { if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
return originalQualityIndex; // Nothing to do. return originalQualityIndex; // Nothing to do.
@@ -141,17 +158,17 @@ public class RememberVideoQualityPatch {
* Injection point. Old quality menu. * Injection point. Old quality menu.
*/ */
public static void userChangedQuality(int selectedQualityIndex) { public static void userChangedQuality(int selectedQualityIndex) {
if (!Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED.get()) return; if (shouldRememberVideoQuality()) {
userSelectedQualityIndex = selectedQualityIndex;
userSelectedQualityIndex = selectedQualityIndex; userChangedDefaultQuality = true;
userChangedDefaultQuality = true; }
} }
/** /**
* Injection point. New quality menu. * Injection point. New quality menu.
*/ */
public static void userChangedQualityInNewFlyout(int selectedQuality) { public static void userChangedQualityInNewFlyout(int selectedQuality) {
if (!Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED.get()) return; if (!shouldRememberVideoQuality()) return;
changeDefaultQuality(selectedQuality); // Quality is human readable resolution (ie: 1080). changeDefaultQuality(selectedQuality); // Quality is human readable resolution (ie: 1080).
} }

View File

@@ -6,11 +6,19 @@ import android.content.res.Resources;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.AnimatedVectorDrawable;
import com.airbnb.lottie.LottieAnimationView;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Scanner;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -18,6 +26,8 @@ public final class SeekbarColorPatch {
private static final boolean SEEKBAR_CUSTOM_COLOR_ENABLED = Settings.SEEKBAR_CUSTOM_COLOR.get(); 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. * Default color of the litho seekbar.
* Differs slightly from the default custom seekbar color setting. * Differs slightly from the default custom seekbar color setting.
@@ -25,14 +35,19 @@ public final class SeekbarColorPatch {
private static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000; 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. * Default YouTube seekbar color brightness.
@@ -41,16 +56,21 @@ public final class SeekbarColorPatch {
/** /**
* If {@link Settings#SEEKBAR_CUSTOM_COLOR} is enabled, * 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}. * 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. * Custom seekbar hue, saturation, and brightness values.
*/ */
private static final float[] customSeekbarColorHSV = new float[3]; 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 { static {
float[] hsv = new float[3]; float[] hsv = new float[3];
Color.colorToHSV(ORIGINAL_SEEKBAR_COLOR, hsv); Color.colorToHSV(ORIGINAL_SEEKBAR_COLOR, hsv);
@@ -63,37 +83,22 @@ public final class SeekbarColorPatch {
private static void loadCustomSeekbarColor() { private static void loadCustomSeekbarColor() {
try { try {
seekbarColor = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_VALUE.get()); customSeekbarColor = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_PRIMARY.get());
Color.colorToHSV(seekbarColor, customSeekbarColorHSV); Color.colorToHSV(customSeekbarColor, customSeekbarColorHSV);
customSeekbarColorGradient[0] = customSeekbarColor;
customSeekbarColorGradient[1] = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_ACCENT.get());
} catch (Exception ex) { } catch (Exception ex) {
Utils.showToastShort(str("revanced_seekbar_custom_color_invalid")); 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(); loadCustomSeekbarColor();
} }
} }
public static int getSeekbarColor() { public static int getSeekbarColor() {
return seekbarColor; return customSeekbarColor;
}
/**
* Injection point
*/
public static boolean playerSeekbarGradientEnabled(boolean original) {
if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false;
return original;
}
/**
* Injection point
*/
public static boolean useLotteLaunchSplashScreen(boolean original) {
Logger.printDebug(() -> "useLotteLaunchSplashScreen original: " + original);
if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false;
return original;
} }
private static int colorChannelTo3Bits(int channel8Bits) { private static int colorChannelTo3Bits(int channel8Bits) {
@@ -119,6 +124,17 @@ public final class SeekbarColorPatch {
/** /**
* Injection point * Injection point
*/ */
public static boolean useLotteLaunchSplashScreen(boolean original) {
// This method is only used for development purposes to force the old style launch screen.
// Forcing this off on some devices can cause unexplained startup crashes,
// where the lottie animation is still used even though this condition appears to bypass it.
return original; // false = drawable style, true = lottie style.
}
/**
* Injection point.
* Old drawable style launch screen.
*/
public static void setSplashAnimationDrawableTheme(AnimatedVectorDrawable vectorDrawable) { public static void setSplashAnimationDrawableTheme(AnimatedVectorDrawable vectorDrawable) {
// Alternatively a ColorMatrixColorFilter can be used to change the color of the drawable // Alternatively a ColorMatrixColorFilter can be used to change the color of the drawable
// without using any styles, but a color filter cannot selectively change the seekbar // without using any styles, but a color filter cannot selectively change the seekbar
@@ -126,7 +142,9 @@ public final class SeekbarColorPatch {
// Even if the seekbar color xml value is changed to a completely different color (such as green), // 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. // a color filter still cannot be selectively applied when the drawable has more than 1 color.
try { try {
String seekbarStyle = get9BitStyleIdentifier(seekbarColor); // Must set the color even if custom seekbar is off,
// because the xml color was replaced with a themed value.
String seekbarStyle = get9BitStyleIdentifier(customSeekbarColor);
Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle); Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle);
final int styleIdentifierDefault = Utils.getResourceIdentifier( final int styleIdentifierDefault = Utils.getResourceIdentifier(
@@ -146,6 +164,84 @@ public final class SeekbarColorPatch {
} }
} }
/**
* Injection point.
* Modern Lottie style animation.
*/
public static void setSplashAnimationLottie(LottieAnimationView view, int resourceId) {
try {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED) {
view.patch_setAnimation(resourceId);
return;
}
//noinspection ConstantConditions
if (false) { // Set true to force slow animation for development.
final int longAnimation = Utils.getResourceIdentifier(
Utils.isDarkModeEnabled(Utils.getContext())
? "startup_animation_5s_30fps_dark"
: "startup_animation_5s_30fps_light",
"raw");
if (longAnimation != 0) {
resourceId = longAnimation;
}
}
// Must specify primary key name otherwise the morphing YT logo color is also changed.
String originalKey = "\"k\":";
String originalPrimary = originalKey + "[1,0,0.2,1]";
String originalAccent = originalKey + "[1,0.152941176471,0.56862745098,1]";
String replacementPrimary = originalKey + getColorStringArray(customSeekbarColor);
String replacementAccent = originalKey + getColorStringArray(customSeekbarColorGradient[1]);
String json = loadRawResourceAsString(resourceId);
if (json == null) {
return; // Should never happen.
}
if (BaseSettings.DEBUG.get() && (!json.contains(originalPrimary) || !json.contains(originalAccent))) {
String jsonFinal = json;
Logger.printException(() -> "Could not replace launch animation colors: " + jsonFinal);
}
Logger.printDebug(() -> "Replacing Lottie animation JSON");
json = json.replace(originalPrimary, replacementPrimary);
json = json.replace(originalAccent, replacementAccent);
// cacheKey is not needed since the animation will not be reused.
view.patch_setAnimation(new ByteArrayInputStream(json.getBytes()), null);
} catch (Exception ex) {
Logger.printException(() -> "setSplashAnimationLottie failure", ex);
}
}
private static String getColorStringArray(int color) {
return Arrays.toString(new double[]{
Color.red(color) / 255.0,
Color.green(color) / 255.0,
Color.blue(color) / 255.0,
Color.alpha(color) / 255.0
});
}
private static String loadRawResourceAsString(int resourceId) {
try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId);
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
return scanner.next();
} catch (IOException e) {
Logger.printException(() -> "Could not load resource: " + resourceId);
return null;
}
}
/**
* Injection point.
*/
public static boolean showWatchHistoryProgressDrawable(boolean original) {
return !HIDE_SEEKBAR_THUMBNAIL_ENABLED && original;
}
/** /**
* Injection point. * Injection point.
* *
@@ -156,35 +252,61 @@ public final class SeekbarColorPatch {
*/ */
public static int getLithoColor(int colorValue) { public static int getLithoColor(int colorValue) {
if (colorValue == ORIGINAL_SEEKBAR_COLOR) { if (colorValue == ORIGINAL_SEEKBAR_COLOR) {
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) { if (HIDE_SEEKBAR_THUMBNAIL_ENABLED) {
return 0x00000000; return 0x0;
} }
return getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR); return customSeekbarColor;
} }
return colorValue; 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. * Injection point.
*/ */
public static void setLinearGradient(int[] colors, float[] positions) { public static int[] getPlayerLinearGradient(int[] original) {
final boolean hideSeekbar = Settings.HIDE_SEEKBAR_THUMBNAIL.get(); 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, // Most litho usage of linear gradients is hooked here,
// so must only change if the values are those for the seekbar. // so must only change if the values are those for the seekbar.
if (Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors) if ((Arrays.equals(FEED_ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors)
&& Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions)) { && Arrays.equals(FEED_ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions))) {
Arrays.fill(colors, hideSeekbar return HIDE_SEEKBAR_THUMBNAIL_ENABLED
? 0x00000000 ? HIDDEN_SEEKBAR_GRADIENT_COLORS
: seekbarColor); : customSeekbarColorGradient;
return;
} }
Logger.printDebug(() -> "Ignoring gradient colors: " + Arrays.toString(colors) Logger.printDebug(() -> "Ignoring gradient colors: " + colorArrayToHex(colors)
+ " positions: " + Arrays.toString(positions)); + " positions: " + Arrays.toString(positions));
} }
return colors;
} }
/** /**
@@ -198,7 +320,7 @@ public final class SeekbarColorPatch {
} }
return colorValue == ORIGINAL_SEEKBAR_COLOR return colorValue == ORIGINAL_SEEKBAR_COLOR
? getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR) ? customSeekbarColor
: colorValue; : colorValue;
} }
@@ -208,11 +330,9 @@ public final class SeekbarColorPatch {
* Overrides color used for the video player seekbar. * Overrides color used for the video player seekbar.
*/ */
public static int getVideoPlayerSeekbarColor(int originalColor) { public static int getVideoPlayerSeekbarColor(int originalColor) {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED) { return SEEKBAR_CUSTOM_COLOR_ENABLED
return originalColor; ? getSeekbarColorValue(originalColor)
} : originalColor;
return getSeekbarColorValue(originalColor);
} }
/** /**
@@ -221,10 +341,6 @@ public final class SeekbarColorPatch {
*/ */
private static int getSeekbarColorValue(int originalColor) { private static int getSeekbarColorValue(int originalColor) {
try { try {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED || originalColor == seekbarColor) {
return originalColor; // nothing to do
}
final int alphaDifference = Color.alpha(originalColor) - Color.alpha(ORIGINAL_SEEKBAR_COLOR); final int alphaDifference = Color.alpha(originalColor) - Color.alpha(ORIGINAL_SEEKBAR_COLOR);
// The seekbar uses the same color but different brightness for different situations. // The seekbar uses the same color but different brightness for different situations.
@@ -237,7 +353,7 @@ public final class SeekbarColorPatch {
hsv[1] = customSeekbarColorHSV[1]; hsv[1] = customSeekbarColorHSV[1];
hsv[2] = clamp(customSeekbarColorHSV[2] + brightnessDifference, 0, 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); final int replacementColor = Color.HSVToColor(replacementAlpha, hsv);
Logger.printDebug(() -> String.format("Original color: #%08X replacement color: #%08X", Logger.printDebug(() -> String.format("Original color: #%08X replacement color: #%08X",
originalColor, replacementColor)); originalColor, replacementColor));

View File

@@ -37,7 +37,6 @@ import java.util.concurrent.*;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.ThemeHelper; import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
import app.revanced.extension.youtube.returnyoutubedislike.requests.RYDVoteData; import app.revanced.extension.youtube.returnyoutubedislike.requests.RYDVoteData;
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi; import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
@@ -87,9 +86,6 @@ public class ReturnYouTubeDislike {
*/ */
private static final char MIDDLE_SEPARATOR_CHARACTER = '◎'; // 'bullseye' private static final char MIDDLE_SEPARATOR_CHARACTER = '◎'; // 'bullseye'
private static final boolean IS_SPOOFING_TO_OLD_SEPARATOR_COLOR
= SpoofAppVersionPatch.isSpoofingToLessThan("18.10.00");
/** /**
* Cached lookup of all video ids. * Cached lookup of all video ids.
*/ */
@@ -184,17 +180,8 @@ public class ReturnYouTubeDislike {
* Color of the left and middle separator, based on the color of the right separator. * Color of the left and middle separator, based on the color of the right separator.
* It's unknown where YT gets the color from, and the values here are approximated by hand. * It's unknown where YT gets the color from, and the values here are approximated by hand.
* Ideally, this would be the actual color YT uses at runtime. * Ideally, this would be the actual color YT uses at runtime.
*
* Older versions before the 'Me' library tab use a slightly different color.
* If spoofing was previously used and is now turned off,
* or an old version was recently upgraded then the old colors are sometimes still used.
*/ */
private static int getSeparatorColor() { private static int getSeparatorColor() {
if (IS_SPOOFING_TO_OLD_SEPARATOR_COLOR) {
return ThemeHelper.isDarkTheme()
? 0x29AAAAAA // transparent dark gray
: 0xFFD9D9D9; // light gray
}
return ThemeHelper.isDarkTheme() return ThemeHelper.isDarkTheme()
? 0x33FFFFFF ? 0x33FFFFFF
: 0xFFD9D9D9; : 0xFFD9D9D9;
@@ -234,6 +221,12 @@ public class ReturnYouTubeDislike {
// example video: https://www.youtube.com/watch?v=UnrU5vxCHxw // example video: https://www.youtube.com/watch?v=UnrU5vxCHxw
// RYD data: https://returnyoutubedislikeapi.com/votes?videoId=UnrU5vxCHxw // RYD data: https://returnyoutubedislikeapi.com/votes?videoId=UnrU5vxCHxw
// //
if (!Settings.RYD_ESTIMATED_LIKE.get()) {
// Change the "Likes" string to show that likes and dislikes are hidden.
String hiddenMessageString = str("revanced_ryd_video_likes_hidden_by_video_owner");
return newSpanUsingStylingOfAnotherSpan(oldSpannable, hiddenMessageString);
}
Logger.printDebug(() -> "Using estimated likes"); Logger.printDebug(() -> "Using estimated likes");
oldLikes = formatDislikeCount(voteData.getLikeCount()); oldLikes = formatDislikeCount(voteData.getLikeCount());
} }
@@ -346,56 +339,52 @@ public class ReturnYouTubeDislike {
} }
private static String formatDislikeCount(long dislikeCount) { private static String formatDislikeCount(long dislikeCount) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe.
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize if (dislikeCountFormatter == null) {
if (dislikeCountFormatter == null) { // Must use default locale and not Utils context locale,
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale; // otherwise if using a different settings language then the
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT); // formatting will use that of the different language.
Locale locale = Locale.getDefault();
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
// YouTube disregards locale specific number characters // YouTube disregards locale specific number characters
// and instead shows english number characters everywhere. // and instead shows English number characters everywhere.
// To use the same behavior, override the digit characters to use English // To use the same behavior, override the digit characters to use English
// so languages such as Arabic will show "1.234" instead of the native "۱,۲۳٤" // so languages such as Arabic will show "1.234" instead of the native "۱,۲۳٤"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings()); symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
dislikeCountFormatter.setDecimalFormatSymbols(symbols); dislikeCountFormatter.setDecimalFormatSymbols(symbols);
}
} }
return dislikeCountFormatter.format(dislikeCount);
} }
}
// Will never be reached, as the oldest supported YouTube app requires Android N or greater. return dislikeCountFormatter.format(dislikeCount);
return String.valueOf(dislikeCount); }
} }
private static String formatDislikePercentage(float dislikePercentage) { private static String formatDislikePercentage(float dislikePercentage) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe, must synchronize.
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize if (dislikePercentageFormatter == null) {
if (dislikePercentageFormatter == null) { Locale locale = Locale.getDefault();
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale; dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
// Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns. // Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& dislikePercentageFormatter instanceof DecimalFormat) { && dislikePercentageFormatter instanceof DecimalFormat decimalFormatter) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings()); symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
((DecimalFormat) dislikePercentageFormatter).setDecimalFormatSymbols(symbols); decimalFormatter.setDecimalFormatSymbols(symbols);
}
} }
if (dislikePercentage >= 0.01) { // at least 1%
dislikePercentageFormatter.setMaximumFractionDigits(0); // show only whole percentage points
} else {
dislikePercentageFormatter.setMaximumFractionDigits(1); // show up to 1 digit precision
}
return dislikePercentageFormatter.format(dislikePercentage);
} }
}
// Will never be reached, as the oldest supported YouTube app requires Android N or greater. if (dislikePercentage >= 0.01) { // at least 1%
return String.valueOf((int) (dislikePercentage * 100)); dislikePercentageFormatter.setMaximumFractionDigits(0); // show only whole percentage points
} else {
dislikePercentageFormatter.setMaximumFractionDigits(1); // show up to 1 digit precision
}
return dislikePercentageFormatter.format(dislikePercentage);
}
} }
@NonNull @NonNull
@@ -403,15 +392,13 @@ public class ReturnYouTubeDislike {
Objects.requireNonNull(videoId); Objects.requireNonNull(videoId);
synchronized (fetchCache) { synchronized (fetchCache) {
// Remove any expired entries. // Remove any expired entries.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { final long now = System.currentTimeMillis();
final long now = System.currentTimeMillis(); fetchCache.values().removeIf(value -> {
fetchCache.values().removeIf(value -> { final boolean expired = value.isExpired(now);
final boolean expired = value.isExpired(now); if (expired)
if (expired) Logger.printDebug(() -> "Removing expired fetch: " + value.videoId);
Logger.printDebug(() -> "Removing expired fetch: " + value.videoId); return expired;
return expired; });
});
}
ReturnYouTubeDislike fetch = fetchCache.get(videoId); ReturnYouTubeDislike fetch = fetchCache.get(videoId);
if (fetch == null) { if (fetch == null) {
@@ -551,6 +538,15 @@ public class ReturnYouTubeDislike {
} }
if (spanIsForLikes) { if (spanIsForLikes) {
if (!Utils.containsNumber(original)) {
if (!Settings.RYD_ESTIMATED_LIKE.get()) {
Logger.printDebug(() -> "Likes are hidden");
return original;
} else {
Logger.printDebug(() -> "Using estimated likes");
}
}
// Scrolling Shorts does not cause the Spans to be reloaded, // Scrolling Shorts does not cause the Spans to be reloaded,
// so there is no need to cache the likes for this situations. // so there is no need to cache the likes for this situations.
Logger.printDebug(() -> "Creating likes span for: " + votingData.videoId); Logger.printDebug(() -> "Creating likes span for: " + votingData.videoId);

View File

@@ -5,15 +5,12 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.preference.PreferenceFragment; import android.preference.PreferenceFragment;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toolbar; import android.widget.Toolbar;
import androidx.annotation.RequiresApi;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
@@ -22,6 +19,7 @@ import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.ThemeHelper; import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.VersionCheckPatch; import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment; import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment; import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment; import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
@@ -66,6 +64,10 @@ public class LicenseActivityHook {
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) { if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
return false; return false;
} }
// Spoofing can cause half broken settings menus of old and new settings.
if (SpoofAppVersionPatch.isSpoofingToLessThan("19.35.36")) {
return false;
}
// On the first launch of a clean install, forcing the cairo menu can give a // 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. // half broken appearance because all the preference icons may not be available yet.
@@ -79,7 +81,6 @@ public class LicenseActivityHook {
* <p> * <p>
* Hooks LicenseActivity#onCreate in order to inject our own fragment. * Hooks LicenseActivity#onCreate in order to inject our own fragment.
*/ */
@RequiresApi(api = Build.VERSION_CODES.N)
public static void initialize(Activity licenseActivity) { public static void initialize(Activity licenseActivity) {
try { try {
ThemeHelper.setActivityTheme(licenseActivity); ThemeHelper.setActivityTheme(licenseActivity);
@@ -119,15 +120,13 @@ public class LicenseActivityHook {
} }
} }
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressLint("UseCompatLoadingForDrawables") @SuppressLint("UseCompatLoadingForDrawables")
private static void createToolbar(Activity activity, String toolbarTitleResourceName) { private static void createToolbar(Activity activity, String toolbarTitleResourceName) {
// Replace dummy placeholder toolbar. // Replace dummy placeholder toolbar.
// This is required to fix submenu title alignment issue with Android ASOP 15+ // This is required to fix submenu title alignment issue with Android ASOP 15+
ViewGroup toolBarParent = activity.findViewById( ViewGroup toolBarParent = activity.findViewById(
getResourceIdentifier("revanced_toolbar_parent", "id")); getResourceIdentifier("revanced_toolbar_parent", "id"));
ViewGroup dummyToolbar = toolBarParent.findViewById(getResourceIdentifier( ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
"revanced_toolbar", "id"));
toolbarLayoutParams = dummyToolbar.getLayoutParams(); toolbarLayoutParams = dummyToolbar.getLayoutParams();
toolBarParent.removeView(dummyToolbar); toolBarParent.removeView(dummyToolbar);

View File

@@ -3,7 +3,6 @@ package app.revanced.extension.youtube.settings;
import static java.lang.Boolean.FALSE; import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.settings.Setting.Availability; import static app.revanced.extension.shared.settings.Setting.Availability;
import static app.revanced.extension.shared.settings.Setting.migrateFromOldPreferences;
import static app.revanced.extension.shared.settings.Setting.migrateOldSettingToNew; 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.parent;
import static app.revanced.extension.shared.settings.Setting.parentsAny; import static app.revanced.extension.shared.settings.Setting.parentsAny;
@@ -21,12 +20,13 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4; import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType; import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability; import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_19_17_OR_GREATER;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE; 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.Logger;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting; import app.revanced.extension.shared.settings.BooleanSetting;
@@ -36,7 +36,6 @@ import app.revanced.extension.shared.settings.IntegerSetting;
import app.revanced.extension.shared.settings.LongSetting; import app.revanced.extension.shared.settings.LongSetting;
import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.StringSetting; import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrowAvailability; import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrowAvailability;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability; import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption; import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
@@ -45,10 +44,14 @@ import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
public class Settings extends BaseSettings { public class Settings extends BaseSettings {
// Video // Video
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_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); public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
public static final IntegerSetting SHORTS_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_shorts_quality_default_wifi", -2, true);
public static final IntegerSetting SHORTS_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_shorts_quality_default_mobile", -2, true);
public static final BooleanSetting REMEMBER_SHORTS_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_shorts_quality_last_selected", FALSE);
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
// Speed // Speed
public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true); 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 REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
@@ -61,6 +64,7 @@ public class Settings extends BaseSettings {
// Ads // Ads
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE); 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_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_GENERAL_ADS = new BooleanSetting("revanced_hide_general_ads", TRUE);
public static final BooleanSetting HIDE_GET_PREMIUM = new BooleanSetting("revanced_hide_get_premium", TRUE); public static final BooleanSetting HIDE_GET_PREMIUM = new BooleanSetting("revanced_hide_get_premium", TRUE);
@@ -120,9 +124,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE); public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE); public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true); public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
public static final BooleanSetting DISABLE_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_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 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_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_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
@@ -132,8 +134,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE); public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE);
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE); public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE); public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
public static final BooleanSetting HIDE_END_SCREEN_SUGGESTED_VIDEO = new BooleanSetting("revanced_end_screen_suggested_video", FALSE, true);
public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE); public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE);
public static final BooleanSetting HIDE_HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE); public static final BooleanSetting HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE); public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE); public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE);
public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE); public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE);
@@ -165,21 +168,25 @@ public class Settings extends BaseSettings {
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name", 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)); "org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
// Comments // Comments
public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_summary", FALSE); public static final BooleanSetting HIDE_COMMENTS_AI_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_ai_chat_summary", FALSE);
public static final BooleanSetting HIDE_COMMENTS_AI_SUMMARY = new BooleanSetting("revanced_hide_comments_ai_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_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_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE); public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE); public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE);
public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE); public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
// Description // Description
public static final BooleanSetting HIDE_AI_GENERATED_VIDEO_SUMMARY_SECTION = new BooleanSetting("revanced_hide_ai_generated_video_summary_section", FALSE);
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE); 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_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_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_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); public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE); public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
// Action buttons // Action buttons
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE); public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE); public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE); public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
@@ -213,7 +220,7 @@ public class Settings extends BaseSettings {
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 SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true); 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 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.26.42" : "17.33.42", true, parent(SPOOF_APP_VERSION)); public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
// Custom filter // Custom filter
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE); 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)); public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
@@ -223,7 +230,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SHORTS_BUTTON = new BooleanSetting("revanced_hide_shorts_button", TRUE, true); public static final BooleanSetting HIDE_SHORTS_BUTTON = new BooleanSetting("revanced_hide_shorts_button", TRUE, true);
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true); 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 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 HIDE_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_hide_notifications_button", FALSE, true);
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true,
"revanced_switch_create_with_notifications_button_user_dialog_message");
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, 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_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); public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true);
@@ -267,18 +276,18 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SHORTS_AUTOPLAY_BACKGROUND = new BooleanSetting("revanced_shorts_autoplay_background", TRUE); public static final BooleanSetting SHORTS_AUTOPLAY_BACKGROUND = new BooleanSetting("revanced_shorts_autoplay_background", TRUE);
// Seekbar // Seekbar
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", FALSE);
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", TRUE);
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true); 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 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 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", FALSE);
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
public static final BooleanSetting SEEKBAR_THUMBNAILS_HIGH_QUALITY = new BooleanSetting("revanced_seekbar_thumbnails_high_quality", FALSE, true, 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()); "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 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);
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 // Misc
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE); public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
@@ -300,19 +309,19 @@ public class Settings extends BaseSettings {
// Swipe controls // Swipe controls
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, 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_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE, true);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE); public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE, true);
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true, public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true, public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true, public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127, true, public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
// Debugging parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true, public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true, public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
@@ -326,6 +335,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting RYD_SHORTS = new BooleanSetting("ryd_shorts", TRUE, parent(RYD_ENABLED)); public static final BooleanSetting RYD_SHORTS = new BooleanSetting("ryd_shorts", TRUE, parent(RYD_ENABLED));
public static final BooleanSetting RYD_DISLIKE_PERCENTAGE = new BooleanSetting("ryd_dislike_percentage", FALSE, parent(RYD_ENABLED)); public static final BooleanSetting RYD_DISLIKE_PERCENTAGE = new BooleanSetting("ryd_dislike_percentage", FALSE, parent(RYD_ENABLED));
public static final BooleanSetting RYD_COMPACT_LAYOUT = new BooleanSetting("ryd_compact_layout", FALSE, parent(RYD_ENABLED)); public static final BooleanSetting RYD_COMPACT_LAYOUT = new BooleanSetting("ryd_compact_layout", FALSE, parent(RYD_ENABLED));
public static final BooleanSetting RYD_ESTIMATED_LIKE = new BooleanSetting("ryd_estimated_like", TRUE, parent(RYD_ENABLED));
public static final BooleanSetting RYD_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("ryd_toast_on_connection_error", TRUE, parent(RYD_ENABLED)); public static final BooleanSetting RYD_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("ryd_toast_on_connection_error", TRUE, parent(RYD_ENABLED));
// SponsorBlock // SponsorBlock
@@ -337,13 +347,14 @@ public class Settings extends BaseSettings {
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED)); public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED)); public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
public static final BooleanSetting SB_CREATE_NEW_SEGMENT = new BooleanSetting("sb_create_new_segment", FALSE, parent(SB_ENABLED)); public static final BooleanSetting SB_CREATE_NEW_SEGMENT = new BooleanSetting("sb_create_new_segment", FALSE, parent(SB_ENABLED));
public static final BooleanSetting SB_SQUARE_LAYOUT = new BooleanSetting("sb_square_layout", FALSE, parent(SB_ENABLED));
public static final BooleanSetting SB_COMPACT_SKIP_BUTTON = new BooleanSetting("sb_compact_skip_button", FALSE, parent(SB_ENABLED)); public static final BooleanSetting SB_COMPACT_SKIP_BUTTON = new BooleanSetting("sb_compact_skip_button", FALSE, parent(SB_ENABLED));
public static final BooleanSetting SB_AUTO_HIDE_SKIP_BUTTON = new BooleanSetting("sb_auto_hide_skip_button", TRUE, parent(SB_ENABLED)); public static final BooleanSetting SB_AUTO_HIDE_SKIP_BUTTON = new BooleanSetting("sb_auto_hide_skip_button", TRUE, parent(SB_ENABLED));
public static final BooleanSetting SB_TOAST_ON_SKIP = new BooleanSetting("sb_toast_on_skip", TRUE, parent(SB_ENABLED)); public static final BooleanSetting SB_TOAST_ON_SKIP = new BooleanSetting("sb_toast_on_skip", TRUE, parent(SB_ENABLED));
public static final BooleanSetting SB_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("sb_toast_on_connection_error", TRUE, parent(SB_ENABLED)); public static final BooleanSetting SB_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("sb_toast_on_connection_error", TRUE, parent(SB_ENABLED));
public static final BooleanSetting SB_TRACK_SKIP_COUNT = new BooleanSetting("sb_track_skip_count", TRUE, parent(SB_ENABLED)); public static final BooleanSetting SB_TRACK_SKIP_COUNT = new BooleanSetting("sb_track_skip_count", TRUE, parent(SB_ENABLED));
public static final FloatSetting SB_SEGMENT_MIN_DURATION = new FloatSetting("sb_min_segment_duration", 0F, parent(SB_ENABLED)); public static final FloatSetting SB_SEGMENT_MIN_DURATION = new FloatSetting("sb_min_segment_duration", 0F, parent(SB_ENABLED));
public static final BooleanSetting SB_VIDEO_LENGTH_WITHOUT_SEGMENTS = new BooleanSetting("sb_video_length_without_segments", TRUE, parent(SB_ENABLED)); public static final BooleanSetting SB_VIDEO_LENGTH_WITHOUT_SEGMENTS = new BooleanSetting("sb_video_length_without_segments", FALSE, parent(SB_ENABLED));
public static final StringSetting SB_API_URL = new StringSetting("sb_api_url", "https://sponsor.ajay.app"); public static final StringSetting SB_API_URL = new StringSetting("sb_api_url", "https://sponsor.ajay.app");
public static final BooleanSetting SB_USER_IS_VIP = new BooleanSetting("sb_user_is_vip", FALSE); public static final BooleanSetting SB_USER_IS_VIP = new BooleanSetting("sb_user_is_vip", FALSE);
public static final IntegerSetting SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS = new IntegerSetting("sb_local_time_saved_number_segments", 0); public static final IntegerSetting SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS = new IntegerSetting("sb_local_time_saved_number_segments", 0);
@@ -353,52 +364,53 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false); public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false);
public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue); public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_SPONSOR_COLOR = new StringSetting("sb_sponsor_color", "#00D400"); public static final StringSetting SB_CATEGORY_SPONSOR_COLOR = new StringSetting("sb_sponsor_color", "#00D400");
public static final FloatSetting SB_CATEGORY_SPONSOR_OPACITY = new FloatSetting("sb_sponsor_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color", "#FFFF00"); public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color", "#FFFF00");
public static final FloatSetting SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF"); public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF");
public static final FloatSetting SB_CATEGORY_INTERACTION_OPACITY = new FloatSetting("sb_interaction_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684"); public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684");
public static final FloatSetting SB_CATEGORY_HIGHLIGHT_OPACITY = new FloatSetting("sb_highlight_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF"); public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF");
public static final FloatSetting SB_CATEGORY_INTRO_OPACITY = new FloatSetting("sb_intro_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED"); public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED");
public static final FloatSetting SB_CATEGORY_OUTRO_OPACITY = new FloatSetting("sb_outro_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", IGNORE.reVancedKeyValue); public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", IGNORE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6"); public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6");
public static final FloatSetting SB_CATEGORY_PREVIEW_OPACITY = new FloatSetting("sb_preview_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", IGNORE.reVancedKeyValue); public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", IGNORE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF"); public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF");
public static final FloatSetting SB_CATEGORY_FILLER_OPACITY = new FloatSetting("sb_filler_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC = new StringSetting("sb_music_offtopic", MANUAL_SKIP.reVancedKeyValue); public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC = new StringSetting("sb_music_offtopic", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC_COLOR = new StringSetting("sb_music_offtopic_color", "#FF9900"); public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC_COLOR = new StringSetting("sb_music_offtopic_color", "#FF9900");
public static final FloatSetting SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY = new FloatSetting("sb_music_offtopic_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue); public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF"); public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
public static final FloatSetting SB_CATEGORY_UNSUBMITTED_OPACITY = new FloatSetting("sb_unsubmitted_opacity", 1.0f);
// Deprecated migrations // Deprecated migrations
public static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true); private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true);
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", FALSE); private static final BooleanSetting DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", FALSE);
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
static { static {
// region Migration // region Migration
// Do _not_ delete this SB private user id migration property until sometime in early 2025.
// This is the only setting that cannot be reconfigured if lost,
// and more time should be given for users who rarely upgrade.
SharedPrefCategory sbPrefs = new SharedPrefCategory("sponsor-block");
// Remove the "sb_" prefix, as old settings are saved without it.
String key = DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING.key.substring(3);
migrateFromOldPreferences(sbPrefs, DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING, key);
migrateOldSettingToNew(DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING, SB_PRIVATE_USER_ID);
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_BUTTONS, HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS); migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_BUTTONS, HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS);
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER); migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
// Old spoof versions that no longer work reliably. migrateOldSettingToNew(DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN, HIDE_END_SCREEN_SUGGESTED_VIDEO);
if (SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0) {
Logger.printInfo(() -> "Resetting spoof app version target"); migrateOldSettingToNew(DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU, ADVANCED_VIDEO_QUALITY_MENU);
SPOOF_APP_VERSION_TARGET.resetToDefault();
}
// Migrate renamed enum. // Migrate renamed enum.
//noinspection deprecation //noinspection deprecation
@@ -406,6 +418,41 @@ public class Settings extends BaseSettings {
MINIPLAYER_TYPE.save(MINIMAL); 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();
}
if (!DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.isSetToDefault()) {
SWIPE_OVERLAY_OPACITY.save(DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.get() / 255);
DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.resetToDefault();
}
// Old spoof versions that no longer work.
if (SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0) {
Logger.printInfo(() -> "Resetting spoof app version target");
SPOOF_APP_VERSION_TARGET.resetToDefault();
}
// endregion // endregion
// region SB import/export callbacks // region SB import/export callbacks

View File

@@ -3,18 +3,14 @@ package app.revanced.extension.youtube.settings.preference;
import static android.text.Html.FROM_HTML_MODE_COMPACT; import static android.text.Html.FROM_HTML_MODE_COMPACT;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.preference.Preference; import android.preference.Preference;
import android.text.Html; import android.text.Html;
import android.util.AttributeSet; import android.util.AttributeSet;
import androidx.annotation.RequiresApi;
/** /**
* Allows using basic html for the summary text. * Allows using basic html for the summary text.
*/ */
@SuppressWarnings({"unused", "deprecation"}) @SuppressWarnings({"unused", "deprecation"})
@RequiresApi(api = Build.VERSION_CODES.O)
public class HtmlPreference extends Preference { public class HtmlPreference extends Preference {
{ {
setSummary(Html.fromHtml(getSummary().toString(), FROM_HTML_MODE_COMPACT)); setSummary(Html.fromHtml(getSummary().toString(), FROM_HTML_MODE_COMPACT));

View File

@@ -17,10 +17,7 @@ import android.view.WindowInsets;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toolbar; import android.widget.Toolbar;
import androidx.annotation.RequiresApi;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
@@ -76,7 +73,8 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
} }
} }
Collections.sort(pairsToSort, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first)); pairsToSort.sort((pair1, pair2)
-> pair1.first.compareToIgnoreCase(pair2.first));
CharSequence[] sortedEntries = new CharSequence[entrySize]; CharSequence[] sortedEntries = new CharSequence[entrySize];
CharSequence[] sortedEntryValues = new CharSequence[entrySize]; CharSequence[] sortedEntryValues = new CharSequence[entrySize];
@@ -98,7 +96,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
listPreference.setEntryValues(sortedEntryValues); listPreference.setEntryValues(sortedEntryValues);
} }
@RequiresApi(api = Build.VERSION_CODES.O)
@Override @Override
protected void initialize() { protected void initialize() {
super.initialize(); super.initialize();
@@ -112,6 +109,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference); CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
} }
sortPreferenceListMenu(Settings.CHANGE_START_PAGE);
sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE); sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE);
sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE); sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE);
} catch (Exception ex) { } catch (Exception ex) {
@@ -140,11 +138,13 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
.findViewById(android.R.id.content) .findViewById(android.R.id.content)
.getParent(); .getParent();
// Fix required for Android 15 and YT 19.45+ // Fix edge-to-edge screen with Android 15 and YT 19.45+
// https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
rootView.setOnApplyWindowInsetsListener((v, insets) -> { rootView.setOnApplyWindowInsetsListener((v, insets) -> {
Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars()); Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars());
v.setPadding(0, statusInsets.top, 0, 0); Insets navInsets = insets.getInsets(WindowInsets.Type.navigationBars());
v.setPadding(0, statusInsets.top, 0, navInsets.bottom);
return insets; return insets;
}); });
} }
@@ -153,13 +153,10 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
toolbar.setTitle(childScreen.getTitle()); toolbar.setTitle(childScreen.getTitle());
toolbar.setNavigationIcon(getBackButtonDrawable()); toolbar.setNavigationIcon(getBackButtonDrawable());
toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss()); toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss());
final int margin = (int) TypedValue.applyDimension(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
final int margin = (int) TypedValue.applyDimension( );
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics() toolbar.setTitleMargin(margin, 0, margin, 0);
);
toolbar.setTitleMargin(margin, 0, margin, 0);
}
TextView toolbarTextView = Utils.getChildView(toolbar, TextView toolbarTextView = Utils.getChildView(toolbar,
true, TextView.class::isInstance); true, TextView.class::isInstance);

View File

@@ -14,6 +14,7 @@ import android.preference.PreferenceScreen;
import android.preference.SwitchPreference; import android.preference.SwitchPreference;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch; import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch;
@@ -39,6 +40,11 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
*/ */
private SwitchPreference compactLayoutPreference; private SwitchPreference compactLayoutPreference;
/**
* If hidden likes are replaced with an estimated value.
*/
private SwitchPreference estimatedLikesPreference;
/** /**
* If segmented like/dislike button uses smaller compact layout. * If segmented like/dislike button uses smaller compact layout.
*/ */
@@ -48,6 +54,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
shortsPreference.setEnabled(Settings.RYD_SHORTS.isAvailable()); shortsPreference.setEnabled(Settings.RYD_SHORTS.isAvailable());
percentagePreference.setEnabled(Settings.RYD_DISLIKE_PERCENTAGE.isAvailable()); percentagePreference.setEnabled(Settings.RYD_DISLIKE_PERCENTAGE.isAvailable());
compactLayoutPreference.setEnabled(Settings.RYD_COMPACT_LAYOUT.isAvailable()); compactLayoutPreference.setEnabled(Settings.RYD_COMPACT_LAYOUT.isAvailable());
estimatedLikesPreference.setEnabled(Settings.RYD_ESTIMATED_LIKE.isAvailable());
toastOnRYDNotAvailable.setEnabled(Settings.RYD_TOAST_ON_CONNECTION_ERROR.isAvailable()); toastOnRYDNotAvailable.setEnabled(Settings.RYD_TOAST_ON_CONNECTION_ERROR.isAvailable());
} }
@@ -79,9 +86,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
shortsPreference = new SwitchPreference(context); shortsPreference = new SwitchPreference(context);
shortsPreference.setChecked(Settings.RYD_SHORTS.get()); shortsPreference.setChecked(Settings.RYD_SHORTS.get());
shortsPreference.setTitle(str("revanced_ryd_shorts_title")); shortsPreference.setTitle(str("revanced_ryd_shorts_title"));
String shortsSummary = ReturnYouTubeDislikePatch.IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER String shortsSummary = str("revanced_ryd_shorts_summary_on_disclaimer");
? str("revanced_ryd_shorts_summary_on")
: str("revanced_ryd_shorts_summary_on_disclaimer");
shortsPreference.setSummaryOn(shortsSummary); shortsPreference.setSummaryOn(shortsSummary);
shortsPreference.setSummaryOff(str("revanced_ryd_shorts_summary_off")); shortsPreference.setSummaryOff(str("revanced_ryd_shorts_summary_off"));
shortsPreference.setOnPreferenceChangeListener((pref, newValue) -> { shortsPreference.setOnPreferenceChangeListener((pref, newValue) -> {
@@ -117,6 +122,19 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
}); });
preferenceScreen.addPreference(compactLayoutPreference); preferenceScreen.addPreference(compactLayoutPreference);
estimatedLikesPreference = new SwitchPreference(context);
estimatedLikesPreference.setChecked(Settings.RYD_ESTIMATED_LIKE.get());
estimatedLikesPreference.setTitle(str("revanced_ryd_estimated_like_title"));
estimatedLikesPreference.setSummaryOn(str("revanced_ryd_estimated_like_summary_on"));
estimatedLikesPreference.setSummaryOff(str("revanced_ryd_estimated_like_summary_off"));
estimatedLikesPreference.setOnPreferenceChangeListener((pref, newValue) -> {
Settings.RYD_ESTIMATED_LIKE.save((Boolean) newValue);
ReturnYouTubeDislike.clearAllUICaches();
updateUIState();
return true;
});
preferenceScreen.addPreference(estimatedLikesPreference);
toastOnRYDNotAvailable = new SwitchPreference(context); toastOnRYDNotAvailable = new SwitchPreference(context);
toastOnRYDNotAvailable.setChecked(Settings.RYD_TOAST_ON_CONNECTION_ERROR.get()); toastOnRYDNotAvailable.setChecked(Settings.RYD_TOAST_ON_CONNECTION_ERROR.get());
toastOnRYDNotAvailable.setTitle(str("revanced_ryd_toast_on_connection_error_title")); toastOnRYDNotAvailable.setTitle(str("revanced_ryd_toast_on_connection_error_title"));
@@ -218,6 +236,8 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
"revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_non_zero_summary")); "revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_non_zero_summary"));
preferenceScreen.addPreference(statisticPreference); preferenceScreen.addPreference(statisticPreference);
} }
Utils.setPreferenceTitlesToMultiLineIfNeeded(preferenceScreen);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onCreate failure", ex); Logger.printException(() -> "onCreate failure", ex);
} }

View File

@@ -6,7 +6,6 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.*; import android.preference.*;
import android.text.Html; import android.text.Html;
@@ -18,6 +17,7 @@ import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.preference.ResettableEditTextPreference;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController; import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings; import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
@@ -37,15 +37,16 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
private SwitchPreference sbEnabled; private SwitchPreference sbEnabled;
private SwitchPreference addNewSegment; private SwitchPreference addNewSegment;
private SwitchPreference votingEnabled; private SwitchPreference votingEnabled;
private SwitchPreference compactSkipButton;
private SwitchPreference autoHideSkipSegmentButton; private SwitchPreference autoHideSkipSegmentButton;
private SwitchPreference compactSkipButton;
private SwitchPreference squareLayout;
private SwitchPreference showSkipToast; private SwitchPreference showSkipToast;
private SwitchPreference trackSkips; private SwitchPreference trackSkips;
private SwitchPreference showTimeWithoutSegments; private SwitchPreference showTimeWithoutSegments;
private SwitchPreference toastOnConnectionError; private SwitchPreference toastOnConnectionError;
private EditTextPreference newSegmentStep; private ResettableEditTextPreference newSegmentStep;
private EditTextPreference minSegmentDuration; private ResettableEditTextPreference minSegmentDuration;
private EditTextPreference privateUserId; private EditTextPreference privateUserId;
private EditTextPreference importExport; private EditTextPreference importExport;
private Preference apiUrl; private Preference apiUrl;
@@ -62,7 +63,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
} else if (!Settings.SB_CREATE_NEW_SEGMENT.get()) { } else if (!Settings.SB_CREATE_NEW_SEGMENT.get()) {
SponsorBlockViewController.hideNewSegmentLayout(); SponsorBlockViewController.hideNewSegmentLayout();
} }
// Voting and add new segment buttons automatically shows/hide themselves. // Voting and add new segment buttons automatically show/hide themselves.
SponsorBlockViewController.updateLayout();
sbEnabled.setChecked(enabled); sbEnabled.setChecked(enabled);
@@ -72,11 +75,14 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
votingEnabled.setChecked(Settings.SB_VOTING_BUTTON.get()); votingEnabled.setChecked(Settings.SB_VOTING_BUTTON.get());
votingEnabled.setEnabled(enabled); votingEnabled.setEnabled(enabled);
autoHideSkipSegmentButton.setEnabled(enabled);
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get());
compactSkipButton.setChecked(Settings.SB_COMPACT_SKIP_BUTTON.get()); compactSkipButton.setChecked(Settings.SB_COMPACT_SKIP_BUTTON.get());
compactSkipButton.setEnabled(enabled); compactSkipButton.setEnabled(enabled);
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get()); squareLayout.setChecked(Settings.SB_SQUARE_LAYOUT.get());
autoHideSkipSegmentButton.setEnabled(enabled); squareLayout.setEnabled(enabled);
showSkipToast.setChecked(Settings.SB_TOAST_ON_SKIP.get()); showSkipToast.setChecked(Settings.SB_TOAST_ON_SKIP.get());
showSkipToast.setEnabled(enabled); showSkipToast.setEnabled(enabled);
@@ -154,6 +160,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
addAboutCategory(context, preferenceScreen); addAboutCategory(context, preferenceScreen);
Utils.setPreferenceTitlesToMultiLineIfNeeded(preferenceScreen);
updateUI(); updateUI();
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onCreate failure", ex); Logger.printException(() -> "onCreate failure", ex);
@@ -176,6 +184,17 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
return true; return true;
}); });
autoHideSkipSegmentButton = new SwitchPreference(context);
autoHideSkipSegmentButton.setTitle(str("revanced_sb_enable_auto_hide_skip_segment_button"));
autoHideSkipSegmentButton.setSummaryOn(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_on"));
autoHideSkipSegmentButton.setSummaryOff(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_off"));
category.addPreference(autoHideSkipSegmentButton);
autoHideSkipSegmentButton.setOnPreferenceChangeListener((preference1, newValue) -> {
Settings.SB_AUTO_HIDE_SKIP_BUTTON.save((Boolean) newValue);
updateUI();
return true;
});
compactSkipButton = new SwitchPreference(context); compactSkipButton = new SwitchPreference(context);
compactSkipButton.setTitle(str("revanced_sb_enable_compact_skip_button")); compactSkipButton.setTitle(str("revanced_sb_enable_compact_skip_button"));
compactSkipButton.setSummaryOn(str("revanced_sb_enable_compact_skip_button_sum_on")); compactSkipButton.setSummaryOn(str("revanced_sb_enable_compact_skip_button_sum_on"));
@@ -187,13 +206,13 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
return true; return true;
}); });
autoHideSkipSegmentButton = new SwitchPreference(context); squareLayout = new SwitchPreference(context);
autoHideSkipSegmentButton.setTitle(str("revanced_sb_enable_auto_hide_skip_segment_button")); squareLayout.setTitle(str("revanced_sb_square_layout"));
autoHideSkipSegmentButton.setSummaryOn(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_on")); squareLayout.setSummaryOn(str("revanced_sb_square_layout_sum_on"));
autoHideSkipSegmentButton.setSummaryOff(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_off")); squareLayout.setSummaryOff(str("revanced_sb_square_layout_sum_off"));
category.addPreference(autoHideSkipSegmentButton); category.addPreference(squareLayout);
autoHideSkipSegmentButton.setOnPreferenceChangeListener((preference1, newValue) -> { squareLayout.setOnPreferenceChangeListener((preference1, newValue) -> {
Settings.SB_AUTO_HIDE_SKIP_BUTTON.save((Boolean) newValue); Settings.SB_SQUARE_LAYOUT.save((Boolean) newValue);
updateUI(); updateUI();
return true; return true;
}); });
@@ -252,7 +271,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
return true; return true;
}); });
newSegmentStep = new EditTextPreference(context); newSegmentStep = new ResettableEditTextPreference(context);
newSegmentStep.setSetting(Settings.SB_CREATE_NEW_SEGMENT_STEP);
newSegmentStep.setTitle(str("revanced_sb_general_adjusting")); newSegmentStep.setTitle(str("revanced_sb_general_adjusting"));
newSegmentStep.setSummary(str("revanced_sb_general_adjusting_sum")); newSegmentStep.setSummary(str("revanced_sb_general_adjusting_sum"));
newSegmentStep.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER); newSegmentStep.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
@@ -310,7 +330,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
}); });
category.addPreference(trackSkips); category.addPreference(trackSkips);
minSegmentDuration = new EditTextPreference(context); minSegmentDuration = new ResettableEditTextPreference(context);
minSegmentDuration.setSetting(Settings.SB_SEGMENT_MIN_DURATION);
minSegmentDuration.setTitle(str("revanced_sb_general_min_duration")); minSegmentDuration.setTitle(str("revanced_sb_general_min_duration"));
minSegmentDuration.setSummary(str("revanced_sb_general_min_duration_sum")); minSegmentDuration.setSummary(str("revanced_sb_general_min_duration_sum"));
minSegmentDuration.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); minSegmentDuration.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
@@ -329,7 +350,15 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
}); });
category.addPreference(minSegmentDuration); category.addPreference(minSegmentDuration);
privateUserId = new EditTextPreference(context); privateUserId = new EditTextPreference(context) {
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
Utils.setEditTextDialogTheme(builder);
builder.setNeutralButton(str("revanced_sb_settings_copy"), (dialog, which) -> {
Utils.setClipboard(getEditText().getText().toString());
});
}
};
privateUserId.setTitle(str("revanced_sb_general_uuid")); privateUserId.setTitle(str("revanced_sb_general_uuid"));
privateUserId.setSummary(str("revanced_sb_general_uuid_sum")); privateUserId.setSummary(str("revanced_sb_general_uuid_sum"));
privateUserId.setOnPreferenceChangeListener((preference1, newValue) -> { privateUserId.setOnPreferenceChangeListener((preference1, newValue) -> {
@@ -393,9 +422,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_MULTI_LINE
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { importExport.getEditText().setAutofillHints((String) null);
importExport.getEditText().setAutofillHints((String) null);
}
importExport.getEditText().setTextSize(TypedValue.COMPLEX_UNIT_PT, 8); importExport.getEditText().setTextSize(TypedValue.COMPLEX_UNIT_PT, 8);
importExport.setOnPreferenceClickListener(preference1 -> { importExport.setOnPreferenceClickListener(preference1 -> {
importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings()); importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings());
@@ -490,7 +517,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
if (stats.totalSegmentCountIncludingIgnored > 0) { if (stats.totalSegmentCountIncludingIgnored > 0) {
// If user has not created any segments, there's no reason to set a username. // If user has not created any segments, there's no reason to set a username.
EditTextPreference preference = new EditTextPreference(context); EditTextPreference preference = new ResettableEditTextPreference(context);
statsCategory.addPreference(preference); statsCategory.addPreference(preference);
String userName = stats.userName; String userName = stats.userName;
preference.setTitle(fromHtml(str("revanced_sb_stats_username", userName))); preference.setTitle(fromHtml(str("revanced_sb_stats_username", userName)));

View File

@@ -73,6 +73,7 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
if (currentClientType == clientType) { if (currentClientType == clientType) {
return; return;
} }
currentClientType = clientType;
Logger.printDebug(() -> "Updating spoof stream side effects preference"); Logger.printDebug(() -> "Updating spoof stream side effects preference");
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get()); setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
@@ -85,10 +86,12 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
String summary = str(key + "_summary"); String summary = str(key + "_summary");
// Android VR supports AV1 but all other clients do not. // Android VR supports AV1 but all other clients do not.
if (clientType != ClientType.ANDROID_VR && clientType != ClientType.ANDROID_VR_NO_AUTH) { if (clientType != ClientType.ANDROID_VR_AUTH && clientType != ClientType.ANDROID_VR_NO_AUTH) {
summary += '\n' + str("revanced_spoof_video_streams_about_no_av1"); summary += '\n' + str("revanced_spoof_video_streams_about_no_av1");
} }
summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos");
setTitle(title); setTitle(title);
setSummary(summary); setSummary(summary);
} }

View File

@@ -3,11 +3,11 @@ package app.revanced.extension.youtube.shared;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
import android.app.Activity; import android.app.Activity;
import android.os.Build; import android.graphics.drawable.Drawable;
import android.view.View; import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Arrays; import java.util.Arrays;
@@ -26,12 +26,22 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class NavigationBar { public final class NavigationBar {
/**
* Interface to call obfuscated methods in AppCompat Toolbar class.
*/
public interface AppCompatToolbarPatchInterface {
Drawable patch_getNavigationIcon();
}
// //
// Search bar // Search and toolbar.
// //
private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null); private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null);
private static volatile WeakReference<AppCompatToolbarPatchInterface> toolbarResultsRef
= new WeakReference<>(null);
/** /**
* Injection point. * Injection point.
*/ */
@@ -39,6 +49,22 @@ public final class NavigationBar {
searchBarResultsRef = new WeakReference<>(searchbarResults); searchBarResultsRef = new WeakReference<>(searchbarResults);
} }
/**
* Injection point.
*/
public static void setToolbar(FrameLayout layout) {
AppCompatToolbarPatchInterface toolbar = Utils.getChildView(layout, false, (view) ->
view instanceof AppCompatToolbarPatchInterface
);
if (toolbar == null) {
Logger.printException(() -> "Could not find navigation toolbar");
return;
}
toolbarResultsRef = new WeakReference<>(toolbar);
}
/** /**
* @return If the search bar is on screen. This includes if the player * @return If the search bar is on screen. This includes if the player
* is on screen and the search results are behind the player (and not visible). * is on screen and the search results are behind the player (and not visible).
@@ -49,8 +75,13 @@ public final class NavigationBar {
return searchbarResults != null && searchbarResults.getParent() != null; return searchbarResults != null && searchbarResults.getParent() != null;
} }
public static boolean isBackButtonVisible() {
AppCompatToolbarPatchInterface toolbar = toolbarResultsRef.get();
return toolbar != null && toolbar.patch_getNavigationIcon() != null;
}
// //
// Navigation bar buttons // Navigation bar buttons.
// //
/** /**
@@ -257,7 +288,6 @@ public final class NavigationBar {
* Injection point. * Injection point.
* Fixes missing drawable. * Fixes missing drawable.
*/ */
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) { public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
if (fillBellCairoBlack != 0) { if (fillBellCairoBlack != 0) {

View File

@@ -5,7 +5,7 @@ import app.revanced.extension.youtube.Event
import app.revanced.extension.youtube.patches.VideoInformation import app.revanced.extension.youtube.patches.VideoInformation
/** /**
* Main player type. * Regular player type.
*/ */
enum class PlayerType { enum class PlayerType {
/** /**
@@ -21,9 +21,6 @@ enum class PlayerType {
/** /**
* A regular video is minimized. * A regular video is minimized.
*
* When spoofing to 16.x YouTube and watching a short with a regular video in the background,
* the type can be this (and not [HIDDEN]).
*/ */
WATCH_WHILE_MINIMIZED, WATCH_WHILE_MINIMIZED,
WATCH_WHILE_MAXIMIZED, WATCH_WHILE_MAXIMIZED,
@@ -56,8 +53,7 @@ enum class PlayerType {
val newType = nameToPlayerType[enumName] val newType = nameToPlayerType[enumName]
if (newType == null) { if (newType == null) {
Logger.printException { "Unknown PlayerType encountered: $enumName" } Logger.printException { "Unknown PlayerType encountered: $enumName" }
} else if (current != newType) { } else {
Logger.printDebug { "PlayerType changed to: $newType" }
current = newType current = newType
} }
} }
@@ -68,9 +64,13 @@ enum class PlayerType {
@JvmStatic @JvmStatic
var current var current
get() = currentPlayerType get() = currentPlayerType
private set(value) { private set(type) {
currentPlayerType = value if (currentPlayerType != type) {
onChange(currentPlayerType) Logger.printDebug { "Changed to: $type" }
currentPlayerType = type
onChange(type)
}
} }
@Volatile // Read/write from different threads. @Volatile // Read/write from different threads.
@@ -90,8 +90,6 @@ enum class PlayerType {
* Does not include the first moment after a short is opened when a regular video is minimized on screen, * Does not include the first moment after a short is opened when a regular video is minimized on screen,
* or while watching a short with a regular video present on a spoofed 16.x version of YouTube. * or while watching a short with a regular video present on a spoofed 16.x version of YouTube.
* To include those situations instead use [isNoneHiddenOrMinimized]. * To include those situations instead use [isNoneHiddenOrMinimized].
*
* @see VideoInformation
*/ */
fun isNoneOrHidden(): Boolean { fun isNoneOrHidden(): Boolean {
return this == NONE || this == HIDDEN return this == NONE || this == HIDDEN
@@ -107,8 +105,11 @@ enum class PlayerType {
* when spoofing to an old version this will return false even * when spoofing to an old version this will return false even
* though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]). * though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]).
* *
* Instead of this method, consider using {@link ShortsPlayerState}
* which may work better for some situations.
*
* @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state. * @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state.
* @see VideoInformation * @see ShortsPlayerState
*/ */
fun isNoneHiddenOrSlidingMinimized(): Boolean { fun isNoneHiddenOrSlidingMinimized(): Boolean {
return isNoneOrHidden() || this == WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED return isNoneOrHidden() || this == WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED
@@ -125,9 +126,12 @@ enum class PlayerType {
* Typically used to detect if a Short is playing when the player cannot be in a minimized state, * Typically used to detect if a Short is playing when the player cannot be in a minimized state,
* such as the user interacting with a button or element of the player. * such as the user interacting with a button or element of the player.
* *
* Instead of this method, consider using {@link ShortsPlayerState}
* which may work better for some situations.
*
* @return If nothing, a Short, a regular video is sliding off screen to a dismissed or hidden state, * @return If nothing, a Short, a regular video is sliding off screen to a dismissed or hidden state,
* a regular video is minimized (and a new video is not being opened). * a regular video is minimized (and a new video is not being opened).
* @see VideoInformation * @see ShortsPlayerState
*/ */
fun isNoneHiddenOrMinimized(): Boolean { fun isNoneHiddenOrMinimized(): Boolean {
return isNoneHiddenOrSlidingMinimized() || this == WATCH_WHILE_MINIMIZED return isNoneHiddenOrSlidingMinimized() || this == WATCH_WHILE_MINIMIZED

View File

@@ -0,0 +1,38 @@
package app.revanced.extension.youtube.shared
import app.revanced.extension.shared.Logger
import app.revanced.extension.youtube.Event
/**
* Shorts player state.
*/
class ShortsPlayerState {
companion object {
@JvmStatic
fun setOpen(open: Boolean) {
if (isOpen != open) {
Logger.printDebug { "ShortsPlayerState open changed to: $isOpen" }
isOpen = open
onChange(open)
}
}
@Volatile
private var isOpen = false
/**
* Shorts player state change listener.
*/
@JvmStatic
val onChange = Event<Boolean>()
/**
* If the Shorts player is currently open.
*/
@JvmStatic
fun isOpen(): Boolean {
return isOpen
}
}
}

View File

@@ -136,7 +136,7 @@ public class SponsorBlockSettings {
for (SegmentCategory category : categories) { for (SegmentCategory category : categories) {
JSONObject categoryObject = new JSONObject(); JSONObject categoryObject = new JSONObject();
String categoryKey = category.keyValue; String categoryKey = category.keyValue;
categoryObject.put("color", category.colorString()); categoryObject.put("color", category.getColorString());
barTypesObject.put(categoryKey, categoryObject); barTypesObject.put(categoryKey, categoryObject);
if (category.behaviour != CategoryBehaviour.IGNORE) { if (category.behaviour != CategoryBehaviour.IGNORE) {

View File

@@ -5,7 +5,12 @@ import static app.revanced.extension.shared.StringRef.str;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.text.Html; import android.graphics.Color;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.widget.EditText; import android.widget.EditText;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -33,7 +38,7 @@ import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockViewController
* Not thread safe. All fields/methods must be accessed from the main thread. * Not thread safe. All fields/methods must be accessed from the main thread.
*/ */
public class SponsorBlockUtils { public class SponsorBlockUtils {
private static final String LOCKED_COLOR = "#FFC83D"; private static final int LOCKED_COLOR = Color.parseColor("#FFC83D");
private static final String MANUAL_EDIT_TIME_TEXT_HINT = "hh:mm:ss.sss"; private static final String MANUAL_EDIT_TIME_TEXT_HINT = "hh:mm:ss.sss";
private static final Pattern manualEditTimePattern private static final Pattern manualEditTimePattern
= Pattern.compile("((\\d{1,2}):)?(\\d{1,2}):(\\d{2})(\\.(\\d{1,3}))?"); = Pattern.compile("((\\d{1,2}):)?(\\d{1,2}):(\\d{2})(\\.(\\d{1,3}))?");
@@ -160,32 +165,34 @@ public class SponsorBlockUtils {
SegmentVote[] voteOptions = (segment.category == SegmentCategory.HIGHLIGHT) SegmentVote[] voteOptions = (segment.category == SegmentCategory.HIGHLIGHT)
? SegmentVote.voteTypesWithoutCategoryChange // highlight segments cannot change category ? SegmentVote.voteTypesWithoutCategoryChange // highlight segments cannot change category
: SegmentVote.values(); : SegmentVote.values();
CharSequence[] items = new CharSequence[voteOptions.length]; final int voteOptionsLength = voteOptions.length;
final boolean userIsVip = Settings.SB_USER_IS_VIP.get();
CharSequence[] items = new CharSequence[voteOptionsLength];
for (int i = 0; i < voteOptions.length; i++) { for (int i = 0; i < voteOptionsLength; i++) {
SegmentVote voteOption = voteOptions[i]; SegmentVote voteOption = voteOptions[i];
String title = voteOption.title.toString(); CharSequence title = voteOption.title.toString();
if (Settings.SB_USER_IS_VIP.get() && segment.isLocked && voteOption.shouldHighlight) { if (userIsVip && segment.isLocked && voteOption.highlightIfVipAndVideoIsLocked) {
items[i] = Html.fromHtml(String.format("<font color=\"%s\">%s</font>", LOCKED_COLOR, title)); SpannableString coloredTitle = new SpannableString(title);
} else { coloredTitle.setSpan(new ForegroundColorSpan(LOCKED_COLOR),
items[i] = title; 0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
title = coloredTitle;
} }
items[i] = title;
} }
new AlertDialog.Builder(context) new AlertDialog.Builder(context).setItems(items, (dialog1, which1) -> {
.setItems(items, (dialog1, which1) -> { SegmentVote voteOption = voteOptions[which1];
SegmentVote voteOption = voteOptions[which1]; switch (voteOption) {
switch (voteOption) { case UPVOTE:
case UPVOTE: case DOWNVOTE:
case DOWNVOTE: SBRequester.voteForSegmentOnBackgroundThread(segment, voteOption);
SBRequester.voteForSegmentOnBackgroundThread(segment, voteOption); break;
break; case CATEGORY_CHANGE:
case CATEGORY_CHANGE: onNewCategorySelect(segment, context);
onNewCategorySelect(segment, context); break;
break; }
} }).show();
})
.show();
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "segmentVoteClickListener failure", ex); Logger.printException(() -> "segmentVoteClickListener failure", ex);
} }
@@ -282,7 +289,6 @@ public class SponsorBlockUtils {
return; return;
} }
final int numberOfSegments = segments.length; final int numberOfSegments = segments.length;
CharSequence[] titles = new CharSequence[numberOfSegments]; CharSequence[] titles = new CharSequence[numberOfSegments];
for (int i = 0; i < numberOfSegments; i++) { for (int i = 0; i < numberOfSegments; i++) {
@@ -290,22 +296,33 @@ public class SponsorBlockUtils {
if (segment.category == SegmentCategory.UNSUBMITTED) { if (segment.category == SegmentCategory.UNSUBMITTED) {
continue; continue;
} }
StringBuilder htmlBuilder = new StringBuilder();
htmlBuilder.append(String.format("<b><font color=\"#%06X\">⬤</font> %s<br>", SpannableStringBuilder spannableBuilder = new SpannableStringBuilder();
segment.category.color, segment.category.title));
htmlBuilder.append(formatSegmentTime(segment.start)); spannableBuilder.append(segment.category.getTitleWithColorDot());
if (segment.category != SegmentCategory.HIGHLIGHT) { spannableBuilder.append('\n');
htmlBuilder.append(" to ").append(formatSegmentTime(segment.end));
String startTime = formatSegmentTime(segment.start);
if (segment.category == SegmentCategory.HIGHLIGHT) {
spannableBuilder.append(startTime);
} else {
String toFromString = str("revanced_sb_vote_segment_time_to_from",
startTime, formatSegmentTime(segment.end));
spannableBuilder.append(toFromString);
} }
htmlBuilder.append("</b>");
if (i + 1 != numberOfSegments) // prevents trailing new line after last segment if (i + 1 != numberOfSegments) {
htmlBuilder.append("<br>"); // prevents trailing new line after last segment
titles[i] = Html.fromHtml(htmlBuilder.toString()); spannableBuilder.append('\n');
}
spannableBuilder.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
0, spannableBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
titles[i] = spannableBuilder;
} }
new AlertDialog.Builder(context) new AlertDialog.Builder(context).setItems(titles, segmentVoteClickListener).show();
.setItems(titles, segmentVoteClickListener)
.show();
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onVotingClicked failure", ex); Logger.printException(() -> "onVotingClicked failure", ex);
} }

View File

@@ -1,13 +1,14 @@
package app.revanced.extension.youtube.sponsorblock.objects; package app.revanced.extension.youtube.sponsorblock.objects;
import static app.revanced.extension.youtube.settings.Settings.*;
import static app.revanced.extension.shared.StringRef.sf; import static app.revanced.extension.shared.StringRef.sf;
import static app.revanced.extension.youtube.settings.Settings.*;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.text.Html; import android.text.Spannable;
import android.text.Spanned; import android.text.SpannableString;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -15,43 +16,45 @@ import androidx.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.StringRef; import app.revanced.extension.shared.StringRef;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.FloatSetting;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.youtube.settings.Settings;
public enum SegmentCategory { public enum SegmentCategory {
SPONSOR("sponsor", sf("revanced_sb_segments_sponsor"), sf("revanced_sb_segments_sponsor_sum"), sf("revanced_sb_skip_button_sponsor"), sf("revanced_sb_skipped_sponsor"), SPONSOR("sponsor", sf("revanced_sb_segments_sponsor"), sf("revanced_sb_segments_sponsor_sum"), sf("revanced_sb_skip_button_sponsor"), sf("revanced_sb_skipped_sponsor"),
SB_CATEGORY_SPONSOR, SB_CATEGORY_SPONSOR_COLOR), SB_CATEGORY_SPONSOR, SB_CATEGORY_SPONSOR_COLOR, SB_CATEGORY_SPONSOR_OPACITY),
SELF_PROMO("selfpromo", sf("revanced_sb_segments_selfpromo"), sf("revanced_sb_segments_selfpromo_sum"), sf("revanced_sb_skip_button_selfpromo"), sf("revanced_sb_skipped_selfpromo"), SELF_PROMO("selfpromo", sf("revanced_sb_segments_selfpromo"), sf("revanced_sb_segments_selfpromo_sum"), sf("revanced_sb_skip_button_selfpromo"), sf("revanced_sb_skipped_selfpromo"),
SB_CATEGORY_SELF_PROMO, SB_CATEGORY_SELF_PROMO_COLOR), SB_CATEGORY_SELF_PROMO, SB_CATEGORY_SELF_PROMO_COLOR, SB_CATEGORY_SELF_PROMO_OPACITY),
INTERACTION("interaction", sf("revanced_sb_segments_interaction"), sf("revanced_sb_segments_interaction_sum"), sf("revanced_sb_skip_button_interaction"), sf("revanced_sb_skipped_interaction"), INTERACTION("interaction", sf("revanced_sb_segments_interaction"), sf("revanced_sb_segments_interaction_sum"), sf("revanced_sb_skip_button_interaction"), sf("revanced_sb_skipped_interaction"),
SB_CATEGORY_INTERACTION, SB_CATEGORY_INTERACTION_COLOR), SB_CATEGORY_INTERACTION, SB_CATEGORY_INTERACTION_COLOR, SB_CATEGORY_INTERACTION_OPACITY),
/** /**
* Unique category that is treated differently than the rest. * Unique category that is treated differently than the rest.
*/ */
HIGHLIGHT("poi_highlight", sf("revanced_sb_segments_highlight"), sf("revanced_sb_segments_highlight_sum"), sf("revanced_sb_skip_button_highlight"), sf("revanced_sb_skipped_highlight"), HIGHLIGHT("poi_highlight", sf("revanced_sb_segments_highlight"), sf("revanced_sb_segments_highlight_sum"), sf("revanced_sb_skip_button_highlight"), sf("revanced_sb_skipped_highlight"),
SB_CATEGORY_HIGHLIGHT, SB_CATEGORY_HIGHLIGHT_COLOR), SB_CATEGORY_HIGHLIGHT, SB_CATEGORY_HIGHLIGHT_COLOR, SB_CATEGORY_HIGHLIGHT_OPACITY),
INTRO("intro", sf("revanced_sb_segments_intro"), sf("revanced_sb_segments_intro_sum"), INTRO("intro", sf("revanced_sb_segments_intro"), sf("revanced_sb_segments_intro_sum"),
sf("revanced_sb_skip_button_intro_beginning"), sf("revanced_sb_skip_button_intro_middle"), sf("revanced_sb_skip_button_intro_end"), sf("revanced_sb_skip_button_intro_beginning"), sf("revanced_sb_skip_button_intro_middle"), sf("revanced_sb_skip_button_intro_end"),
sf("revanced_sb_skipped_intro_beginning"), sf("revanced_sb_skipped_intro_middle"), sf("revanced_sb_skipped_intro_end"), sf("revanced_sb_skipped_intro_beginning"), sf("revanced_sb_skipped_intro_middle"), sf("revanced_sb_skipped_intro_end"),
SB_CATEGORY_INTRO, SB_CATEGORY_INTRO_COLOR), SB_CATEGORY_INTRO, SB_CATEGORY_INTRO_COLOR, SB_CATEGORY_INTRO_OPACITY),
OUTRO("outro", sf("revanced_sb_segments_outro"), sf("revanced_sb_segments_outro_sum"), sf("revanced_sb_skip_button_outro"), sf("revanced_sb_skipped_outro"), OUTRO("outro", sf("revanced_sb_segments_outro"), sf("revanced_sb_segments_outro_sum"), sf("revanced_sb_skip_button_outro"), sf("revanced_sb_skipped_outro"),
SB_CATEGORY_OUTRO, SB_CATEGORY_OUTRO_COLOR), SB_CATEGORY_OUTRO, SB_CATEGORY_OUTRO_COLOR, SB_CATEGORY_OUTRO_OPACITY),
PREVIEW("preview", sf("revanced_sb_segments_preview"), sf("revanced_sb_segments_preview_sum"), PREVIEW("preview", sf("revanced_sb_segments_preview"), sf("revanced_sb_segments_preview_sum"),
sf("revanced_sb_skip_button_preview_beginning"), sf("revanced_sb_skip_button_preview_middle"), sf("revanced_sb_skip_button_preview_end"), sf("revanced_sb_skip_button_preview_beginning"), sf("revanced_sb_skip_button_preview_middle"), sf("revanced_sb_skip_button_preview_end"),
sf("revanced_sb_skipped_preview_beginning"), sf("revanced_sb_skipped_preview_middle"), sf("revanced_sb_skipped_preview_end"), sf("revanced_sb_skipped_preview_beginning"), sf("revanced_sb_skipped_preview_middle"), sf("revanced_sb_skipped_preview_end"),
SB_CATEGORY_PREVIEW, SB_CATEGORY_PREVIEW_COLOR), SB_CATEGORY_PREVIEW, SB_CATEGORY_PREVIEW_COLOR, SB_CATEGORY_PREVIEW_OPACITY),
FILLER("filler", sf("revanced_sb_segments_filler"), sf("revanced_sb_segments_filler_sum"), sf("revanced_sb_skip_button_filler"), sf("revanced_sb_skipped_filler"), FILLER("filler", sf("revanced_sb_segments_filler"), sf("revanced_sb_segments_filler_sum"), sf("revanced_sb_skip_button_filler"), sf("revanced_sb_skipped_filler"),
SB_CATEGORY_FILLER, SB_CATEGORY_FILLER_COLOR), SB_CATEGORY_FILLER, SB_CATEGORY_FILLER_COLOR, SB_CATEGORY_FILLER_OPACITY),
MUSIC_OFFTOPIC("music_offtopic", sf("revanced_sb_segments_nomusic"), sf("revanced_sb_segments_nomusic_sum"), sf("revanced_sb_skip_button_nomusic"), sf("revanced_sb_skipped_nomusic"), MUSIC_OFFTOPIC("music_offtopic", sf("revanced_sb_segments_nomusic"), sf("revanced_sb_segments_nomusic_sum"), sf("revanced_sb_skip_button_nomusic"), sf("revanced_sb_skipped_nomusic"),
SB_CATEGORY_MUSIC_OFFTOPIC, SB_CATEGORY_MUSIC_OFFTOPIC_COLOR), SB_CATEGORY_MUSIC_OFFTOPIC, SB_CATEGORY_MUSIC_OFFTOPIC_COLOR, SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY),
UNSUBMITTED("unsubmitted", StringRef.empty, StringRef.empty, sf("revanced_sb_skip_button_unsubmitted"), sf("revanced_sb_skipped_unsubmitted"), UNSUBMITTED("unsubmitted", StringRef.empty, StringRef.empty, sf("revanced_sb_skip_button_unsubmitted"), sf("revanced_sb_skipped_unsubmitted"),
SB_CATEGORY_UNSUBMITTED, SB_CATEGORY_UNSUBMITTED_COLOR),; SB_CATEGORY_UNSUBMITTED, SB_CATEGORY_UNSUBMITTED_COLOR, SB_CATEGORY_UNSUBMITTED_OPACITY);
private static final StringRef skipSponsorTextCompact = sf("revanced_sb_skip_button_compact"); private static final StringRef skipSponsorTextCompact = sf("revanced_sb_skip_button_compact");
private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight"); private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight");
@@ -90,12 +93,10 @@ public enum SegmentCategory {
mValuesMap.put(value.keyValue, value); mValuesMap.put(value.keyValue, value);
} }
@NonNull
public static SegmentCategory[] categoriesWithoutUnsubmitted() { public static SegmentCategory[] categoriesWithoutUnsubmitted() {
return categoriesWithoutUnsubmitted; return categoriesWithoutUnsubmitted;
} }
@NonNull
public static SegmentCategory[] categoriesWithoutHighlights() { public static SegmentCategory[] categoriesWithoutHighlights() {
return categoriesWithoutHighlights; return categoriesWithoutHighlights;
} }
@@ -106,7 +107,7 @@ public enum SegmentCategory {
} }
/** /**
* Must be called if behavior of any category is changed * Must be called if behavior of any category is changed.
*/ */
public static void updateEnabledCategories() { public static void updateEnabledCategories() {
Utils.verifyOnMainThread(); Utils.verifyOnMainThread();
@@ -133,32 +134,33 @@ public enum SegmentCategory {
updateEnabledCategories(); updateEnabledCategories();
} }
@NonNull public static int applyOpacityToColor(int color, float opacity) {
public final String keyValue; if (opacity < 0 || opacity > 1.0f) {
@NonNull throw new IllegalArgumentException("Invalid opacity: " + opacity);
public final StringSetting behaviorSetting; }
@NonNull final int opacityInt = (int) (255 * opacity);
private final StringSetting colorSetting; return (color & 0x00FFFFFF) | (opacityInt << 24);
}
public final String keyValue;
public final StringSetting behaviorSetting; // TODO: Replace with EnumSetting.
private final StringSetting colorSetting;
private final FloatSetting opacitySetting;
@NonNull
public final StringRef title; public final StringRef title;
@NonNull
public final StringRef description; public final StringRef description;
/** /**
* Skip button text, if the skip occurs in the first quarter of the video * Skip button text, if the skip occurs in the first quarter of the video
*/ */
@NonNull
public final StringRef skipButtonTextBeginning; public final StringRef skipButtonTextBeginning;
/** /**
* Skip button text, if the skip occurs in the middle half of the video * Skip button text, if the skip occurs in the middle half of the video
*/ */
@NonNull
public final StringRef skipButtonTextMiddle; public final StringRef skipButtonTextMiddle;
/** /**
* Skip button text, if the skip occurs in the last quarter of the video * Skip button text, if the skip occurs in the last quarter of the video
*/ */
@NonNull
public final StringRef skipButtonTextEnd; public final StringRef skipButtonTextEnd;
/** /**
* Skipped segment toast, if the skip occurred in the first quarter of the video * Skipped segment toast, if the skip occurred in the first quarter of the video
@@ -179,10 +181,7 @@ public enum SegmentCategory {
@NonNull @NonNull
public final Paint paint; public final Paint paint;
/** private int color;
* Value must be changed using {@link #setColor(String)}.
*/
public int color;
/** /**
* Value must be changed using {@link #setBehaviour(CategoryBehaviour)}. * Value must be changed using {@link #setBehaviour(CategoryBehaviour)}.
@@ -194,17 +193,20 @@ public enum SegmentCategory {
SegmentCategory(String keyValue, StringRef title, StringRef description, SegmentCategory(String keyValue, StringRef title, StringRef description,
StringRef skipButtonText, StringRef skipButtonText,
StringRef skippedToastText, StringRef skippedToastText,
StringSetting behavior, StringSetting color) { StringSetting behavior,
StringSetting color, FloatSetting opacity) {
this(keyValue, title, description, this(keyValue, title, description,
skipButtonText, skipButtonText, skipButtonText, skipButtonText, skipButtonText, skipButtonText,
skippedToastText, skippedToastText, skippedToastText, skippedToastText, skippedToastText, skippedToastText,
behavior, color); behavior,
color, opacity);
} }
SegmentCategory(String keyValue, StringRef title, StringRef description, SegmentCategory(String keyValue, StringRef title, StringRef description,
StringRef skipButtonTextBeginning, StringRef skipButtonTextMiddle, StringRef skipButtonTextEnd, StringRef skipButtonTextBeginning, StringRef skipButtonTextMiddle, StringRef skipButtonTextEnd,
StringRef skippedToastBeginning, StringRef skippedToastMiddle, StringRef skippedToastEnd, StringRef skippedToastBeginning, StringRef skippedToastMiddle, StringRef skippedToastEnd,
StringSetting behavior, StringSetting color) { StringSetting behavior,
StringSetting color, FloatSetting opacity) {
this.keyValue = Objects.requireNonNull(keyValue); this.keyValue = Objects.requireNonNull(keyValue);
this.title = Objects.requireNonNull(title); this.title = Objects.requireNonNull(title);
this.description = Objects.requireNonNull(description); this.description = Objects.requireNonNull(description);
@@ -216,6 +218,7 @@ public enum SegmentCategory {
this.skippedToastEnd = Objects.requireNonNull(skippedToastEnd); this.skippedToastEnd = Objects.requireNonNull(skippedToastEnd);
this.behaviorSetting = Objects.requireNonNull(behavior); this.behaviorSetting = Objects.requireNonNull(behavior);
this.colorSetting = Objects.requireNonNull(color); this.colorSetting = Objects.requireNonNull(color);
this.opacitySetting = Objects.requireNonNull(opacity);
this.paint = new Paint(); this.paint = new Paint();
loadFromSettings(); loadFromSettings();
} }
@@ -232,11 +235,14 @@ public enum SegmentCategory {
this.behaviour = savedBehavior; this.behaviour = savedBehavior;
String colorString = colorSetting.get(); String colorString = colorSetting.get();
final float opacity = opacitySetting.get();
try { try {
setColor(colorString); setColor(colorString);
setOpacity(opacity);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "Invalid color: " + colorString, ex); Logger.printException(() -> "Invalid color: " + colorString + " opacity: " + opacity, ex);
colorSetting.resetToDefault(); colorSetting.resetToDefault();
opacitySetting.resetToDefault();
loadFromSettings(); loadFromSettings();
} }
} }
@@ -245,45 +251,78 @@ public enum SegmentCategory {
this.behaviour = Objects.requireNonNull(behaviour); this.behaviour = Objects.requireNonNull(behaviour);
this.behaviorSetting.save(behaviour.reVancedKeyValue); this.behaviorSetting.save(behaviour.reVancedKeyValue);
} }
/**
* @return HTML color format string
*/
@NonNull
public String colorString() {
return String.format("#%06X", color);
}
public void setColor(@NonNull String colorString) throws IllegalArgumentException { private void updateColor() {
final int color = Color.parseColor(colorString) & 0xFFFFFF; color = applyOpacityToColor(color, opacitySetting.get());
this.color = color;
paint.setColor(color); paint.setColor(color);
paint.setAlpha(255);
colorSetting.save(colorString); // Save after parsing.
} }
public void resetColor() { /**
* @param opacity Segment color opacity between [0, 1].
*/
public void setOpacity(float opacity) throws IllegalArgumentException {
if (opacity < 0 || opacity > 1) {
throw new IllegalArgumentException("Invalid opacity: " + opacity);
}
opacitySetting.save(opacity);
updateColor();
}
public float getOpacity() {
return opacitySetting.get();
}
public void resetColorAndOpacity() {
setColor(colorSetting.defaultValue); setColor(colorSetting.defaultValue);
setOpacity(opacitySetting.defaultValue);
} }
@NonNull /**
private static String getCategoryColorDotHTML(int color) { * @param colorString Segment color with #RRGGBB format.
color &= 0xFFFFFF; */
return String.format("<font color=\"#%06X\">⬤</font>", color); public void setColor(String colorString) throws IllegalArgumentException {
color = Color.parseColor(colorString);
colorSetting.save(colorString);
updateColor();
} }
@NonNull /**
public static Spanned getCategoryColorDot(int color) { * @return Integer color of #RRGGBB format.
return Html.fromHtml(getCategoryColorDotHTML(color)); */
public int getColorNoOpacity() {
return color & 0x00FFFFFF;
} }
@NonNull /**
public Spanned getCategoryColorDot() { * @return Hex color string of #RRGGBB format with no opacity level.
*/
public String getColorString() {
return String.format(Locale.US, "#%06X", getColorNoOpacity());
}
private static SpannableString getCategoryColorDotSpan(String text, int color) {
SpannableString dotSpan = new SpannableString('⬤' + text);
dotSpan.setSpan(new ForegroundColorSpan(color), 0, 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return dotSpan;
}
public static SpannableString getCategoryColorDot(int color) {
return getCategoryColorDotSpan("", color);
}
public SpannableString getCategoryColorDot() {
return getCategoryColorDot(color); return getCategoryColorDot(color);
} }
@NonNull public SpannableString getTitleWithColorDot(int categoryColor) {
public Spanned getTitleWithColorDot() { return getCategoryColorDotSpan(" " + title, categoryColor);
return Html.fromHtml(getCategoryColorDotHTML(color) + " " + title); }
public SpannableString getTitleWithColorDot() {
return getTitleWithColorDot(color);
} }
/** /**
@@ -291,7 +330,6 @@ public enum SegmentCategory {
* @param videoLength length of the video * @param videoLength length of the video
* @return the skip button text * @return the skip button text
*/ */
@NonNull
StringRef getSkipButtonText(long segmentStartTime, long videoLength) { StringRef getSkipButtonText(long segmentStartTime, long videoLength) {
if (Settings.SB_COMPACT_SKIP_BUTTON.get()) { if (Settings.SB_COMPACT_SKIP_BUTTON.get()) {
return (this == SegmentCategory.HIGHLIGHT) return (this == SegmentCategory.HIGHLIGHT)
@@ -300,7 +338,7 @@ public enum SegmentCategory {
} }
if (videoLength == 0) { if (videoLength == 0) {
return skipButtonTextBeginning; // video is still loading. Assume it's the beginning return skipButtonTextBeginning; // Video is still loading. Assume it's the beginning.
} }
final float position = segmentStartTime / (float) videoLength; final float position = segmentStartTime / (float) videoLength;
if (position < 0.25f) { if (position < 0.25f) {
@@ -316,10 +354,9 @@ public enum SegmentCategory {
* @param videoLength length of the video * @param videoLength length of the video
* @return 'skipped segment' toast message * @return 'skipped segment' toast message
*/ */
@NonNull
StringRef getSkippedToastText(long segmentStartTime, long videoLength) { StringRef getSkippedToastText(long segmentStartTime, long videoLength) {
if (videoLength == 0) { if (videoLength == 0) {
return skippedToastBeginning; // video is still loading. Assume it's the beginning return skippedToastBeginning; // Video is still loading. Assume it's the beginning.
} }
final float position = segmentStartTime / (float) videoLength; final float position = segmentStartTime / (float) videoLength;
if (position < 0.25f) { if (position < 0.25f) {

View File

@@ -1,6 +1,7 @@
package app.revanced.extension.youtube.sponsorblock.objects; package app.revanced.extension.youtube.sponsorblock.objects;
import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory.applyOpacityToColor;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
@@ -11,11 +12,10 @@ import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout; import android.widget.GridLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView; import android.widget.TextView;
import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
@@ -24,27 +24,38 @@ import app.revanced.extension.shared.Utils;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class SegmentCategoryListPreference extends ListPreference { public class SegmentCategoryListPreference extends ListPreference {
private final SegmentCategory category; private final SegmentCategory category;
private EditText mEditText; private TextView colorDotView;
private int mClickedDialogEntryIndex; private EditText colorEditText;
private EditText opacityEditText;
/**
* #RRGGBB
*/
private int categoryColor;
/**
* [0, 1]
*/
private float categoryOpacity;
private int selectedDialogEntryIndex;
public SegmentCategoryListPreference(Context context, SegmentCategory category) { public SegmentCategoryListPreference(Context context, SegmentCategory category) {
super(context); super(context);
final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
this.category = Objects.requireNonNull(category); this.category = Objects.requireNonNull(category);
// Edit: Using preferences to sync together multiple pieces // Edit: Using preferences to sync together multiple pieces
// of code together is messy and should be rethought. // of code is messy and should be rethought.
setKey(category.behaviorSetting.key); setKey(category.behaviorSetting.key);
setDefaultValue(category.behaviorSetting.defaultValue); setDefaultValue(category.behaviorSetting.defaultValue);
final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
setEntries(isHighlightCategory setEntries(isHighlightCategory
? CategoryBehaviour.getBehaviorDescriptionsWithoutSkipOnce() ? CategoryBehaviour.getBehaviorDescriptionsWithoutSkipOnce()
: CategoryBehaviour.getBehaviorDescriptions()); : CategoryBehaviour.getBehaviorDescriptions());
setEntryValues(isHighlightCategory setEntryValues(isHighlightCategory
? CategoryBehaviour.getBehaviorKeyValuesWithoutSkipOnce() ? CategoryBehaviour.getBehaviorKeyValuesWithoutSkipOnce()
: CategoryBehaviour.getBehaviorKeyValues()); : CategoryBehaviour.getBehaviorKeyValues());
setSummary(category.description.toString()); setSummary(category.description.toString());
updateTitle();
updateTitleFromCategory();
} }
@Override @Override
@@ -52,26 +63,40 @@ public class SegmentCategoryListPreference extends ListPreference {
try { try {
Utils.setEditTextDialogTheme(builder); Utils.setEditTextDialogTheme(builder);
categoryColor = category.getColorNoOpacity();
categoryOpacity = category.getOpacity();
Context context = builder.getContext(); Context context = builder.getContext();
TableLayout table = new TableLayout(context); GridLayout gridLayout = new GridLayout(context);
table.setOrientation(LinearLayout.HORIZONTAL); gridLayout.setPadding(70, 0, 150, 0); // Padding for the entire layout.
table.setPadding(70, 0, 150, 0); gridLayout.setColumnCount(3);
gridLayout.setRowCount(2);
TableRow row = new TableRow(context);
GridLayout.LayoutParams gridParams = new GridLayout.LayoutParams();
gridParams.rowSpec = GridLayout.spec(0); // First row.
gridParams.columnSpec = GridLayout.spec(0); // First column.
TextView colorTextLabel = new TextView(context); TextView colorTextLabel = new TextView(context);
colorTextLabel.setText(str("revanced_sb_color_dot_label")); colorTextLabel.setText(str("revanced_sb_color_dot_label"));
row.addView(colorTextLabel); colorTextLabel.setLayoutParams(gridParams);
gridLayout.addView(colorTextLabel);
TextView colorDotView = new TextView(context); gridParams = new GridLayout.LayoutParams();
colorDotView.setText(category.getCategoryColorDot()); gridParams.rowSpec = GridLayout.spec(0); // First row.
colorDotView.setPadding(30, 0, 30, 0); gridParams.columnSpec = GridLayout.spec(1); // Second column.
row.addView(colorDotView); gridParams.setMargins(0, 0, 10, 0);
colorDotView = new TextView(context);
colorDotView.setLayoutParams(gridParams);
gridLayout.addView(colorDotView);
updateCategoryColorDot();
mEditText = new EditText(context); gridParams = new GridLayout.LayoutParams();
mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS); gridParams.rowSpec = GridLayout.spec(0); // First row.
mEditText.setText(category.colorString()); gridParams.columnSpec = GridLayout.spec(2); // Third column.
mEditText.addTextChangedListener(new TextWatcher() { colorEditText = new EditText(context);
colorEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
colorEditText.setTextLocale(Locale.US);
colorEditText.setText(category.getColorString());
colorEditText.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
} }
@@ -81,29 +106,94 @@ public class SegmentCategoryListPreference extends ListPreference {
} }
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable edit) {
try { try {
String colorString = s.toString(); String colorString = edit.toString();
final int colorStringLength = colorString.length();
if (!colorString.startsWith("#")) { if (!colorString.startsWith("#")) {
s.insert(0, "#"); // recursively calls back into this method edit.insert(0, "#"); // Recursively calls back into this method.
return; return;
} }
if (colorString.length() > 7) {
s.delete(7, colorString.length()); final int maxColorStringLength = 7; // #RRGGBB
if (colorStringLength > maxColorStringLength) {
edit.delete(maxColorStringLength, colorStringLength);
return; return;
} }
final int color = Color.parseColor(colorString);
colorDotView.setText(SegmentCategory.getCategoryColorDot(color)); categoryColor = Color.parseColor(colorString);
updateCategoryColorDot();
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
// ignore // Ignore.
} }
} }
}); });
mEditText.setLayoutParams(new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 1f)); colorEditText.setLayoutParams(gridParams);
row.addView(mEditText); gridLayout.addView(colorEditText);
table.addView(row); gridParams = new GridLayout.LayoutParams();
builder.setView(table); gridParams.rowSpec = GridLayout.spec(1); // Second row.
gridParams.columnSpec = GridLayout.spec(0, 1); // First and second column.
TextView opacityLabel = new TextView(context);
opacityLabel.setText(str("revanced_sb_color_opacity_label"));
opacityLabel.setLayoutParams(gridParams);
gridLayout.addView(opacityLabel);
gridParams = new GridLayout.LayoutParams();
gridParams.rowSpec = GridLayout.spec(1); // Second row.
gridParams.columnSpec = GridLayout.spec(2); // Third column.
opacityEditText = new EditText(context);
opacityEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
opacityEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable edit) {
try {
String editString = edit.toString();
final int opacityStringLength = editString.length();
final int maxOpacityStringLength = 4; // [0.00, 1.00]
if (opacityStringLength > maxOpacityStringLength) {
edit.delete(maxOpacityStringLength, opacityStringLength);
return;
}
final float opacity = opacityStringLength == 0
? 0
: Float.parseFloat(editString);
if (opacity < 0) {
categoryOpacity = 0;
edit.replace(0, opacityStringLength, "0");
return;
} else if (opacity > 1.0f) {
categoryOpacity = 1;
edit.replace(0, opacityStringLength, "1.0");
return;
} else if (!editString.endsWith(".")) {
// Ignore "0." and "1." until the user finishes entering a valid number.
categoryOpacity = opacity;
}
updateCategoryColorDot();
} catch (NumberFormatException ex) {
// Should never happen.
Logger.printException(() -> "Could not parse opacity string", ex);
}
}
});
opacityEditText.setLayoutParams(gridParams);
gridLayout.addView(opacityEditText);
updateOpacityText();
builder.setView(gridLayout);
builder.setTitle(category.title.toString()); builder.setTitle(category.title.toString());
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
@@ -111,8 +201,8 @@ public class SegmentCategoryListPreference extends ListPreference {
}); });
builder.setNeutralButton(str("revanced_sb_reset_color"), (dialog, which) -> { builder.setNeutralButton(str("revanced_sb_reset_color"), (dialog, which) -> {
try { try {
category.resetColor(); category.resetColorAndOpacity();
updateTitle(); updateTitleFromCategory();
Utils.showToastShort(str("revanced_sb_color_reset")); Utils.showToastShort(str("revanced_sb_color_reset"));
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "setNeutralButton failure", ex); Logger.printException(() -> "setNeutralButton failure", ex);
@@ -120,8 +210,9 @@ public class SegmentCategoryListPreference extends ListPreference {
}); });
builder.setNegativeButton(android.R.string.cancel, null); builder.setNegativeButton(android.R.string.cancel, null);
mClickedDialogEntryIndex = findIndexOfValue(getValue()); selectedDialogEntryIndex = findIndexOfValue(getValue());
builder.setSingleChoiceItems(getEntries(), mClickedDialogEntryIndex, (dialog, which) -> mClickedDialogEntryIndex = which); builder.setSingleChoiceItems(getEntries(), selectedDialogEntryIndex,
(dialog, which) -> selectedDialogEntryIndex = which);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onPrepareDialogBuilder failure", ex); Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
} }
@@ -130,30 +221,51 @@ public class SegmentCategoryListPreference extends ListPreference {
@Override @Override
protected void onDialogClosed(boolean positiveResult) { protected void onDialogClosed(boolean positiveResult) {
try { try {
if (positiveResult && mClickedDialogEntryIndex >= 0 && getEntryValues() != null) { if (positiveResult && selectedDialogEntryIndex >= 0 && getEntryValues() != null) {
String value = getEntryValues()[mClickedDialogEntryIndex].toString(); String value = getEntryValues()[selectedDialogEntryIndex].toString();
if (callChangeListener(value)) { if (callChangeListener(value)) {
setValue(value); setValue(value);
category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value))); category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
SegmentCategory.updateEnabledCategories(); SegmentCategory.updateEnabledCategories();
} }
String colorString = mEditText.getText().toString();
try { try {
if (!colorString.equals(category.colorString())) { String colorString = colorEditText.getText().toString();
if (!colorString.equals(category.getColorString()) || categoryOpacity != category.getOpacity()) {
category.setColor(colorString); category.setColor(colorString);
category.setOpacity(categoryOpacity);
Utils.showToastShort(str("revanced_sb_color_changed")); Utils.showToastShort(str("revanced_sb_color_changed"));
} }
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
Utils.showToastShort(str("revanced_sb_color_invalid")); Utils.showToastShort(str("revanced_sb_color_invalid"));
} }
updateTitle();
updateTitleFromCategory();
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "onDialogClosed failure", ex); Logger.printException(() -> "onDialogClosed failure", ex);
} }
} }
private void updateTitle() { private void applyOpacityToCategoryColor() {
setTitle(category.getTitleWithColorDot()); categoryColor = applyOpacityToColor(categoryColor, categoryOpacity);
}
private void updateTitleFromCategory() {
categoryColor = category.getColorNoOpacity();
categoryOpacity = category.getOpacity();
applyOpacityToCategoryColor();
setTitle(category.getTitleWithColorDot(categoryColor));
}
private void updateCategoryColorDot() {
applyOpacityToCategoryColor();
colorDotView.setText(SegmentCategory.getCategoryColorDot(categoryColor));
}
private void updateOpacityText() {
opacityEditText.setText(String.format(Locale.US, "%.2f", categoryOpacity));
} }
} }

View File

@@ -23,12 +23,15 @@ public class SponsorSegment implements Comparable<SponsorSegment> {
@NonNull @NonNull
public final StringRef title; public final StringRef title;
public final int apiVoteType; public final int apiVoteType;
public final boolean shouldHighlight; /**
* If the option should be highlighted for VIP users.
*/
public final boolean highlightIfVipAndVideoIsLocked;
SegmentVote(@NonNull StringRef title, int apiVoteType, boolean shouldHighlight) { SegmentVote(@NonNull StringRef title, int apiVoteType, boolean highlightIfVipAndVideoIsLocked) {
this.title = title; this.title = title;
this.apiVoteType = apiVoteType; this.apiVoteType = apiVoteType;
this.shouldHighlight = shouldHighlight; this.highlightIfVipAndVideoIsLocked = highlightIfVipAndVideoIsLocked;
} }
} }

View File

@@ -0,0 +1,56 @@
package app.revanced.extension.youtube.sponsorblock.ui;
import android.view.View;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
public class CreateSegmentButton {
@Nullable
private static PlayerControlButton instance;
public static void hideControls() {
if (instance != null) instance.hide();
}
/**
* injection point
*/
public static void initialize(View controlsView) {
try {
instance = new PlayerControlButton(
controlsView,
"revanced_sb_create_segment_button",
null,
CreateSegmentButton::shouldBeShown,
v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility(),
null
);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
}
/**
* Injection point
*/
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* Injection point
*/
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated);
}
private static boolean shouldBeShown() {
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get()
&& !VideoInformation.isAtEndOfVideo();
}
}

View File

@@ -1,111 +0,0 @@
package app.revanced.extension.youtube.sponsorblock.ui;
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.view.View;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
import java.util.Objects;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
// Edit: This should be a subclass of PlayerControlButton
public class CreateSegmentButtonController {
private static WeakReference<ImageView> buttonReference = new WeakReference<>(null);
private static boolean isShowing;
/**
* injection point
*/
public static void initialize(View youtubeControlsLayout) {
try {
Logger.printDebug(() -> "initializing new segment button");
ImageView imageView = Objects.requireNonNull(Utils.getChildViewByResourceName(
youtubeControlsLayout, "revanced_sb_create_segment_button"));
imageView.setVisibility(View.GONE);
imageView.setOnClickListener(v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility());
buttonReference = new WeakReference<>(imageView);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
}
/**
* injection point
*/
public static void changeVisibilityImmediate(boolean visible) {
if (visible) {
// Fix button flickering, by pushing this call to the back of
// the main thread and letting other layout code run first.
Utils.runOnMainThread(() -> setVisibility(true, false));
} else {
setVisibility(false, false);
}
}
/**
* injection point
*/
public static void changeVisibility(boolean visible, boolean animated) {
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
if (visible && !animated) return;
setVisibility(visible, animated);
}
private static void setVisibility(boolean visible, boolean animated) {
try {
if (isShowing == visible) return;
isShowing = visible;
ImageView iView = buttonReference.get();
if (iView == null) return;
if (visible) {
iView.clearAnimation();
if (!shouldBeShown()) {
return;
}
if (animated) {
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
}
iView.setVisibility(View.VISIBLE);
return;
}
if (iView.getVisibility() == View.VISIBLE) {
iView.clearAnimation();
if (animated) {
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
}
iView.setVisibility(View.GONE);
}
} catch (Exception ex) {
Logger.printException(() -> "changeVisibility failure", ex);
}
}
private static boolean shouldBeShown() {
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get()
&& !VideoInformation.isAtEndOfVideo();
}
public static void hide() {
if (!isShowing) {
return;
}
Utils.verifyOnMainThread();
View v = buttonReference.get();
if (v == null) {
return;
}
v.setVisibility(View.GONE);
isShowing = false;
}
}

View File

@@ -2,10 +2,11 @@ package app.revanced.extension.youtube.sponsorblock.ui;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageButton; import android.widget.ImageButton;
@@ -14,15 +15,15 @@ import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils; import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import static app.revanced.extension.shared.Utils.getResourceColor;
import static app.revanced.extension.shared.Utils.getResourceDimensionPixelSize; import static app.revanced.extension.shared.Utils.getResourceDimensionPixelSize;
import static app.revanced.extension.shared.Utils.getResourceIdentifier; import static app.revanced.extension.shared.Utils.getResourceIdentifier;
public final class NewSegmentLayout extends FrameLayout { public final class NewSegmentLayout extends FrameLayout {
private static final ColorStateList rippleColorStateList = new ColorStateList( private static final ColorStateList rippleColorStateList = new ColorStateList(
new int[][]{new int[]{android.R.attr.state_enabled}}, new int[][]{new int[]{android.R.attr.state_enabled}},
new int[]{0x33ffffff} // sets the ripple color to white new int[]{0x33ffffff} // Ripple effect color (semi-transparent white)
); );
private final int rippleEffectId;
final int defaultBottomMargin; final int defaultBottomMargin;
final int ctaBottomMargin; final int ctaBottomMargin;
@@ -47,10 +48,6 @@ public final class NewSegmentLayout extends FrameLayout {
getResourceIdentifier(context, "revanced_sb_new_segment", "layout"), this, true getResourceIdentifier(context, "revanced_sb_new_segment", "layout"), this, true
); );
TypedValue rippleEffect = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, rippleEffect, true);
rippleEffectId = rippleEffect.resourceId;
initializeButton( initializeButton(
context, context,
"revanced_sb_new_segment_rewind", "revanced_sb_new_segment_rewind",
@@ -120,6 +117,28 @@ public final class NewSegmentLayout extends FrameLayout {
}); });
} }
/**
* Update the layout of this UI control.
*/
public void updateLayout() {
final boolean squareLayout = Settings.SB_SQUARE_LAYOUT.get();
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) getLayoutParams();
final int margin = squareLayout
? 0
: SponsorBlockViewController.ROUNDED_LAYOUT_MARGIN;
params.setMarginStart(margin);
setLayoutParams(params);
GradientDrawable backgroundDrawable = new GradientDrawable();
backgroundDrawable.setColor(getResourceColor("skip_ad_button_background_color"));
final float cornerRadius = squareLayout
? 0
: 16 * getResources().getDisplayMetrics().density;
backgroundDrawable.setCornerRadius(cornerRadius);
setBackground(backgroundDrawable);
}
@FunctionalInterface @FunctionalInterface
private interface ButtonOnClickHandlerFunction { private interface ButtonOnClickHandlerFunction {
void apply(); void apply();

View File

@@ -8,6 +8,7 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -19,11 +20,19 @@ import androidx.annotation.NonNull;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController; import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment; import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
public class SkipSponsorButton extends FrameLayout { public class SkipSponsorButton extends FrameLayout {
private static final boolean highContrast = true; /**
* Adds a high contrast border around the skip button.
*
* This feature is not currently used.
* If this is added, it needs an additional button width change because
* as-is the skip button text is clipped when this is on.
*/
private static final boolean highContrast = false;
private final LinearLayout skipSponsorBtnContainer; private final LinearLayout skipSponsorBtnContainer;
private final TextView skipSponsorTextView; private final TextView skipSponsorTextView;
private final Paint background; private final Paint background;
@@ -49,18 +58,23 @@ public class SkipSponsorButton extends FrameLayout {
LayoutInflater.from(context).inflate(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button", "layout"), this, true); // layout:skip_ad_button LayoutInflater.from(context).inflate(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button", "layout"), this, true); // layout:skip_ad_button
setMinimumHeight(getResourceDimensionPixelSize("ad_skip_ad_button_min_height")); // dimen:ad_skip_ad_button_min_height setMinimumHeight(getResourceDimensionPixelSize("ad_skip_ad_button_min_height")); // dimen:ad_skip_ad_button_min_height
skipSponsorBtnContainer = Objects.requireNonNull((LinearLayout) findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_container", "id"))); // id:skip_ad_button_container skipSponsorBtnContainer = Objects.requireNonNull(findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_container", "id"))); // id:skip_ad_button_container
background = new Paint(); background = new Paint();
background.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color); background.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color);
background.setStyle(Paint.Style.FILL); background.setStyle(Paint.Style.FILL);
border = new Paint(); border = new Paint();
border.setColor(getResourceColor("skip_ad_button_border_color")); // color:skip_ad_button_border_color); border.setColor(getResourceColor("skip_ad_button_border_color")); // color:skip_ad_button_border_color);
border.setStrokeWidth(getResourceDimension("ad_skip_ad_button_border_width")); // dimen:ad_skip_ad_button_border_width); border.setStrokeWidth(getResourceDimension("ad_skip_ad_button_border_width")); // dimen:ad_skip_ad_button_border_width);
border.setStyle(Paint.Style.STROKE); border.setStyle(Paint.Style.STROKE);
skipSponsorTextView = Objects.requireNonNull((TextView) findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_text", "id"))); // id:skip_ad_button_text;
skipSponsorTextView = Objects.requireNonNull(findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_text", "id"))); // id:skip_ad_button_text;
defaultBottomMargin = getResourceDimensionPixelSize("skip_button_default_bottom_margin"); // dimen:skip_button_default_bottom_margin defaultBottomMargin = getResourceDimensionPixelSize("skip_button_default_bottom_margin"); // dimen:skip_button_default_bottom_margin
ctaBottomMargin = getResourceDimensionPixelSize("skip_button_cta_bottom_margin"); // dimen:skip_button_cta_bottom_margin ctaBottomMargin = getResourceDimensionPixelSize("skip_button_cta_bottom_margin"); // dimen:skip_button_cta_bottom_margin
updateLayout();
skipSponsorBtnContainer.setOnClickListener(v -> { skipSponsorBtnContainer.setOnClickListener(v -> {
// The view controller handles hiding this button, but hide it here as well just in case something goofs. // The view controller handles hiding this button, but hide it here as well just in case something goofs.
setVisibility(View.GONE); setVisibility(View.GONE);
@@ -72,30 +86,56 @@ public class SkipSponsorButton extends FrameLayout {
protected final void dispatchDraw(Canvas canvas) { protected final void dispatchDraw(Canvas canvas) {
final int left = skipSponsorBtnContainer.getLeft(); final int left = skipSponsorBtnContainer.getLeft();
final int top = skipSponsorBtnContainer.getTop(); final int top = skipSponsorBtnContainer.getTop();
final int leftPlusWidth = (left + skipSponsorBtnContainer.getWidth()); final int right = left + skipSponsorBtnContainer.getWidth();
final int topPlusHeight = (top + skipSponsorBtnContainer.getHeight()); final int bottom = top + skipSponsorBtnContainer.getHeight();
canvas.drawRect(left, top, leftPlusWidth, topPlusHeight, background);
if (!highContrast) { // Determine corner radius for rounded button
canvas.drawLines(new float[]{ float cornerRadius = skipSponsorBtnContainer.getHeight() / 2f;
leftPlusWidth, top, left, top,
left, top, left, topPlusHeight, if (Settings.SB_SQUARE_LAYOUT.get()) {
left, topPlusHeight, leftPlusWidth, topPlusHeight}, // Square button.
border); canvas.drawRect(left, top, right, bottom, background);
if (highContrast) {
canvas.drawLines(new float[]{
right, top, left, top,
left, top, left, bottom,
left, bottom, right, bottom},
border); // Draw square border.
}
} else {
// Rounded button.
RectF rect = new RectF(left, top, right, bottom);
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, background); // Draw rounded background.
if (highContrast) {
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, border); // Draw rounded border.
}
} }
super.dispatchDraw(canvas); super.dispatchDraw(canvas);
} }
/** /**
* @return true, if this button state was changed * Update the layout of this button.
*/ */
public boolean updateSkipButtonText(@NonNull SponsorSegment segment) { public void updateLayout() {
if (Settings.SB_SQUARE_LAYOUT.get()) {
// No padding for square corners.
setPadding(0, 0, 0, 0);
} else {
// Apply padding for rounded corners.
final int padding = SponsorBlockViewController.ROUNDED_LAYOUT_MARGIN;
setPadding(padding, 0, padding, 0);
}
}
public void updateSkipButtonText(@NonNull SponsorSegment segment) {
this.segment = segment; this.segment = segment;
CharSequence newText = segment.getSkipButtonText(); CharSequence newText = segment.getSkipButtonText();
//noinspection StringEqualsCharSequence
if (newText.equals(skipSponsorTextView.getText())) { if (newText.equals(skipSponsorTextView.getText())) {
return false; return;
} }
skipSponsorTextView.setText(newText); skipSponsorTextView.setText(newText);
return true;
} }
} }

View File

@@ -19,8 +19,11 @@ import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment; import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
import kotlin.Unit;
public class SponsorBlockViewController { public class SponsorBlockViewController {
public static final int ROUNDED_LAYOUT_MARGIN = 12;
private static WeakReference<RelativeLayout> inlineSponsorOverlayRef = new WeakReference<>(null); private static WeakReference<RelativeLayout> inlineSponsorOverlayRef = new WeakReference<>(null);
private static WeakReference<ViewGroup> youtubeOverlaysLayoutRef = new WeakReference<>(null); private static WeakReference<ViewGroup> youtubeOverlaysLayoutRef = new WeakReference<>(null);
private static WeakReference<SkipSponsorButton> skipHighlightButtonRef = new WeakReference<>(null); private static WeakReference<SkipSponsorButton> skipHighlightButtonRef = new WeakReference<>(null);
@@ -36,7 +39,7 @@ public class SponsorBlockViewController {
static { static {
PlayerType.getOnChange().addObserver((PlayerType type) -> { PlayerType.getOnChange().addObserver((PlayerType type) -> {
playerTypeChanged(type); playerTypeChanged(type);
return null; return Unit.INSTANCE;
}); });
} }
@@ -80,12 +83,16 @@ public class SponsorBlockViewController {
}); });
youtubeOverlaysLayoutRef = new WeakReference<>(viewGroup); youtubeOverlaysLayoutRef = new WeakReference<>(viewGroup);
skipHighlightButtonRef = new WeakReference<>( skipHighlightButtonRef = new WeakReference<>(Objects.requireNonNull(
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_skip_highlight_button", "id")))); layout.findViewById(getResourceIdentifier("revanced_sb_skip_highlight_button", "id"))));
skipSponsorButtonRef = new WeakReference<>(
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_skip_sponsor_button", "id")))); skipSponsorButtonRef = new WeakReference<>(Objects.requireNonNull(
newSegmentLayoutRef = new WeakReference<>( layout.findViewById(getResourceIdentifier("revanced_sb_skip_sponsor_button", "id"))));
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_new_segment_view", "id"))));
NewSegmentLayout newSegmentLayout = Objects.requireNonNull(
layout.findViewById(getResourceIdentifier("revanced_sb_new_segment_view", "id")));
newSegmentLayoutRef = new WeakReference<>(newSegmentLayout);
newSegmentLayout.updateLayout();
newSegmentLayoutVisible = false; newSegmentLayoutVisible = false;
skipHighlight = null; skipHighlight = null;
@@ -101,6 +108,23 @@ public class SponsorBlockViewController {
hideNewSegmentLayout(); hideNewSegmentLayout();
} }
public static void updateLayout() {
SkipSponsorButton button = skipSponsorButtonRef.get();
if (button != null) {
button.updateLayout();
}
button = skipHighlightButtonRef.get();
if (button != null) {
button.updateLayout();
}
NewSegmentLayout newSegmentLayout = newSegmentLayoutRef.get();
if (newSegmentLayout != null) {
newSegmentLayout.updateLayout();
}
}
public static void showSkipHighlightButton(@NonNull SponsorSegment segment) { public static void showSkipHighlightButton(@NonNull SponsorSegment segment) {
skipHighlight = Objects.requireNonNull(segment); skipHighlight = Objects.requireNonNull(segment);
NewSegmentLayout newSegmentLayout = newSegmentLayoutRef.get(); NewSegmentLayout newSegmentLayout = newSegmentLayoutRef.get();
@@ -214,8 +238,8 @@ public class SponsorBlockViewController {
// but if buttons are showing when the end of the video is reached then they need // but if buttons are showing when the end of the video is reached then they need
// to be forcefully hidden // to be forcefully hidden
if (!Settings.AUTO_REPEAT.get()) { if (!Settings.AUTO_REPEAT.get()) {
CreateSegmentButtonController.hide(); CreateSegmentButton.hideControls();
VotingButtonController.hide(); VotingButton.hideControls();
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "endOfVideoReached failure", ex); Logger.printException(() -> "endOfVideoReached failure", ex);

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