Compare commits

...

263 Commits

Author SHA1 Message Date
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
semantic-release-bot
80d7c78cf6 chore: Release v5.9.0-dev.4 [skip ci]
# [5.9.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.3...v5.9.0-dev.4) (2025-01-20)

### Bug Fixes

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

### Features

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

### Features

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

### Features

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Features

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

### Features

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

### Bug Fixes

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

### Bug Fixes

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

### Features

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

### Bug Fixes

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

### Features

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

### Bug Fixes

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

### Features

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

### Features

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Features

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

### Features

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Features

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Features

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Bug Fixes

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

### Features

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

### Features

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

### Bug Fixes

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

### Features

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

### Bug Fixes

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

### Features

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

### Bug Fixes

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

### Bug Fixes

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

### Features

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

### Features

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

### Bug Fixes

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

### Features

* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([8d06a4a](8d06a4a8ad))
2024-12-12 18:42:30 +00:00
ILoveOpenSourceApplications
8d06a4a8ad feat(YouTube - Comments): Add Hide 'Chat summary' (#4110) 2024-12-12 22:39:08 +04:00
github-actions[bot]
d7ca7c1733 chore: Sync translations (#4105) 2024-12-12 19:01:48 +04:00
LisoUseInAIKyrios
8e0b7db82a refactor: Use raw string resources (#4109) 2024-12-12 17:01:16 +04:00
355 changed files with 36869 additions and 16580 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

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 {
compileOnly(libs.annotation)

View File

@@ -12,7 +12,7 @@ import android.os.Handler;
import androidx.annotation.RequiresApi;
/** @noinspection deprecation, unused */
@SuppressWarnings({"deprecation", "unused"})
public class SpoofWifiPatch {
// 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 {
compileOnly(libs.annotation)
}

View File

@@ -1,5 +1,6 @@
package app.revanced.extension.all.misc.directory.documentsprovider;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.ApplicationInfo;
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.
*/
@SuppressLint("LongLogTag")
public class InternalDataDocumentsProvider extends DocumentsProvider {
private static final String[] rootColumns =
{"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 {
compileOnly(libs.annotation)

View File

@@ -5,7 +5,8 @@ import android.os.Build;
import androidx.annotation.RequiresApi;
public final class RemoveScreencaptureRestrictionPatch {
@SuppressWarnings("unused")
public final class RemoveScreenCaptureRestrictionPatch {
// Member of AudioAttributes.Builder
@RequiresApi(api = Build.VERSION_CODES.Q)
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.WindowManager;
@SuppressWarnings("unused")
public class RemoveScreenshotRestrictionPatch {
public static void addFlags(Window window, int flags) {

View File

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

View File

@@ -0,0 +1,5 @@
android {
defaultConfig {
minSdk = 26
}
}

View File

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

View File

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

View File

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

View File

@@ -106,7 +106,11 @@ public class GmsCoreSupport {
}
// Check if GmsCore is whitelisted from battery optimizations.
if (batteryOptimizationsEnabled(context)) {
if (isAndroidAutomotive(context)) {
// Ignore Android Automotive devices (Google built-in),
// as there is no way to disable battery optimizations.
Logger.printDebug(() -> "Device is Android Automotive");
} else if (batteryOptimizationsEnabled(context)) {
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
showBatteryOptimizationDialog(context,
@@ -147,6 +151,10 @@ public class GmsCoreSupport {
return !powerManager.isIgnoringBatteryOptimizations(GMS_CORE_PACKAGE_NAME);
}
private static boolean isAndroidAutomotive(Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
private static String getGmsCoreDownload() {
final var vendorGroupId = getGmsCoreVendorGroupId();
//noinspection SwitchStatementWithTooFewBranches

View File

@@ -40,13 +40,15 @@ import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
public class Utils {
@SuppressLint("StaticFieldLeak")
private static Context context;
private static volatile Context context;
private static String versionName;
private static String applicationLabel;
@@ -360,7 +362,17 @@ public class Utils {
}
public static void setContext(Context appContext) {
// Must initially set context as the language settings needs it.
context = appContext;
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language != AppLanguage.DEFAULT) {
// Create a new context with the desired language.
Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale());
context = appContext.createConfigurationContext(config);
}
// In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies.
// Calling the regular printDebug method here can cause a Settings context null pointer exception,
// even though the context is already set before the call.
@@ -523,6 +535,11 @@ public class Utils {
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
}
public static boolean isLandscapeOrientation() {
final int orientation = context.getResources().getConfiguration().orientation;
return orientation == Configuration.ORIENTATION_LANDSCAPE;
}
/**
* Automatically logs any exceptions the runnable throws.
*
@@ -595,7 +612,7 @@ public class Utils {
|| networkType == NetworkType.OTHER;
}
@SuppressLint("MissingPermission") // permission already included in YouTube
@SuppressLint({"MissingPermission", "deprecation"}) // Permission already included in YouTube.
public static NetworkType getNetworkType() {
Context networkContext = getContext();
if (networkContext == null) {
@@ -705,8 +722,8 @@ public class Utils {
Preference preference = group.getPreference(i);
final Sort preferenceSort;
if (preference instanceof PreferenceGroup) {
sortPreferenceGroups((PreferenceGroup) preference);
if (preference instanceof PreferenceGroup subGroup) {
sortPreferenceGroups(subGroup);
preferenceSort = groupSort; // Sort value for groups is for it's content, not itself.
} else {
// Allow individual preferences to set a key sorting.
@@ -760,8 +777,8 @@ public class Utils {
return;
}
String deviceLanguage = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
if (deviceLanguage.equals("en")) {
String revancedLocale = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
if (revancedLocale.equals(Locale.ENGLISH.getLanguage())) {
return;
}
@@ -769,8 +786,8 @@ public class Utils {
Preference pref = group.getPreference(i);
pref.setSingleLineTitle(false);
if (pref instanceof PreferenceGroup) {
setPreferenceTitlesToMultiLineIfNeeded((PreferenceGroup) pref);
if (pref instanceof PreferenceGroup subGroup) {
setPreferenceTitlesToMultiLineIfNeeded(subGroup);
}
}
}

View File

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

View File

@@ -3,9 +3,9 @@ package app.revanced.extension.shared.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.ForceiOSAVCAvailability;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
import app.revanced.extension.shared.spoof.ClientType;
/**
@@ -21,10 +21,14 @@ public class BaseSettings {
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
public static final EnumSetting<AppLanguage> REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true, "revanced_language_user_dialog_message");
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, parent(SPOOF_VIDEO_STREAMS));
public static final EnumSetting<AppLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new ForceiOSAVCAvailability());
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
// Client type must be last spoof setting due to cyclic references.
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_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) {
setting.value = Objects.requireNonNull(newValue);
if (setting.isSetToDefault()) {
setting.removeFromPreferences();
}
}
@Override
@@ -65,10 +69,8 @@ public class BooleanSetting extends Setting<Boolean> {
}
@Override
public void save(@NonNull Boolean newValue) {
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
value = Objects.requireNonNull(newValue);
preferences.saveBoolean(key, newValue);
public void saveToPreferences() {
preferences.saveBoolean(key, value);
}
@NonNull

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,6 @@ import java.util.*;
import static app.revanced.extension.shared.StringRef.str;
@SuppressWarnings("unused")
public abstract class Setting<T> {
/**
@@ -153,7 +152,6 @@ public abstract class Setting<T> {
/**
* Confirmation message to display, if the user tries to change the setting from the default value.
* Currently this works only for Boolean setting types.
*/
@Nullable
public final StringRef userDialogMessage;
@@ -244,6 +242,7 @@ public abstract class Setting<T> {
*
* This method will be deleted in the future.
*/
@SuppressWarnings("rawtypes")
public static void migrateFromOldPreferences(@NonNull SharedPrefCategory oldPrefs, @NonNull Setting setting, String settingKey) {
if (!oldPrefs.preferences.contains(settingKey)) {
return; // Nothing to do.
@@ -288,6 +287,13 @@ public abstract class Setting<T> {
*/
public static void privateSetValueFromString(@NonNull Setting<?> setting, @NonNull String 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.
*/
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
public abstract T get();
@@ -419,6 +451,7 @@ public abstract class Setting<T> {
boolean rebootSettingChanged = false;
int numberOfSettingsImported = 0;
//noinspection rawtypes
for (Setting setting : SETTINGS) {
String key = setting.getImportExportKey();
if (json.has(key)) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,3 +3,14 @@ dependencies {
compileOnly(project(":extensions:tiktok:stub"))
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,
@NonNull Setting<?> setting,
boolean applySettingToPreference) {
if (pref instanceof RangeValuePreference rangeValuePref) {
if (pref instanceof RangeValuePreference) {
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
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());
} else {
super.syncSettingWithPreference(pref, setting, applySettingToPreference);
@@ -32,7 +34,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
@Override
protected void initialize() {
final var context = getContext();
final var context = getActivity();
// Currently no resources can be compiled for TikTok (fails with aapt error).
// So all TikTok Strings are hard coded in the extension.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,6 @@ import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeD
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
@@ -366,9 +365,7 @@ public class ReturnYouTubeDislikePatch {
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);
}
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
}
/**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,8 +6,12 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
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 ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
private final StringFilterGroup likeSubscribeGlow;
private final StringFilterGroup actionBarGroup;
private final StringFilterGroup bufferFilterPathGroup;
private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList();
@@ -20,11 +24,19 @@ final class ButtonsFilter extends Filter {
addIdentifierCallbacks(actionBarGroup);
likeSubscribeGlow = new StringFilterGroup(
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
"animated_button_border.eml"
);
bufferFilterPathGroup = new StringFilterGroup(
null,
"|ContainerType|button.eml|"
);
addPathCallbacks(
likeSubscribeGlow,
bufferFilterPathGroup,
new StringFilterGroup(
Settings.HIDE_LIKE_DISLIKE_BUTTON,
"|segmented_like_dislike_button"
@@ -40,8 +52,7 @@ final class ButtonsFilter extends Filter {
new StringFilterGroup(
Settings.HIDE_CLIP_BUTTON,
"|clip_button.eml|"
),
bufferFilterPathGroup
)
);
bufferButtonsGroupList.addAll(
@@ -83,6 +94,15 @@ final class ButtonsFilter extends Filter {
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
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,
// in case every filter group is enabled, hide the action bar.
if (matchedGroup == actionBarGroup) {

View File

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

View File

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

View File

@@ -1,9 +1,6 @@
package app.revanced.extension.youtube.patches.components;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.util.*;
import java.util.function.Consumer;
@@ -44,13 +41,11 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
return filterGroups.iterator();
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void forEach(@NonNull Consumer<? super T> action) {
filterGroups.forEach(action);
}
@RequiresApi(api = Build.VERSION_CODES.N)
@NonNull
@Override
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 java.lang.Character.UnicodeBlock.*;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.nio.charset.StandardCharsets;
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.
*/
@SuppressWarnings("unused")
@RequiresApi(api = Build.VERSION_CODES.N)
final class KeywordContentFilter extends Filter {
/**
@@ -528,14 +524,13 @@ final class KeywordContentFilter extends Filter {
if (selectedNavButton == null) {
return hideHome; // Unknown tab, treat the same as home.
}
if (selectedNavButton == NavigationButton.HOME) {
return hideHome;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS) {
return hideSubscriptions;
}
// User is in the Library or Notifications tab.
return false;
return switch (selectedNavButton) {
case HOME, EXPLORE -> hideHome;
case SUBSCRIPTIONS -> hideSubscriptions;
// User is in the Library or notifications.
default -> false;
};
}
private void updateStats(boolean videoWasHidden, @Nullable String keyword) {

View File

@@ -3,11 +3,9 @@ package app.revanced.extension.youtube.patches.components;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
@@ -18,10 +16,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
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(
"V.ED", // Playlist browse id.
"java.lang.ref.WeakReference"
@@ -36,19 +30,17 @@ public final class LayoutComponentsFilter extends Filter {
);
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup searchResultShelfHeader;
private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup notifyMe;
private final StringFilterGroup singleItemInformationPanel;
private final StringFilterGroup expandableMetadata;
private final ByteArrayFilterGroup searchResultRecommendations;
private final StringFilterGroup searchResultVideo;
private final StringFilterGroup compactChannelBarInner;
private final StringFilterGroup compactChannelBarInnerButton;
private final ByteArrayFilterGroup joinMembershipButton;
private final StringFilterGroup likeSubscribeGlow;
private final StringFilterGroup horizontalShelves;
@RequiresApi(api = Build.VERSION_CODES.N)
public LayoutComponentsFilter() {
exceptions.addPatterns(
"home_video_with_context",
@@ -74,13 +66,16 @@ public final class LayoutComponentsFilter extends Filter {
final var communityPosts = new StringFilterGroup(
Settings.HIDE_COMMUNITY_POSTS,
"post_base_wrapper",
"post_base_wrapper", // may be obsolete and no longer needed.
"text_post_root.eml",
"images_post_root.eml",
"images_post_slim.eml",
"images_post_slim.eml", // may be obsolete and no longer needed.
"images_post_root_slim.eml",
"text_post_root_slim.eml",
"post_base_wrapper_slim.eml"
"post_base_wrapper_slim.eml",
"poll_post_root.eml",
"videos_post_root.eml",
"post_shelf_slim.eml"
);
final var communityGuidelines = new StringFilterGroup(
@@ -106,7 +101,8 @@ public final class LayoutComponentsFilter extends Filter {
inFeedSurvey = new StringFilterGroup(
Settings.HIDE_FEED_SURVEY,
"in_feed_survey",
"slimline_survey"
"slimline_survey",
"feed_nudge"
);
final var medicalPanel = new StringFilterGroup(
@@ -120,8 +116,12 @@ public final class LayoutComponentsFilter extends Filter {
);
final var infoPanel = new StringFilterGroup(
Settings.HIDE_HIDE_INFO_PANELS,
"publisher_transparency_panel",
Settings.HIDE_INFO_PANELS,
"publisher_transparency_panel"
);
singleItemInformationPanel = new StringFilterGroup(
Settings.HIDE_INFO_PANELS,
"single_item_information_panel"
);
@@ -161,9 +161,9 @@ public final class LayoutComponentsFilter extends Filter {
"inline_expander"
);
final var channelBar = new StringFilterGroup(
final var compactChannelBar = new StringFilterGroup(
Settings.HIDE_CHANNEL_BAR,
"channel_bar"
"compact_channel_bar"
);
final var relatedVideos = new StringFilterGroup(
@@ -194,11 +194,6 @@ public final class LayoutComponentsFilter extends Filter {
"timed_reaction"
);
searchResultShelfHeader = new StringFilterGroup(
Settings.HIDE_SEARCH_RESULT_SHELF_HEADER,
"shelf_header.eml"
);
notifyMe = new StringFilterGroup(
Settings.HIDE_NOTIFY_ME_BUTTON,
"set_reminder_button"
@@ -219,10 +214,6 @@ public final class LayoutComponentsFilter extends Filter {
"sponsorships"
);
likeSubscribeGlow = new StringFilterGroup(
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
"animated_button_border.eml"
);
final var channelWatermark = new StringFilterGroup(
Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
@@ -256,8 +247,7 @@ public final class LayoutComponentsFilter extends Filter {
expandableMetadata,
inFeedSurvey,
notifyMe,
likeSubscribeGlow,
channelBar,
compactChannelBar,
communityPosts,
paidPromotion,
searchResultVideo,
@@ -271,6 +261,7 @@ public final class LayoutComponentsFilter extends Filter {
compactChannelBarInner,
medicalPanel,
infoPanel,
singleItemInformationPanel,
emergencyBox,
subscribersCommunityGuidelines,
channelGuidelines,
@@ -287,6 +278,19 @@ public final class LayoutComponentsFilter extends Filter {
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
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 (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
@@ -294,15 +298,6 @@ public final class LayoutComponentsFilter extends Filter {
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.
// Filter them separately here.
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
@@ -324,9 +319,6 @@ public final class LayoutComponentsFilter extends Filter {
return false;
}
// TODO: This also hides the feed Shorts shelf header
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
if (matchedGroup == horizontalShelves) {
if (contentIndex == 0 && hideShelves()) {
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);

View File

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

View File

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

View File

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

View File

@@ -6,11 +6,19 @@ import android.content.res.Resources;
import android.graphics.Color;
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.Locale;
import java.util.Scanner;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.settings.Settings;
@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 HIDE_SEEKBAR_THUMBNAIL_ENABLED = Settings.HIDE_SEEKBAR_THUMBNAIL.get();
/**
* Default color of the litho seekbar.
* Differs slightly from the default custom seekbar color setting.
@@ -25,14 +35,19 @@ public final class SeekbarColorPatch {
private static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000;
/**
* Default colors of the gradient seekbar.
* Feed default colors of the gradient seekbar.
*/
private static final int[] ORIGINAL_SEEKBAR_GRADIENT_COLORS = { 0xFFFF0033, 0xFFFF2791 };
private static final int[] FEED_ORIGINAL_SEEKBAR_GRADIENT_COLORS = { 0xFFFF0033, 0xFFFF2791 };
/**
* Default positions of the gradient seekbar.
* Feed default positions of the gradient seekbar.
*/
private static final float[] ORIGINAL_SEEKBAR_GRADIENT_POSITIONS = { 0.8f, 1.0f };
private static final float[] FEED_ORIGINAL_SEEKBAR_GRADIENT_POSITIONS = { 0.8f, 1.0f };
/**
* Empty seekbar gradient, if hide seekbar in feed is enabled.
*/
private static final int[] HIDDEN_SEEKBAR_GRADIENT_COLORS = { 0x0, 0x0 };
/**
* Default YouTube seekbar color brightness.
@@ -41,16 +56,21 @@ public final class SeekbarColorPatch {
/**
* If {@link Settings#SEEKBAR_CUSTOM_COLOR} is enabled,
* this is the color value of {@link Settings#SEEKBAR_CUSTOM_COLOR_VALUE}.
* this is the color value of {@link Settings#SEEKBAR_CUSTOM_COLOR_PRIMARY}.
* Otherwise this is {@link #ORIGINAL_SEEKBAR_COLOR}.
*/
private static int seekbarColor = ORIGINAL_SEEKBAR_COLOR;
private static int customSeekbarColor = ORIGINAL_SEEKBAR_COLOR;
/**
* Custom seekbar hue, saturation, and brightness values.
*/
private static final float[] customSeekbarColorHSV = new float[3];
/**
* Custom seekbar color, used for linear gradient replacements.
*/
private static final int[] customSeekbarColorGradient = new int[2];
static {
float[] hsv = new float[3];
Color.colorToHSV(ORIGINAL_SEEKBAR_COLOR, hsv);
@@ -63,37 +83,22 @@ public final class SeekbarColorPatch {
private static void loadCustomSeekbarColor() {
try {
seekbarColor = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_VALUE.get());
Color.colorToHSV(seekbarColor, customSeekbarColorHSV);
customSeekbarColor = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_PRIMARY.get());
Color.colorToHSV(customSeekbarColor, customSeekbarColorHSV);
customSeekbarColorGradient[0] = customSeekbarColor;
customSeekbarColorGradient[1] = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_ACCENT.get());
} catch (Exception ex) {
Utils.showToastShort(str("revanced_seekbar_custom_color_invalid"));
Settings.SEEKBAR_CUSTOM_COLOR_VALUE.resetToDefault();
Settings.SEEKBAR_CUSTOM_COLOR_PRIMARY.resetToDefault();
Settings.SEEKBAR_CUSTOM_COLOR_ACCENT.resetToDefault();
loadCustomSeekbarColor();
}
}
public static int getSeekbarColor() {
return seekbarColor;
}
/**
* Injection point
*/
public static boolean playerSeekbarGradientEnabled(boolean original) {
if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false;
return original;
}
/**
* Injection point
*/
public static boolean useLotteLaunchSplashScreen(boolean original) {
Logger.printDebug(() -> "useLotteLaunchSplashScreen original: " + original);
if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false;
return original;
return customSeekbarColor;
}
private static int colorChannelTo3Bits(int channel8Bits) {
@@ -119,6 +124,17 @@ public final class SeekbarColorPatch {
/**
* 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) {
// 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
@@ -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),
// a color filter still cannot be selectively applied when the drawable has more than 1 color.
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);
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.
*
@@ -156,35 +252,61 @@ public final class SeekbarColorPatch {
*/
public static int getLithoColor(int colorValue) {
if (colorValue == ORIGINAL_SEEKBAR_COLOR) {
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) {
return 0x00000000;
if (HIDE_SEEKBAR_THUMBNAIL_ENABLED) {
return 0x0;
}
return getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR);
return customSeekbarColor;
}
return colorValue;
}
private static String colorArrayToHex(int[] colors) {
final int length = colors.length;
StringBuilder builder = new StringBuilder(length * 12);
builder.append("[");
int i = 0;
for (int color : colors) {
builder.append(String.format("#%X", color));
if (++i < length) {
builder.append(", ");
}
}
builder.append("]");
return builder.toString();
}
/**
* Injection point.
*/
public static void setLinearGradient(int[] colors, float[] positions) {
final boolean hideSeekbar = Settings.HIDE_SEEKBAR_THUMBNAIL.get();
public static int[] getPlayerLinearGradient(int[] original) {
return SEEKBAR_CUSTOM_COLOR_ENABLED
? customSeekbarColorGradient
: original;
}
if (SEEKBAR_CUSTOM_COLOR_ENABLED || hideSeekbar) {
/**
* Injection point.
*/
public static int[] getLithoLinearGradient(int[] colors, float[] positions) {
if (SEEKBAR_CUSTOM_COLOR_ENABLED || HIDE_SEEKBAR_THUMBNAIL_ENABLED) {
// Most litho usage of linear gradients is hooked here,
// so must only change if the values are those for the seekbar.
if (Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors)
&& Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions)) {
Arrays.fill(colors, hideSeekbar
? 0x00000000
: seekbarColor);
return;
if ((Arrays.equals(FEED_ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors)
&& Arrays.equals(FEED_ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions))) {
return HIDE_SEEKBAR_THUMBNAIL_ENABLED
? HIDDEN_SEEKBAR_GRADIENT_COLORS
: customSeekbarColorGradient;
}
Logger.printDebug(() -> "Ignoring gradient colors: " + Arrays.toString(colors)
Logger.printDebug(() -> "Ignoring gradient colors: " + colorArrayToHex(colors)
+ " positions: " + Arrays.toString(positions));
}
return colors;
}
/**
@@ -198,7 +320,7 @@ public final class SeekbarColorPatch {
}
return colorValue == ORIGINAL_SEEKBAR_COLOR
? getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR)
? customSeekbarColor
: colorValue;
}
@@ -208,11 +330,9 @@ public final class SeekbarColorPatch {
* Overrides color used for the video player seekbar.
*/
public static int getVideoPlayerSeekbarColor(int originalColor) {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED) {
return originalColor;
}
return getSeekbarColorValue(originalColor);
return SEEKBAR_CUSTOM_COLOR_ENABLED
? getSeekbarColorValue(originalColor)
: originalColor;
}
/**
@@ -221,10 +341,6 @@ public final class SeekbarColorPatch {
*/
private static int getSeekbarColorValue(int originalColor) {
try {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED || originalColor == seekbarColor) {
return originalColor; // nothing to do
}
final int alphaDifference = Color.alpha(originalColor) - Color.alpha(ORIGINAL_SEEKBAR_COLOR);
// The seekbar uses the same color but different brightness for different situations.
@@ -237,7 +353,7 @@ public final class SeekbarColorPatch {
hsv[1] = customSeekbarColorHSV[1];
hsv[2] = clamp(customSeekbarColorHSV[2] + brightnessDifference, 0, 1);
final int replacementAlpha = clamp(Color.alpha(seekbarColor) + alphaDifference, 0, 255);
final int replacementAlpha = clamp(Color.alpha(customSeekbarColor) + alphaDifference, 0, 255);
final int replacementColor = Color.HSVToColor(replacementAlpha, hsv);
Logger.printDebug(() -> String.format("Original color: #%08X replacement color: #%08X",
originalColor, replacementColor));

View File

@@ -234,6 +234,12 @@ public class ReturnYouTubeDislike {
// example video: https://www.youtube.com/watch?v=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");
oldLikes = formatDislikeCount(voteData.getLikeCount());
}
@@ -346,56 +352,49 @@ public class ReturnYouTubeDislike {
}
private static String formatDislikeCount(long dislikeCount) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
if (dislikeCountFormatter == null) {
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
if (dislikeCountFormatter == null) {
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
// YouTube disregards locale specific number characters
// and instead shows english number characters everywhere.
// 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 "۱,۲۳٤"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
dislikeCountFormatter.setDecimalFormatSymbols(symbols);
}
// YouTube disregards locale specific number characters
// and instead shows english number characters everywhere.
// 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 "۱,۲۳٤"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
dislikeCountFormatter.setDecimalFormatSymbols(symbols);
}
return dislikeCountFormatter.format(dislikeCount);
}
}
// Will never be reached, as the oldest supported YouTube app requires Android N or greater.
return String.valueOf(dislikeCount);
return dislikeCountFormatter.format(dislikeCount);
}
}
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
if (dislikePercentageFormatter == null) {
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe, must synchronize.
if (dislikePercentageFormatter == null) {
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
// 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
&& dislikePercentageFormatter instanceof DecimalFormat) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
((DecimalFormat) dislikePercentageFormatter).setDecimalFormatSymbols(symbols);
}
// 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
&& dislikePercentageFormatter instanceof DecimalFormat) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
((DecimalFormat) dislikePercentageFormatter).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.
return String.valueOf((int) (dislikePercentage * 100));
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);
}
}
@NonNull
@@ -403,15 +402,13 @@ public class ReturnYouTubeDislike {
Objects.requireNonNull(videoId);
synchronized (fetchCache) {
// Remove any expired entries.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
final long now = System.currentTimeMillis();
fetchCache.values().removeIf(value -> {
final boolean expired = value.isExpired(now);
if (expired)
Logger.printDebug(() -> "Removing expired fetch: " + value.videoId);
return expired;
});
}
final long now = System.currentTimeMillis();
fetchCache.values().removeIf(value -> {
final boolean expired = value.isExpired(now);
if (expired)
Logger.printDebug(() -> "Removing expired fetch: " + value.videoId);
return expired;
});
ReturnYouTubeDislike fetch = fetchCache.get(videoId);
if (fetch == null) {
@@ -551,6 +548,15 @@ public class ReturnYouTubeDislike {
}
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,
// so there is no need to cache the likes for this situations.
Logger.printDebug(() -> "Creating likes span for: " + votingData.videoId);

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
package app.revanced.extension.youtube.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import android.content.Context;
import android.preference.SwitchPreference;
import android.util.AttributeSet;
import app.revanced.extension.youtube.patches.ForceOriginalAudioPatch;
@SuppressWarnings({"deprecation", "unused"})
public class ForceOriginalAudioSwitchPreference extends SwitchPreference {
{
if (!ForceOriginalAudioPatch.PATCH_AVAILABLE) {
// Show why force audio is not available.
String summary = str("revanced_force_original_audio_not_available");
setSummary(summary);
setSummaryOn(summary);
setSummaryOff(summary);
}
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ForceOriginalAudioSwitchPreference(Context context) {
super(context);
}
}

View File

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

View File

@@ -17,17 +17,18 @@ import android.view.WindowInsets;
import android.widget.TextView;
import android.widget.Toolbar;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.EnumSetting;
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.LicenseActivityHook;
import app.revanced.extension.youtube.settings.Settings;
/**
@@ -48,35 +49,44 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
/**
* Sorts a preference list by menu entries, but preserves the first value as the first entry.
*
* @noinspection SameParameterValue
*/
private static void sortListPreferenceByValues(ListPreference listPreference) {
private static void sortListPreferenceByValues(ListPreference listPreference, int firstEntriesToPreserve) {
CharSequence[] entries = listPreference.getEntries();
CharSequence[] entryValues = listPreference.getEntryValues();
final int entrySize = entries.length;
if (entrySize != entryValues.length) {
// Xml array declaration has a missing/extra entry.
throw new IllegalStateException();
}
// Ensure the first entry remains the first after sorting.
CharSequence firstEntry = entries[0];
CharSequence firstEntryValue = entryValues[0];
List<Pair<String, String>> firstPairs = new ArrayList<>(firstEntriesToPreserve);
List<Pair<String, String>> pairsToSort = new ArrayList<>(entrySize);
List<Pair<String, String>> entryPairs = new ArrayList<>(entrySize);
for (int i = 1; i < entrySize; i++) {
entryPairs.add(new Pair<>(entries[i].toString(), entryValues[i].toString()));
for (int i = 0; i < entrySize; i++) {
Pair<String, String> pair = new Pair<>(entries[i].toString(), entryValues[i].toString());
if (i < firstEntriesToPreserve) {
firstPairs.add(pair);
} else {
pairsToSort.add(pair);
}
}
Collections.sort(entryPairs, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
Collections.sort(pairsToSort, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
CharSequence[] sortedEntries = new CharSequence[entrySize];
CharSequence[] sortedEntryValues = new CharSequence[entrySize];
sortedEntries[0] = firstEntry;
sortedEntryValues[0] = firstEntryValue;
int i = 0;
for (Pair<String, String> pair : firstPairs) {
sortedEntries[i] = pair.first;
sortedEntryValues[i] = pair.second;
i++;
}
int i = 1;
for (Pair<String, String> pair : entryPairs) {
for (Pair<String, String> pair : pairsToSort) {
sortedEntries[i] = pair.first;
sortedEntryValues[i] = pair.second;
i++;
@@ -86,7 +96,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
listPreference.setEntryValues(sortedEntryValues);
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
protected void initialize() {
super.initialize();
@@ -100,15 +109,20 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
}
preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference);
}
sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE);
sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
}
private void sortPreferenceListMenu(EnumSetting<?> setting) {
Preference preference = findPreference(setting.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference, 1);
}
}
private void setPreferenceScreenToolbar(PreferenceScreen parentScreen) {
for (int i = 0, preferenceCount = parentScreen.getPreferenceCount(); i < preferenceCount; i++) {
Preference childPreference = parentScreen.getPreference(i);
@@ -124,9 +138,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
.getParent();
// Fix required for Android 15 and YT 19.45+
// FIXME:
// On Android 15 the text layout is not aligned the same as the parent
// screen and it looks a little off. Otherwise this works.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
rootView.setOnApplyWindowInsetsListener((v, insets) -> {
Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars());
@@ -139,13 +150,10 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
toolbar.setTitle(childScreen.getTitle());
toolbar.setNavigationIcon(getBackButtonDrawable());
toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
final int margin = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
);
toolbar.setTitleMargin(margin, 0, margin, 0);
}
final int margin = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
);
toolbar.setTitleMargin(margin, 0, margin, 0);
TextView toolbarTextView = Utils.getChildView(toolbar,
true, TextView.class::isInstance);
@@ -153,6 +161,8 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
toolbarTextView.setTextColor(ThemeHelper.getForegroundColor());
}
LicenseActivityHook.setToolbarLayoutParams(toolbar);
rootView.addView(toolbar, 0);
return false;
}

View File

@@ -39,6 +39,11 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
*/
private SwitchPreference compactLayoutPreference;
/**
* If hidden likes are replaced with an estimated value.
*/
private SwitchPreference estimatedLikesPreference;
/**
* If segmented like/dislike button uses smaller compact layout.
*/
@@ -48,6 +53,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
shortsPreference.setEnabled(Settings.RYD_SHORTS.isAvailable());
percentagePreference.setEnabled(Settings.RYD_DISLIKE_PERCENTAGE.isAvailable());
compactLayoutPreference.setEnabled(Settings.RYD_COMPACT_LAYOUT.isAvailable());
estimatedLikesPreference.setEnabled(Settings.RYD_ESTIMATED_LIKE.isAvailable());
toastOnRYDNotAvailable.setEnabled(Settings.RYD_TOAST_ON_CONNECTION_ERROR.isAvailable());
}
@@ -117,6 +123,19 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
});
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.setChecked(Settings.RYD_TOAST_ON_CONNECTION_ERROR.get());
toastOnRYDNotAvailable.setTitle(str("revanced_ryd_toast_on_connection_error_title"));

View File

@@ -6,7 +6,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.*;
import android.text.Html;
@@ -37,8 +36,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
private SwitchPreference sbEnabled;
private SwitchPreference addNewSegment;
private SwitchPreference votingEnabled;
private SwitchPreference compactSkipButton;
private SwitchPreference autoHideSkipSegmentButton;
private SwitchPreference compactSkipButton;
private SwitchPreference squareLayout;
private SwitchPreference showSkipToast;
private SwitchPreference trackSkips;
private SwitchPreference showTimeWithoutSegments;
@@ -62,7 +62,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
} else if (!Settings.SB_CREATE_NEW_SEGMENT.get()) {
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);
@@ -72,11 +74,14 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
votingEnabled.setChecked(Settings.SB_VOTING_BUTTON.get());
votingEnabled.setEnabled(enabled);
autoHideSkipSegmentButton.setEnabled(enabled);
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get());
compactSkipButton.setChecked(Settings.SB_COMPACT_SKIP_BUTTON.get());
compactSkipButton.setEnabled(enabled);
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get());
autoHideSkipSegmentButton.setEnabled(enabled);
squareLayout.setChecked(Settings.SB_SQUARE_LAYOUT.get());
squareLayout.setEnabled(enabled);
showSkipToast.setChecked(Settings.SB_TOAST_ON_SKIP.get());
showSkipToast.setEnabled(enabled);
@@ -176,6 +181,17 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
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.setTitle(str("revanced_sb_enable_compact_skip_button"));
compactSkipButton.setSummaryOn(str("revanced_sb_enable_compact_skip_button_sum_on"));
@@ -187,13 +203,13 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
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);
squareLayout = new SwitchPreference(context);
squareLayout.setTitle(str("revanced_sb_square_layout"));
squareLayout.setSummaryOn(str("revanced_sb_square_layout_sum_on"));
squareLayout.setSummaryOff(str("revanced_sb_square_layout_sum_off"));
category.addPreference(squareLayout);
squareLayout.setOnPreferenceChangeListener((preference1, newValue) -> {
Settings.SB_SQUARE_LAYOUT.save((Boolean) newValue);
updateUI();
return true;
});
@@ -393,9 +409,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_MULTI_LINE
| 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.setOnPreferenceClickListener(preference1 -> {
importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings());
@@ -507,7 +521,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
Utils.showToastLong(str("revanced_sb_stats_username_changed"));
} else {
preference.setText(userName); // revert to previous
Utils.showToastLong(errorMessage);
SponsorBlockUtils.showErrorDialog(errorMessage);
}
});
});

View File

@@ -77,9 +77,21 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
Logger.printDebug(() -> "Updating spoof stream side effects preference");
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
String key = "revanced_spoof_video_streams_about_"
+ clientType.name().toLowerCase();
setTitle(str(key + "_title"));
setSummary(str(key + "_summary"));
String key = "revanced_spoof_video_streams_about_" +
(clientType == ClientType.IOS_UNPLUGGED
? "ios_tv"
: "android");
String title = str(key + "_title");
String summary = str(key + "_summary");
// Android VR supports AV1 but all other clients do not.
if (clientType != ClientType.ANDROID_VR_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_kids_videos");
setTitle(title);
setSummary(summary);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,23 +1,25 @@
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 androidx.annotation.Nullable;
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;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.videoplayer.PlayerControlTopButton;
// Edit: This should be a subclass of PlayerControlButton
public class CreateSegmentButtonController {
private static WeakReference<ImageView> buttonReference = new WeakReference<>(null);
private static boolean isShowing;
public class CreateSegmentButtonController extends PlayerControlTopButton {
@Nullable
private static CreateSegmentButtonController instance;
public static void hideControls() {
if (instance != null) instance.hide();
}
/**
* injection point
@@ -27,10 +29,7 @@ public class CreateSegmentButtonController {
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);
instance = new CreateSegmentButtonController(imageView);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
@@ -40,72 +39,22 @@ public class CreateSegmentButtonController {
* 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);
}
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* 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);
if (instance != null) instance.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 CreateSegmentButtonController(ImageView imageView) {
super(imageView, v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility());
}
private static boolean shouldBeShown() {
protected 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.res.ColorStateList;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.FrameLayout;
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.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.getResourceIdentifier;
public final class NewSegmentLayout extends FrameLayout {
private static final ColorStateList rippleColorStateList = new ColorStateList(
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 ctaBottomMargin;
@@ -47,10 +48,6 @@ public final class NewSegmentLayout extends FrameLayout {
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(
context,
"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
private interface ButtonOnClickHandlerFunction {
void apply();

View File

@@ -8,6 +8,7 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -19,11 +20,19 @@ import androidx.annotation.NonNull;
import java.util.Objects;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
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 TextView skipSponsorTextView;
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
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.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color);
background.setStyle(Paint.Style.FILL);
border = new Paint();
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.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
ctaBottomMargin = getResourceDimensionPixelSize("skip_button_cta_bottom_margin"); // dimen:skip_button_cta_bottom_margin
updateLayout();
skipSponsorBtnContainer.setOnClickListener(v -> {
// The view controller handles hiding this button, but hide it here as well just in case something goofs.
setVisibility(View.GONE);
@@ -72,30 +86,56 @@ public class SkipSponsorButton extends FrameLayout {
protected final void dispatchDraw(Canvas canvas) {
final int left = skipSponsorBtnContainer.getLeft();
final int top = skipSponsorBtnContainer.getTop();
final int leftPlusWidth = (left + skipSponsorBtnContainer.getWidth());
final int topPlusHeight = (top + skipSponsorBtnContainer.getHeight());
canvas.drawRect(left, top, leftPlusWidth, topPlusHeight, background);
if (!highContrast) {
canvas.drawLines(new float[]{
leftPlusWidth, top, left, top,
left, top, left, topPlusHeight,
left, topPlusHeight, leftPlusWidth, topPlusHeight},
border);
final int right = left + skipSponsorBtnContainer.getWidth();
final int bottom = top + skipSponsorBtnContainer.getHeight();
// Determine corner radius for rounded button
float cornerRadius = skipSponsorBtnContainer.getHeight() / 2f;
if (Settings.SB_SQUARE_LAYOUT.get()) {
// Square button.
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);
}
/**
* @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;
CharSequence newText = segment.getSkipButtonText();
//noinspection StringEqualsCharSequence
if (newText.equals(skipSponsorTextView.getText())) {
return false;
return;
}
skipSponsorTextView.setText(newText);
return true;
}
}

View File

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

View File

@@ -1,25 +1,27 @@
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 androidx.annotation.Nullable;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
import app.revanced.extension.youtube.videoplayer.PlayerControlTopButton;
// Edit: This should be a subclass of PlayerControlButton
public class VotingButtonController {
private static WeakReference<ImageView> buttonReference = new WeakReference<>(null);
private static boolean isShowing;
public class VotingButtonController extends PlayerControlTopButton {
@Nullable
private static VotingButtonController instance;
public static void hideControls() {
if (instance != null) instance.hide();
}
/**
* injection point
@@ -29,10 +31,7 @@ public class VotingButtonController {
Logger.printDebug(() -> "initializing voting button");
ImageView imageView = Objects.requireNonNull(Utils.getChildViewByResourceName(
youtubeControlsLayout, "revanced_sb_voting_button"));
imageView.setVisibility(View.GONE);
imageView.setOnClickListener(v -> SponsorBlockUtils.onVotingClicked(v.getContext()));
buttonReference = new WeakReference<>(imageView);
instance = new VotingButtonController(imageView);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
@@ -42,75 +41,22 @@ public class VotingButtonController {
* 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);
}
if (instance != null) instance.setVisibilityImmediate(visible);
}
/**
* 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);
if (instance != null) instance.setVisibility(visible, animated);
}
/**
* injection point
*/
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 VotingButtonController(ImageView imageView) {
super(imageView, v -> SponsorBlockUtils.onVotingClicked(v.getContext()));
}
private static boolean shouldBeShown() {
protected boolean shouldBeShown() {
return Settings.SB_ENABLED.get() && Settings.SB_VOTING_BUTTON.get()
&& SegmentPlaybackController.videoHasSegments() && !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,6 +2,8 @@ package app.revanced.extension.youtube.swipecontrols
import android.content.Context
import android.graphics.Color
import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerType
@@ -18,19 +20,17 @@ class SwipeControlsConfigurationProvider(
* should swipe controls be enabled? (global setting)
*/
val enableSwipeControls: Boolean
get() = isFullscreenVideo && (enableVolumeControls || enableBrightnessControl)
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
/**
* should swipe controls for volume be enabled?
*/
val enableVolumeControls: Boolean
get() = Settings.SWIPE_VOLUME.get()
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
/**
* should swipe controls for volume be enabled?
*/
val enableBrightnessControl: Boolean
get() = Settings.SWIPE_BRIGHTNESS.get()
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
/**
* is the video player currently in fullscreen mode?
@@ -44,7 +44,7 @@ class SwipeControlsConfigurationProvider(
* should volume key controls be overwritten? (global setting)
*/
val overwriteVolumeKeyControls: Boolean
get() = isFullscreenVideo && enableVolumeControls
get() = enableVolumeControls && isFullscreenVideo
//endregion
//region gesture adjustments
@@ -63,7 +63,6 @@ class SwipeControlsConfigurationProvider(
//endregion
//region overlay adjustments
/**
* should the overlay enable haptic feedback?
*/
@@ -77,23 +76,52 @@ class SwipeControlsConfigurationProvider(
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
/**
* text size for the overlay, in sp
* Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency.
* If the opacity value is out of range, it resets to the default and displays a warning message.
*/
val overlayTextSize: Int
get() = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
val overlayBackgroundOpacity: Int
get() {
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
}
opacity = opacity * 255 / 100
return Color.argb(opacity, 0, 0, 0)
}
/**
* get the background color for text on the overlay, as a color int
* The color of the progress overlay.
*/
val overlayTextBackgroundColor: Int
get() = Color.argb(Settings.SWIPE_OVERLAY_BACKGROUND_ALPHA.get(), 0, 0, 0)
val overlayProgressColor: Int
get() = 0xBFFFFFFF.toInt()
/**
* get the foreground color for text on the overlay, as a color int
* The color used for the background of the progress overlay fill.
*/
val overlayForegroundColor: Int
val overlayFillBackgroundPaint: Int
get() = 0x80D3D3D3.toInt()
/**
* The color used for the text and icons in the overlay.
*/
val overlayTextColor: Int
get() = Color.WHITE
/**
* A flag that determines if the overlay should only show the icon.
*/
val overlayShowOverlayMinimalStyle: Boolean
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get()
/**
* A flag that determines if the progress bar should be circular.
*/
val isCircularProgressBar: Boolean
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get()
//endregion
//region behaviour

View File

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

View File

@@ -82,11 +82,15 @@ abstract class BaseGestureController(
}
override fun onScroll(
from: MotionEvent,
from: MotionEvent?,
to: MotionEvent,
distanceX: Float,
distanceY: Float,
): Boolean {
if (from == null) {
return false
}
// submit to swipe detector
submitForSwipe(from, to, distanceX, distanceY)

View File

@@ -1,138 +1,145 @@
package app.revanced.extension.youtube.swipecontrols.views
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.os.Handler
import android.os.Looper
import android.util.TypedValue
import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.TextView
import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
import app.revanced.extension.youtube.swipecontrols.misc.applyDimension
import kotlin.math.min
import kotlin.math.round
/**
* main overlay layout for volume and brightness swipe controls
*
* @param context context to create in
* Main overlay layout for displaying volume and brightness level with both circular and rectangular progress bars.
*/
class SwipeControlsOverlayLayout(
context: Context,
private val config: SwipeControlsConfigurationProvider,
) : RelativeLayout(context), SwipeControlsOverlay {
/**
* DO NOT use this, for tools only
*/
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
private val feedbackTextView: TextView
private val autoBrightnessIcon: Drawable
private val manualBrightnessIcon: Drawable
private val mutedVolumeIcon: Drawable
private val normalVolumeIcon: Drawable
// Drawable icons for brightness and volume
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
private val lowBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_low")
private val mediumBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_medium")
private val highBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_high")
private val fullBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_full")
private val mutedVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_mute")
private val lowVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_low")
private val normalVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_normal")
private val fullVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_high")
private fun getDrawable(name: String, width: Int, height: Int): Drawable {
return resources.getDrawable(
// Function to retrieve drawable resources by name
private fun getDrawable(name: String): Drawable {
val drawable = resources.getDrawable(
Utils.getResourceIdentifier(context, name, "drawable"),
context.theme,
).apply {
setTint(config.overlayForegroundColor)
setBounds(
0,
0,
width,
height,
)
}
)
drawable.setTint(config.overlayTextColor)
return drawable
}
// Initialize progress bars
private val circularProgressView: CircularProgressView
private val horizontalProgressView: HorizontalProgressView
init {
// init views
val feedbackTextViewPadding = 2.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP)
val compoundIconPadding = 4.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP)
feedbackTextView = TextView(context).apply {
layoutParams = LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
).apply {
// Initialize circular progress bar
circularProgressView = CircularProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayShowOverlayMinimalStyle,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor
).apply {
layoutParams = LayoutParams(300, 300).apply {
addRule(CENTER_IN_PARENT, TRUE)
setPadding(
feedbackTextViewPadding,
feedbackTextViewPadding,
feedbackTextViewPadding,
feedbackTextViewPadding,
)
}
background = GradientDrawable().apply {
cornerRadius = 8f
setColor(config.overlayTextBackgroundColor)
}
setTextColor(config.overlayForegroundColor)
setTextSize(TypedValue.COMPLEX_UNIT_SP, config.overlayTextSize.toFloat())
compoundDrawablePadding = compoundIconPadding
visibility = GONE
visibility = GONE // Initially hidden
}
addView(feedbackTextView)
addView(circularProgressView)
// get icons scaled, assuming square icons
val iconHeight = round(feedbackTextView.lineHeight * .8).toInt()
autoBrightnessIcon = getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
manualBrightnessIcon = getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight)
normalVolumeIcon = getDrawable("revanced_ic_sc_volume_normal", iconHeight, iconHeight)
// Initialize rectangular progress bar
val screenWidth = resources.displayMetrics.widthPixels
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
horizontalProgressView = HorizontalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayShowOverlayMinimalStyle,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor
).apply {
layoutParams = LayoutParams(layoutWidth, 100).apply {
addRule(CENTER_HORIZONTAL)
topMargin = 40 // Top margin
}
visibility = GONE // Initially hidden
}
addView(horizontalProgressView)
}
// Handler and callback for hiding progress bars
private val feedbackHideHandler = Handler(Looper.getMainLooper())
private val feedbackHideCallback = Runnable {
feedbackTextView.visibility = GONE
circularProgressView.visibility = GONE
horizontalProgressView.visibility = GONE
}
/**
* show the feedback view for a given time
*
* @param message the message to show
* @param icon the icon to use
* Displays the progress bar with the appropriate value, icon, and type (brightness or volume).
*/
private fun showFeedbackView(message: String, icon: Drawable) {
private fun showFeedbackView(value: String, progress: Int, max: Int, icon: Drawable, isBrightness: Boolean) {
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
feedbackTextView.apply {
text = message
setCompoundDrawablesRelative(
icon,
null,
null,
null,
)
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
viewToShow.apply {
setProgress(progress, max, value, isBrightness)
this.icon = icon
visibility = VISIBLE
}
}
// Handle volume change
override fun onVolumeChanged(newVolume: Int, maximumVolume: Int) {
showFeedbackView(
"$newVolume",
if (newVolume > 0) normalVolumeIcon else mutedVolumeIcon,
)
val volumePercentage = (newVolume.toFloat() / maximumVolume) * 100
val icon = when {
newVolume == 0 -> mutedVolumeIcon
volumePercentage < 33 -> lowVolumeIcon
volumePercentage < 66 -> normalVolumeIcon
else -> fullVolumeIcon
}
showFeedbackView("$newVolume", newVolume, maximumVolume, icon, isBrightness = false)
}
// Handle brightness change
override fun onBrightnessChanged(brightness: Double) {
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
showFeedbackView(
str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text"),
autoBrightnessIcon,
)
} else if (brightness >= 0) {
showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon)
showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
} else {
val brightnessValue = round(brightness).toInt()
val icon = when {
brightnessValue < 25 -> lowBrightnessIcon
brightnessValue < 50 -> mediumBrightnessIcon
brightnessValue < 75 -> highBrightnessIcon
else -> fullBrightnessIcon
}
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
}
}
// Begin swipe session
override fun onEnterSwipeSession() {
if (config.shouldEnableHapticFeedback) {
@Suppress("DEPRECATION")
@@ -143,3 +150,233 @@ class SwipeControlsOverlayLayout(
}
}
}
/**
* Abstract base class for progress views to reduce code duplication.
*/
/**
* Abstract base class for progress views to reduce code duplication.
*/
abstract class AbstractProgressView(
context: Context,
protected val overlayBackgroundOpacity: Int,
protected val overlayShowOverlayMinimalStyle: Boolean,
protected val overlayProgressColor: Int,
protected val overlayFillBackgroundPaint: Int,
protected val overlayTextColor: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
// Combined paint creation function for both fill and stroke styles
private fun createPaint(color: Int, style: Paint.Style = Paint.Style.FILL, strokeCap: Paint.Cap = Paint.Cap.BUTT, strokeWidth: Float = 0f) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
this.style = style
this.color = color
this.strokeCap = strokeCap
this.strokeWidth = strokeWidth
}
// Initialize paints
public val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
public val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
public val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor
textAlign = Paint.Align.CENTER
textSize = 30f // Can adjust based on need
}
protected var progress = 0
protected var maxProgress = 100
protected var displayText: String = "0"
protected var isBrightness = true
public var icon: Drawable? = null
init {
// Stroke widths are now set in createPaint for progressPaint and fillBackgroundPaint
}
fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
progress = value
maxProgress = max
displayText = text
isBrightness = isBrightnessMode
invalidate()
}
override fun onDraw(canvas: Canvas) {
// Base class implementation can be empty
}
}
/**
* Custom view for rendering a circular progress indicator with text and icon.
*/
class CircularProgressView(
context: Context,
overlayBackgroundOpacity: Int,
overlayShowOverlayMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
overlayTextColor: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractProgressView(
context,
overlayBackgroundOpacity,
overlayShowOverlayMinimalStyle,
overlayProgressColor,
overlayFillBackgroundPaint,
overlayTextColor,
attrs,
defStyleAttr
) {
private val rectF = RectF()
init {
textPaint.textSize = 40f // Override default text size for horizontal view
progressPaint.strokeWidth = 20f
fillBackgroundPaint.strokeWidth = 20f
progressPaint.strokeCap = Paint.Cap.ROUND
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.STROKE
fillBackgroundPaint.style = Paint.Style.STROKE
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val size = min(width, height).toFloat()
rectF.set(20f, 20f, size - 20f, size - 20f)
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
// Select the paint for drawing based on whether it's brightness or volume.
val sweepAngle = (progress.toFloat() / maxProgress) * 360
canvas.drawArc(rectF, -90f, sweepAngle, false, progressPaint) // Draw the progress arc.
// Draw the icon in the center.
icon?.let {
val iconSize = if (overlayShowOverlayMinimalStyle) 100 else 80
val iconX = (width - iconSize) / 2
val iconY = (height / 2) - if (overlayShowOverlayMinimalStyle) 50 else 80
it.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
it.draw(canvas)
}
// If not in icon-only mode, draw the text inside the ring.
if (!overlayShowOverlayMinimalStyle) {
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
}
}
}
/**
* Custom view for rendering a rectangular progress bar with icons and text.
*/
class HorizontalProgressView(
context: Context,
overlayBackgroundOpacity: Int,
overlayShowOverlayMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
overlayTextColor: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractProgressView(
context,
overlayBackgroundOpacity,
overlayShowOverlayMinimalStyle,
overlayProgressColor,
overlayFillBackgroundPaint,
overlayTextColor,
attrs,
defStyleAttr
) {
private val iconSize = 60f
private val padding = 40f
init {
textPaint.textSize = 30f // Override default text size for horizontal view
progressPaint.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
fillBackgroundPaint.style = Paint.Style.FILL
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val width = width.toFloat()
val height = height.toFloat()
// Radius for rounded corners
val cornerRadius = min(width, height) / 2
// Calculate the total width for the elements
val minimalElementWidth = 5 * padding + iconSize
// Calculate the starting point (X) to center the elements
val minimalStartX = (width - minimalElementWidth) / 2
// Draw the background
if (!overlayShowOverlayMinimalStyle) {
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
} else {
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
}
if (!overlayShowOverlayMinimalStyle) {
// Draw the fill background
val startX = 2 * padding + iconSize
val endX = width - 4 * padding
val fillWidth = endX - startX
canvas.drawRoundRect(
startX,
height / 2 - 5f,
endX,
height / 2 + 5f,
10f, 10f,
fillBackgroundPaint
)
// Draw the progress
val progressWidth = (progress.toFloat() / maxProgress) * fillWidth
canvas.drawRoundRect(
startX,
height / 2 - 5f,
startX + progressWidth,
height / 2 + 5f,
10f, 10f,
progressPaint
)
}
// Draw the icon
icon?.let {
val iconX = if (!overlayShowOverlayMinimalStyle) {
padding
} else {
padding + minimalStartX
}
val iconY = height / 2 - iconSize / 2
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
it.draw(canvas)
}
// Draw the text on the right
val textX = if (!overlayShowOverlayMinimalStyle) {
width - 2 * padding
} else {
minimalStartX + minimalElementWidth - 2 * padding
}
val textY = height / 2 + textPaint.textSize / 3
// Draw the text
canvas.drawText(displayText, textX, textY, textPaint)
}
}

View File

@@ -10,7 +10,7 @@ import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class CopyVideoUrlButton extends PlayerControlButton {
public class CopyVideoUrlButton extends PlayerControlBottomButton {
@Nullable
private static CopyVideoUrlButton instance;

View File

@@ -10,7 +10,7 @@ import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class CopyVideoUrlTimestampButton extends PlayerControlButton {
public class CopyVideoUrlTimestampButton extends PlayerControlBottomButton {
@Nullable
private static CopyVideoUrlTimestampButton instance;

View File

@@ -11,7 +11,7 @@ import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class ExternalDownloadButton extends PlayerControlButton {
public class ExternalDownloadButton extends PlayerControlBottomButton {
@Nullable
private static ExternalDownloadButton instance;

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