Compare commits

..

111 Commits

Author SHA1 Message Date
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
176 changed files with 13002 additions and 8137 deletions

View File

@@ -2,7 +2,7 @@ name: Pull strings
on:
schedule:
- cron: "0 */8 * * *"
- cron: "0 */6 * * *"
workflow_dispatch:
jobs:
@@ -16,8 +16,9 @@ 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
@@ -25,6 +26,7 @@ jobs:
config: crowdin.yml
upload_sources: false
download_translations: true
skip_ref_checkout: true
localization_branch_name: feat/translations
create_pull_request: false
env:

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

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

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,8 +1,8 @@
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.
*/
@@ -34,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,
@@ -87,7 +87,7 @@ public enum AudioStreamLanguage {
private final String language;
AudioStreamLanguage() {
AppLanguage() {
language = name().toLowerCase(Locale.US);
}
@@ -103,4 +103,12 @@ public enum AudioStreamLanguage {
return language;
}
public Locale getLocale() {
if (this == DEFAULT) {
return Locale.getDefault();
}
return Locale.forLanguageTag(language);
}
}

View File

@@ -6,7 +6,6 @@ import static app.revanced.extension.shared.settings.Setting.parent;
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;
/**
@@ -22,9 +21,11 @@ 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, new AudioStreamLanguageOverrideAvailability());
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE);
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 SpoofiOSAvailability());
// Client type must be last spoof setting due to cyclic references.

View File

@@ -153,7 +153,6 @@ public abstract class Setting<T> {
/**
* Confirmation message to display, if the user tries to change the setting from the default value.
* Currently this works only for Boolean setting types.
*/
@Nullable
public final StringRef userDialogMessage;
@@ -244,6 +243,7 @@ public abstract class Setting<T> {
*
* This method will be deleted in the future.
*/
@SuppressWarnings("rawtypes")
public static void migrateFromOldPreferences(@NonNull SharedPrefCategory oldPrefs, @NonNull Setting setting, String settingKey) {
if (!oldPrefs.preferences.contains(settingKey)) {
return; // Nothing to do.
@@ -419,6 +419,7 @@ public abstract class Setting<T> {
boolean rebootSettingChanged = false;
int numberOfSettingsImported = 0;
//noinspection rawtypes
for (Setting setting : SETTINGS) {
String key = setting.getImportExportKey();
if (json.has(key)) {

View File

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

View File

@@ -4,6 +4,10 @@ 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 {
@@ -11,56 +15,99 @@ public enum ClientType {
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",
"com.google.android.apps.youtube.unplugged/8.49.0 (Linux; U; Android 14; GB) gzip",
"34",
"UTT3.240625.001.K5",
"132.0.6808.3",
"8.49.0",
true,
true,
"Android TV"
),
// Cannot play livestreams and lacks HDR, but can play videos with music and labeled "for children".
// Google Pixel 9 Pro Fold
// https://dumps.tadiphone.dev/dumps/google/barbet
ANDROID_CREATOR(
14,
"ANDROID_CREATOR",
"com.google.android.apps.youtube.creator",
"Google",
"Pixel 9 Pro Fold",
"Android",
"15",
"35",
"AP3A.241005.015.A2",
"132.0.6779.0",
"23.47.101",
true,
true,
"Android Creator"
),
ANDROID_VR(
ANDROID_VR_NO_AUTH.id,
ANDROID_VR_NO_AUTH.clientName,
ANDROID_VR_NO_AUTH.packageName,
ANDROID_VR_NO_AUTH.deviceMake,
ANDROID_VR_NO_AUTH.deviceModel,
ANDROID_VR_NO_AUTH.osName,
ANDROID_VR_NO_AUTH.osVersion,
ANDROID_VR_NO_AUTH.userAgent,
ANDROID_VR_NO_AUTH.androidSdkVersion,
ANDROID_VR_NO_AUTH.buildId,
ANDROID_VR_NO_AUTH.cronetVersion,
ANDROID_VR_NO_AUTH.clientVersion,
ANDROID_VR_NO_AUTH.requiresAuth,
true,
"Android VR"
),
IOS_UNPLUGGED(33,
IOS_UNPLUGGED(
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.
: "18.1.1.22B91",
forceAVC()
? "com.google.ios.youtubeunplugged/6.45 (iPhone; U; CPU iOS 13_7 like Mac OS X)"
: "com.google.ios.youtubeunplugged/8.33 (iPhone; U; CPU iOS 18_1_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/152043/
// Some newer versions can also force AVC,
// but 6.45 is the last version that supports iOS 13.
forceAVC()
// Some newer versions can also force AVC,
// but 6.45 is the last version that supports iOS 13.
? "6.45"
: "8.33",
: "8.49",
true,
true,
forceAVC()
? "iOS TV Force AVC"
@@ -80,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.
@@ -101,39 +163,97 @@ public enum ClientType {
@Nullable
public final String androidSdkVersion;
/**
* Android build id, equivalent to {@link Build#ID}.
* Field is null if not applicable.
*/
@Nullable
private final String buildId;
/**
* Cronet release version, as found in decompiled client apk.
* Field is null if not applicable.
*/
@Nullable
private final String cronetVersion;
/**
* App version.
*/
public final String clientVersion;
/**
* If the client can access the API logged in.
* If this client requires authentication and does not work
* if logged out or in incognito mode.
*/
public final boolean canLogin;
public final boolean requiresAuth;
/**
* If the client should use authentication if available.
*/
public final boolean useAuth;
/**
* Friendly name displayed in stats for nerds.
*/
public final String friendlyName;
@SuppressWarnings("ConstantLocale")
ClientType(int id,
String clientName,
String packageName,
String deviceMake,
String deviceModel,
String osName,
String osVersion,
String userAgent,
@Nullable String androidSdkVersion,
@Nullable String buildId,
@Nullable String cronetVersion,
String clientVersion,
boolean canLogin,
boolean requiresAuth,
boolean useAuth,
String friendlyName) {
this.id = id;
this.clientName = clientName;
this.packageName = packageName;
this.deviceMake = deviceMake;
this.deviceModel = deviceModel;
this.osName = osName;
this.osVersion = osVersion;
this.userAgent = userAgent;
this.androidSdkVersion = androidSdkVersion;
this.buildId = buildId;
this.cronetVersion = cronetVersion;
this.clientVersion = clientVersion;
this.canLogin = canLogin;
this.requiresAuth = requiresAuth;
this.useAuth = useAuth;
this.friendlyName = friendlyName;
Locale defaultLocale = Locale.getDefault();
if (androidSdkVersion == null) {
// Convert version from '18.2.22C152' into '18_2_22'
String userAgentOsVersion = osVersion
.replaceAll("(\\d+\\.\\d+\\.\\d+).*", "$1")
.replace(".", "_");
// https://github.com/mitmproxy/mitmproxy/issues/4836
this.userAgent = String.format("%s/%s (%s; U; CPU iOS %s like Mac OS X; %s)",
packageName,
clientVersion,
deviceModel,
userAgentOsVersion,
defaultLocale
);
} else {
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)",
packageName,
clientVersion,
osVersion,
defaultLocale,
deviceModel,
Objects.requireNonNull(buildId),
Objects.requireNonNull(cronetVersion)
);
}
Logger.printDebug(() -> "userAgent: " + this.userAgent);
}
}

View File

@@ -34,6 +34,12 @@ public class SpoofVideoStreamsPatch {
return false; // Modified during patching.
}
public static boolean notSpoofingToAndroid() {
return !isPatchIncluded()
|| !BaseSettings.SPOOF_VIDEO_STREAMS.get()
|| BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
}
/**
* Injection point.
* Blocks /get_watch requests by returning an unreachable URI.
@@ -71,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);
@@ -109,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);
}
@@ -206,23 +219,11 @@ public class SpoofVideoStreamsPatch {
return videoFormat;
}
public static final class NotSpoofingAndroidAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
if (SpoofVideoStreamsPatch.isPatchIncluded()) {
return !BaseSettings.SPOOF_VIDEO_STREAMS.get()
|| BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
}
return true;
}
}
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.ANDROID_VR_NO_AUTH;
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.ANDROID_VR_NO_AUTH;
}
}

View File

@@ -5,12 +5,12 @@ 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;
import app.revanced.extension.shared.requests.Route;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
import app.revanced.extension.shared.spoof.ClientType;
final class PlayerRoutes {
@@ -31,7 +31,7 @@ final class PlayerRoutes {
private PlayerRoutes() {
}
static String createInnertubeBody(ClientType clientType) {
static String createInnertubeBody(ClientType clientType, String videoId) {
JSONObject innerTubeBody = new JSONObject();
try {
@@ -42,25 +42,28 @@ final class PlayerRoutes {
// 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();
AudioStreamLanguage language = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH
? BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get()
: AudioStreamLanguage.DEFAULT;
Locale streamLocale = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH
? BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getLocale()
: Locale.getDefault();
JSONObject client = new JSONObject();
client.put("hl", language.getLanguage());
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);
}
@@ -76,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

@@ -120,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);
@@ -128,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);
@@ -150,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);
@@ -182,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);
@@ -191,8 +203,8 @@ public class StreamingDataRequest {
// gzip encoding doesn't response with content length (-1),
// but empty response body does.
if (connection.getContentLength() == 0) {
if (BaseSettings.DEBUG.get()) {
Logger.printException(() -> "Ignoring empty client: " + clientType);
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());

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

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

View File

@@ -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

@@ -1,6 +1,8 @@
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")
@@ -8,6 +10,20 @@ 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.
*/

View File

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

View File

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

View File

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

View File

@@ -8,10 +8,12 @@ import android.view.View;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.settings.Settings;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.StringTrieSearch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class AdsFilter extends Filter {
@@ -22,6 +24,11 @@ public final class AdsFilter extends Filter {
// endregion
// https://encrypted-tbn0.gstatic.com/shopping?q=abc
private static final String STORE_BANNER_DOMAIN = "gstatic.com/shopping";
private static final boolean HIDE_END_SCREEN_STORE_BANNER =
Settings.HIDE_END_SCREEN_STORE_BANNER.get();
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup playerShoppingShelf;
@@ -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(
@@ -112,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"
);
@@ -172,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;
@@ -188,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

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

View File

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

View File

@@ -80,7 +80,8 @@ public final class LayoutComponentsFilter extends Filter {
"images_post_root_slim.eml",
"text_post_root_slim.eml",
"post_base_wrapper_slim.eml",
"poll_post_root.eml"
"poll_post_root.eml",
"videos_post_root.eml"
);
final var communityGuidelines = new StringFilterGroup(
@@ -106,7 +107,8 @@ public final class LayoutComponentsFilter extends Filter {
inFeedSurvey = new StringFilterGroup(
Settings.HIDE_FEED_SURVEY,
"in_feed_survey",
"slimline_survey"
"slimline_survey",
"feed_nudge"
);
final var medicalPanel = new StringFilterGroup(
@@ -161,9 +163,9 @@ public final class LayoutComponentsFilter extends Filter {
"inline_expander"
);
final var channelBar = new StringFilterGroup(
final var compactChannelBar = new StringFilterGroup(
Settings.HIDE_CHANNEL_BAR,
"channel_bar"
"compact_channel_bar"
);
final var relatedVideos = new StringFilterGroup(
@@ -252,7 +254,7 @@ public final class LayoutComponentsFilter extends Filter {
inFeedSurvey,
notifyMe,
likeSubscribeGlow,
channelBar,
compactChannelBar,
communityPosts,
paidPromotion,
searchResultVideo,

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

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

View File

@@ -7,8 +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.shared.spoof.SpoofVideoStreamsPatch.NotSpoofingAndroidAvailability;
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;
@@ -25,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;
@@ -43,21 +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 NotSpoofingAndroidAvailability());
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);
@@ -120,6 +127,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
@@ -139,10 +147,10 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_subscribers_community_guidelines", TRUE);
public static final BooleanSetting HIDE_TIMED_REACTIONS = new BooleanSetting("revanced_hide_timed_reactions", TRUE);
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
// Miniplayer
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.DEFAULT, true);
private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);
@@ -171,6 +179,7 @@ public class Settings extends BaseSettings {
// Description
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE);
public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE);
public static final BooleanSetting HIDE_HOW_THIS_WAS_MADE_SECTION = new BooleanSetting("revanced_hide_how_this_was_made_section", FALSE);
public static final BooleanSetting HIDE_INFO_CARDS_SECTION = new BooleanSetting("revanced_hide_info_cards_section", TRUE);
public static final BooleanSetting HIDE_KEY_CONCEPTS_SECTION = new BooleanSetting("revanced_hide_key_concepts_section", FALSE);
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
@@ -200,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));
@@ -233,6 +243,7 @@ public class Settings extends BaseSettings {
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);
@@ -261,18 +272,19 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SHORTS_AUTOPLAY_BACKGROUND = new BooleanSetting("revanced_shorts_autoplay_background", TRUE);
// Seekbar
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", TRUE);
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", FALSE);
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true);
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE);
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE, true);
public static final BooleanSetting HIDE_TIMESTAMP = new BooleanSetting("revanced_hide_timestamp", FALSE);
public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE);
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", FALSE);
public static final BooleanSetting SEEKBAR_THUMBNAILS_HIGH_QUALITY = new BooleanSetting("revanced_seekbar_thumbnails_high_quality", FALSE, true,
"revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability());
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
public static final StringSetting SEEKBAR_CUSTOM_COLOR_VALUE = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
public static final StringSetting SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_primary", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
public static final StringSetting SEEKBAR_CUSTOM_COLOR_ACCENT = new StringSetting("revanced_seekbar_custom_color_accent", "#FF2791", true, parent(SEEKBAR_CUSTOM_COLOR));
// Misc
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
@@ -293,16 +305,18 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, parent(BaseSettings.DEBUG));
// Swipe controls
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", TRUE);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", TRUE);
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE);
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
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 IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 50, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
// Debugging
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true,
@@ -399,6 +413,35 @@ public class Settings extends BaseSettings {
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
// region SB import/export callbacks

View File

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

View File

@@ -25,9 +25,12 @@ import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.EnumSetting;
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.LicenseActivityHook;
import app.revanced.extension.youtube.settings.Settings;
/**
@@ -109,15 +112,20 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
}
preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference, 1);
}
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);
@@ -133,9 +141,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
.getParent();
// Fix required for Android 15 and YT 19.45+
// FIXME:
// On Android 15 the text layout is not aligned the same as the parent
// screen and it looks a little off. Otherwise this works.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
rootView.setOnApplyWindowInsetsListener((v, insets) -> {
Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars());
@@ -162,6 +167,8 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
toolbarTextView.setTextColor(ThemeHelper.getForegroundColor());
}
LicenseActivityHook.setToolbarLayoutParams(toolbar);
rootView.addView(toolbar, 0);
return false;
}

View File

@@ -81,7 +81,17 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
(clientType == ClientType.IOS_UNPLUGGED
? "ios_tv"
: "android");
setTitle(str(key + "_title"));
setSummary(str(key + "_summary"));
String title = str(key + "_title");
String summary = str(key + "_summary");
// Android VR supports AV1 but all other clients do not.
if (clientType != ClientType.ANDROID_VR && clientType != ClientType.ANDROID_VR_NO_AUTH) {
summary += '\n' + str("revanced_spoof_video_streams_about_no_av1");
}
summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos");
setTitle(title);
setSummary(summary);
}
}

View File

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

View File

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

View File

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

View File

@@ -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
@@ -86,7 +88,18 @@ class SwipeControlsConfigurationProvider(
* get the background color for text on the overlay, as a color int
*/
val overlayTextBackgroundColor: Int
get() = Color.argb(Settings.SWIPE_OVERLAY_BACKGROUND_ALPHA.get(), 0, 0, 0)
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 foreground color for text on the overlay, as a color int

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

@@ -0,0 +1,15 @@
package com.airbnb.lottie;
import java.io.InputStream;
@SuppressWarnings("unused")
public class LottieAnimationView {
public void patch_setAnimation(InputStream stream, String cacheKey) {
throw new RuntimeException("stub");
}
public final void patch_setAnimation(int rawResInt) {
throw new RuntimeException("stub");
}
}

View File

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

View File

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

View File

@@ -461,10 +461,6 @@ public final class app/revanced/patches/reddit/customclients/joeyforreddit/detec
public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/redditisfun/api/FingerprintsKt {
public static final fun baseClientIdFingerprint (Ljava/lang/String;)Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -548,7 +544,6 @@ public final class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentP
}
public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
public final fun getFingerprint ()Lapp/revanced/patcher/Fingerprint;
public final fun invoke (Lapp/revanced/patcher/patch/BytecodePatchContext;Ljava/lang/String;)V
}
@@ -608,16 +603,19 @@ public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatch
}
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
public static synthetic fun settingsPatch$default (Lkotlin/Pair;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
public static synthetic fun settingsPatch$default (Ljava/util/List;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
}
public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreference {
public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getIcon ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
public final fun getLayout ()Ljava/lang/String;
public final fun getSummaryKey ()Ljava/lang/String;
public final fun getTag ()Ljava/lang/String;
public final fun getTitleKey ()Ljava/lang/String;
@@ -640,17 +638,19 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP
public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getIcon ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
public final fun getLayout ()Ljava/lang/String;
public final fun getPreferences ()Ljava/util/Set;
public final fun getTitleKey ()Ljava/lang/String;
public abstract fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
}
public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V
public final fun getCategories ()Ljava/util/Set;
public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
@@ -658,8 +658,8 @@ public class app/revanced/patches/shared/misc/settings/preference/BasePreference
}
public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen$Category : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V
public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceCategory;
@@ -667,6 +667,7 @@ public class app/revanced/patches/shared/misc/settings/preference/BasePreference
public final class app/revanced/patches/shared/misc/settings/preference/InputType : java/lang/Enum {
public static final field NUMBER Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field NUMBER_DECIMAL Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT_CAP_CHARACTERS Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT_MULTI_LINE Lapp/revanced/patches/shared/misc/settings/preference/InputType;
@@ -677,8 +678,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/InputTyp
}
public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getIntent ()Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
public fun hashCode ()I
@@ -698,8 +699,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getEntries ()Lapp/revanced/util/resource/ArrayResource;
public final fun getEntriesKey ()Ljava/lang/String;
public final fun getEntryValues ()Lapp/revanced/util/resource/ArrayResource;
@@ -708,22 +709,22 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
}
public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSelectable ()Z
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
public class app/revanced/patches/shared/misc/settings/preference/PreferenceCategory : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getPreferences ()Ljava/util/Set;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getPreferences ()Ljava/util/Set;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
@@ -732,6 +733,7 @@ public final class app/revanced/patches/shared/misc/settings/preference/Preferen
public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public final fun appendSortType (Ljava/lang/String;)Ljava/lang/String;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public final fun getKeySuffix ()Ljava/lang/String;
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
@@ -750,8 +752,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SummaryT
public final class app/revanced/patches/shared/misc/settings/preference/SwitchPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSummaryOffKey ()Ljava/lang/String;
public final fun getSummaryOnKey ()Ljava/lang/String;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
@@ -759,8 +761,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SwitchPr
public final class app/revanced/patches/shared/misc/settings/preference/TextPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getInputType ()Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
@@ -1106,6 +1108,10 @@ public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlaye
public static final fun getHidePlayerOverlayButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/formfactor/ChangeFormFactorPatchKt {
public static final fun getChangeFormFactorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatchKt {
public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1235,7 +1241,6 @@ public final class app/revanced/patches/youtube/layout/startupshortsreset/Disabl
}
public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatchKt {
public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
public static final fun getEnableTabletLayoutPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1268,10 +1273,6 @@ public final class app/revanced/patches/youtube/misc/backgroundplayback/Backgrou
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatchKt {
public static final fun getCheckEnvironmentPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt {
public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1364,6 +1365,7 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
public static final fun is_19_43_or_greater ()Z
public static final fun is_19_46_or_greater ()Z
public static final fun is_19_47_or_greater ()Z
public static final fun is_19_49_or_greater ()Z
}
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
@@ -1408,14 +1410,14 @@ public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatc
public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/shared/FingerprintsKt {
public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt {
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/hdr/DisableHdrPatchKt {
public static final fun getDisableHdrPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt {
public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V
@@ -1457,10 +1459,6 @@ public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeed
public static final fun getPlaybackSpeedButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatchKt {
public static final fun getSpeedUnavailableId ()J
}
public final class app/revanced/patches/youtube/video/videoid/VideoIdPatchKt {
public static final fun getVideoIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun hookBackgroundPlayVideoId (Ljava/lang/String;)V

View File

@@ -21,6 +21,25 @@ dependencies {
compileOnly(project(":patches:stub"))
}
tasks {
register<JavaExec>("preprocessCrowdinStrings") {
description = "Preprocess strings for Crowdin push"
dependsOn(compileKotlin)
classpath = sourceSets["main"].runtimeClasspath
mainClass.set("app.revanced.util.CrowdinPreprocessorKt")
args = listOf(
"src/main/resources/addresources/values/strings.xml",
// Ideally this would use build/tmp/crowdin/strings.xml
// But using that does not work with Crowdin pull because
// it does not recognize the strings.xml file belongs to this project.
"src/main/resources/addresources/values/strings.xml"
)
}
}
kotlin {
compilerOptions {
freeCompilerArgs = listOf("-Xcontext-receivers")
@@ -38,4 +57,4 @@ publishing {
}
}
}
}
}

View File

@@ -359,15 +359,15 @@ val addResourcesPatch = resourcePatch(
}
getOrPut(resourceFileName) {
val targetFile = this@finalize["res/$value/$resourceFileName.xml"].also {
this@finalize["res/$value/$resourceFileName.xml"].also {
it.parentFile?.mkdirs()
if (it.createNewFile()) {
it.writeText("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>")
}
}
document(targetFile.path).let { document ->
document("res/$value/$resourceFileName.xml").let { document ->
// Save the target node here as well
// in order to avoid having to call document.getNode("resources")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ val removeGooglePlayIntegrityCheckPatch = bytecodePatch(
description = "Removes the Google Play Integrity check. With this it's possible to use SwissID on custom ROMS." +
"If the device is rooted, root permissions must be hidden from the app.",
) {
compatibleWith("com.swisssign.swissid.mobile")
compatibleWith("com.swisssign.swissid.mobile"("5.2.9"))
execute {
checkIntegrityFingerprint.method.addInstructions(

View File

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

View File

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

View File

@@ -24,9 +24,7 @@ internal const val EXTENSION_METHOD_DESCRIPTOR =
val enableSlideToSeekPatch = bytecodePatch(
name = "Enable slide to seek",
description = "Adds an option to enable slide to seek " +
"instead of playing at 2x speed when pressing and holding in the video player. " +
"Including this patch may cause issues with tapping or double tapping the video player overlay.",
use = false,
"instead of playing at 2x speed when pressing and holding in the video player."
) {
dependsOn(
sharedExtensionPatch,

View File

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

View File

@@ -10,6 +10,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityFingerprint
@@ -17,6 +18,8 @@ import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity;"
private val swipeControlsResourcePatch = resourcePatch {
dependsOn(
settingsPatch,
@@ -26,6 +29,12 @@ private val swipeControlsResourcePatch = resourcePatch {
execute {
addResources("youtube", "interaction.swipecontrols.swipeControlsResourcePatch")
if (is_19_43_or_greater) {
PreferenceScreen.SWIPE_CONTROLS.addPreferences(
SwitchPreference("revanced_swipe_change_video")
)
}
PreferenceScreen.SWIPE_CONTROLS.addPreferences(
SwitchPreference("revanced_swipe_brightness"),
SwitchPreference("revanced_swipe_volume"),
@@ -35,7 +44,7 @@ private val swipeControlsResourcePatch = resourcePatch {
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_background_alpha", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
)
@@ -101,5 +110,16 @@ val swipeControlsPatch = bytecodePatch(
).toMutable()
}
}
// region patch to enable/disable swipe to change video.
if (is_19_43_or_greater) {
swipeChangeVideoFingerprint.method.insertFeatureFlagBooleanOverride(
SWIPE_CHANGE_VIDEO_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z"
)
}
// endregion
}
}

View File

@@ -65,9 +65,12 @@ val navigationButtonsPatch = bytecodePatch(
)
if (is_19_25_or_greater) {
preferences += SwitchPreference("revanced_disable_translucent_status_bar")
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_light")
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_disable_translucent_status_bar")
)
}
PreferenceScreen.GENERAL_LAYOUT.addPreferences(

View File

@@ -0,0 +1,75 @@
package app.revanced.patches.youtube.layout.formfactor
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;"
@Suppress("unused")
val changeFormFactorPatch = bytecodePatch(
name = "Change form factor",
description = "Adds an option to change the UI appearance to a phone, tablet, or automotive device.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
addResources("youtube", "layout.formfactor.changeFormFactorPatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
ListPreference(
"revanced_change_form_factor",
summaryKey = null,
)
)
createPlayerRequestBodyWithModelFingerprint.method.apply {
val formFactorEnumClass = formFactorEnumConstructorFingerprint.originalClassDef.type
val index = indexOfFirstInstructionOrThrow {
val reference = getReference<FieldReference>()
opcode == Opcode.IGET &&
reference?.definingClass == formFactorEnumClass &&
reference.type == "I"
}
val register = getInstruction<TwoRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getFormFactor(I)I
move-result v$register
"""
)
}
}
}

View File

@@ -0,0 +1,49 @@
package app.revanced.patches.youtube.layout.formfactor
import app.revanced.patcher.fingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal val formFactorEnumConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
strings(
"UNKNOWN_FORM_FACTOR",
"SMALL_FORM_FACTOR",
"LARGE_FORM_FACTOR",
"AUTOMOTIVE_FORM_FACTOR"
)
}
internal val createPlayerRequestBodyWithModelFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
parameters()
opcodes(Opcode.OR_INT_LIT16)
custom { method, _ ->
method.indexOfModelInstruction() >= 0 &&
method.indexOfReleaseInstruction() >= 0
}
}
private fun Method.indexOfModelInstruction() =
indexOfFirstInstruction {
val reference = getReference<FieldReference>()
reference?.definingClass == "Landroid/os/Build;" &&
reference.name == "MODEL" &&
reference.type == "Ljava/lang/String;"
}
internal fun Method.indexOfReleaseInstruction(): Int =
indexOfFirstInstruction {
val reference = getReference<FieldReference>()
reference?.definingClass == "Landroid/os/Build${'$'}VERSION;" &&
reference.name == "RELEASE" &&
reference.type == "Ljava/lang/String;"
}

View File

@@ -147,6 +147,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_attributes_section"),
SwitchPreference("revanced_hide_chapters_section"),
SwitchPreference("revanced_hide_info_cards_section"),
SwitchPreference("revanced_hide_how_this_was_made_section"),
SwitchPreference("revanced_hide_key_concepts_section"),
SwitchPreference("revanced_hide_podcast_section"),
SwitchPreference("revanced_hide_transcript_section"),

View File

@@ -71,6 +71,7 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
SwitchPreference("revanced_hide_shorts_home"),
SwitchPreference("revanced_hide_shorts_subscriptions"),
SwitchPreference("revanced_hide_shorts_search"),
SwitchPreference("revanced_hide_shorts_history"),
PreferenceScreenPreference(
key = "revanced_shorts_player_screen",

View File

@@ -0,0 +1,67 @@
package app.revanced.patches.youtube.layout.player.fullscreen
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playercontrols.playerControlsPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.autoRepeatFingerprint
import app.revanced.patches.youtube.shared.autoRepeatParentFingerprint
import app.revanced.util.addInstructionsAtControlFlowLabel
@Suppress("unused")
internal val exitFullscreenPatch = bytecodePatch(
name = "Exit fullscreen mode",
description = "Adds options to automatically exit fullscreen mode when a video reaches the end."
) {
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
)
)
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
playerTypeHookPatch,
playerControlsPatch
)
// Cannot declare as top level since this patch is in the same package as
// other patches that declare same constant name with internal visibility.
@Suppress("LocalVariableName")
val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/ExitFullscreenPatch;"
execute {
addResources("youtube", "layout.player.fullscreen.exitFullscreenPatch")
PreferenceScreen.PLAYER.addPreferences(
ListPreference(
"revanced_exit_fullscreen",
summaryKey = null,
)
)
autoRepeatFingerprint.match(autoRepeatParentFingerprint.originalClassDef).method.apply {
addInstructionsAtControlFlowLabel(
implementation!!.instructions.lastIndex,
"invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->endOfVideoReached()V",
)
}
}
}

View File

@@ -2,9 +2,12 @@ package app.revanced.patches.youtube.layout.seekbar
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val fullscreenSeekbarThumbnailsFingerprint = fingerprint {
returns("Z")
@@ -34,13 +37,17 @@ internal val shortsSeekbarColorFingerprint = fingerprint {
literal { reelTimeBarPlayedColorId }
}
internal const val PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG = 45617850L
internal val playerSeekbarHandleColorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters("Landroid/content/Context;")
literal { ytStaticBrandRedId }
}
internal val playerSeekbarGradientConfigFingerprint = fingerprint {
internal val watchHistoryMenuUseProgressDrawableFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
parameters()
literal { PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG }
returns("V")
parameters("L")
literal { -1712394514 }
}
internal val lithoLinearGradientFingerprint = fingerprint {
@@ -49,6 +56,49 @@ internal val lithoLinearGradientFingerprint = fingerprint {
parameters("F", "F", "F", "F", "[I", "[F")
}
/**
* 19.49+
*/
internal val playerLinearGradientFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
parameters("I", "I", "I", "I", "Landroid/content/Context;", "I")
returns("Landroid/graphics/LinearGradient;")
opcodes(
Opcode.FILLED_NEW_ARRAY,
Opcode.MOVE_RESULT_OBJECT
)
literal { ytYoutubeMagentaColorId }
}
/**
* 19.46 - 19.47
*/
internal val playerLinearGradientLegacy1946Fingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("I", "I", "I", "I")
returns("V")
opcodes(
Opcode.FILLED_NEW_ARRAY,
Opcode.MOVE_RESULT_OBJECT
)
custom { method, _ ->
method.name == "setBounds" && method.containsLiteralInstruction(ytYoutubeMagentaColorId)
}
}
/**
* 19.25 - 19.45
*/
internal val playerLinearGradientLegacy1925Fingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters("Landroid/content/Context;")
opcodes(
Opcode.FILLED_NEW_ARRAY,
Opcode.MOVE_RESULT_OBJECT
)
literal { ytYoutubeMagentaColorId }
}
internal const val launchScreenLayoutTypeLotteFeatureFlag = 268507948L
internal val launchScreenLayoutTypeFingerprint = fingerprint {
@@ -62,3 +112,52 @@ internal val launchScreenLayoutTypeFingerprint = fingerprint {
&& method.containsLiteralInstruction(launchScreenLayoutTypeLotteFeatureFlag)
}
}
internal const val LOTTIE_ANIMATION_VIEW_CLASS_TYPE = "Lcom/airbnb/lottie/LottieAnimationView;"
internal val lottieAnimationViewSetAnimationIntFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("I")
returns("V")
custom { methodDef, classDef ->
classDef.type == LOTTIE_ANIMATION_VIEW_CLASS_TYPE && methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/airbnb/lottie/LottieAnimationView;"
&& reference.name == "isInEditMode"
} >= 0
}
}
internal val lottieAnimationViewSetAnimationStreamFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L")
returns("V")
custom { methodDef, classDef ->
classDef.type == LOTTIE_ANIMATION_VIEW_CLASS_TYPE && methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.definingClass == "Ljava/util/Set;"
&& reference.name == "add"
} >= 0 && methodDef.containsLiteralInstruction(0)
}
}
internal val lottieCompositionFactoryZipFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
parameters("Landroid/content/Context;", "Ljava/lang/String;", "Ljava/lang/String;")
returns("L")
strings(".zip", ".lottie")
}
/**
* Resolves using class found in [lottieCompositionFactoryZipFingerprint].
*
* [Original method](https://github.com/airbnb/lottie-android/blob/26ad8bab274eac3f93dccccfa0cafc39f7408d13/lottie/src/main/java/com/airbnb/lottie/LottieCompositionFactory.java#L386)
*/
internal val lottieCompositionFactoryFromJsonInputStreamFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
parameters("Ljava/io/InputStream;", "Ljava/lang/String;")
returns("L")
literal { 2 }
}

View File

@@ -3,10 +3,12 @@ package app.revanced.patches.youtube.layout.seekbar
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
@@ -14,21 +16,29 @@ import app.revanced.patches.youtube.layout.theme.lithoColorHookPatch
import app.revanced.patches.youtube.layout.theme.lithoColorOverrideHook
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_49_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
import app.revanced.util.copyXmlNode
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import app.revanced.util.inputStreamFromBundledResource
import app.revanced.util.insertFeatureFlagBooleanOverride
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import org.w3c.dom.Element
import java.io.ByteArrayInputStream
import kotlin.use
@@ -39,6 +49,10 @@ internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L
private set
internal var inlineTimeBarPlayedNotHighlightedColorId = -1L
private set
internal var ytYoutubeMagentaColorId = -1L
private set
internal var ytStaticBrandRedId = -1L
private set
internal const val splashSeekbarColorAttributeName = "splash_custom_seekbar_color"
@@ -83,6 +97,15 @@ private val seekbarColorResourcePatch = resourcePatch {
return@execute
}
ytYoutubeMagentaColorId = resourceMappings[
"color",
"yt_youtube_magenta",
]
ytStaticBrandRedId = resourceMappings[
"attr",
"ytStaticBrandRed",
]
// Add attribute and styles for splash screen custom color.
// Using a style is the only way to selectively change just the seekbar fill color.
//
@@ -182,28 +205,31 @@ val seekbarColorPatch = bytecodePatch(
sharedExtensionPatch,
lithoColorHookPatch,
seekbarColorResourcePatch,
versionCheckPatch
)
execute {
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
val registerIndex = indexOfFirstLiteralInstructionOrThrow(resourceId) + 2
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
fun MutableMethod.addColorChangeInstructions(resourceId: Long, methodName: String) {
val index = indexOfFirstLiteralInstructionOrThrow(resourceId)
val insertIndex = indexOfFirstInstructionOrThrow(index, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
registerIndex + 1,
insertIndex + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$methodName(I)I
move-result v$register
"""
invoke-static { v$colorRegister }, $EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I
move-result v$colorRegister
""",
)
}
playerSeekbarColorFingerprint.method.apply {
addColorChangeInstructions(inlineTimeBarColorizedBarPlayedColorDarkId)
addColorChangeInstructions(inlineTimeBarPlayedNotHighlightedColorId)
addColorChangeInstructions(inlineTimeBarColorizedBarPlayedColorDarkId, "getVideoPlayerSeekbarColor")
addColorChangeInstructions(inlineTimeBarPlayedNotHighlightedColorId, "getVideoPlayerSeekbarColor")
}
shortsSeekbarColorFingerprint.method.apply {
addColorChangeInstructions(reelTimeBarPlayedColorId)
addColorChangeInstructions(reelTimeBarPlayedColorId, "getVideoPlayerSeekbarColor")
}
setSeekbarClickedColorFingerprint.originalMethod.let {
@@ -229,20 +255,71 @@ val seekbarColorPatch = bytecodePatch(
// 19.25+ changes
playerSeekbarGradientConfigFingerprint.method.insertFeatureFlagBooleanOverride(
PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z"
)
playerSeekbarHandleColorFingerprint.method.apply {
addColorChangeInstructions(ytStaticBrandRedId, "getVideoPlayerSeekbarColor")
}
lithoLinearGradientFingerprint.method.addInstruction(
// If hiding feed seekbar thumbnails, then turn off the cairo gradient
// of the watch history menu items as they use the same gradient as the
// player and there is no easy way to distinguish which to use a transparent color.
if (is_19_34_or_greater) {
watchHistoryMenuUseProgressDrawableFingerprint.method.apply {
val progressIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Landroid/widget/ProgressBar;" && reference.name == "setMax"
}
val index = indexOfFirstInstructionOrThrow(progressIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->showWatchHistoryProgressDrawable(Z)Z
move-result v$register
"""
)
}
}
lithoLinearGradientFingerprint.method.addInstructions(
0,
"invoke-static/range { p4 .. p5 }, $EXTENSION_CLASS_DESCRIPTOR->setLinearGradient([I[F)V"
"""
invoke-static/range { p4 .. p5 }, $EXTENSION_CLASS_DESCRIPTOR->getLithoLinearGradient([I[F)[I
move-result-object p4
"""
)
val playerFingerprint =
if (is_19_49_or_greater) {
playerLinearGradientFingerprint
} else if (is_19_46_or_greater) {
playerLinearGradientLegacy1946Fingerprint
} else {
playerLinearGradientLegacy1925Fingerprint
}
playerFingerprint.let {
it.method.apply {
val index = it.patternMatch!!.endIndex
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getPlayerLinearGradient([I)[I
move-result-object v$register
"""
)
}
}
// region apply seekbar custom color to splash screen animation.
// Don't use the lotte splash screen layout if using custom seekbar.
if (!is_19_34_or_greater) {
return@execute // 19.25 does not have a cairo launch animation.
}
// Add development hook to force old drawable splash animation.
arrayOf(
launchScreenLayoutTypeFingerprint,
mainActivityOnCreateFingerprint
@@ -253,7 +330,7 @@ val seekbarColorPatch = bytecodePatch(
)
}
// Hook the splash animation drawable to set the a seekbar color theme.
// Hook the splash animation to set the a seekbar color.
mainActivityOnCreateFingerprint.method.apply {
val drawableIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
@@ -268,6 +345,87 @@ val seekbarColorPatch = bytecodePatch(
"invoke-static { v$drawableRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
"setSplashAnimationDrawableTheme(Landroid/graphics/drawable/AnimatedVectorDrawable;)V"
)
// Replace the Lottie animation view setAnimation(int) call.
val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/airbnb/lottie/LottieAnimationView;"
&& reference.name == setAnimationIntMethodName
}.forEach { index ->
val instruction = getInstruction<FiveRegisterInstruction>(index)
replaceInstruction(
index,
"invoke-static { v${instruction.registerC}, v${instruction.registerD} }, " +
"$EXTENSION_CLASS_DESCRIPTOR->setSplashAnimationLottie" +
"(Lcom/airbnb/lottie/LottieAnimationView;I)V"
)
}
}
// Add non obfuscated method aliases for `setAnimation(int)`
// and `setAnimation(InputStream, String)` so extension code can call them.
lottieAnimationViewSetAnimationIntFingerprint.classDef.methods.apply {
val addedMethodName = "patch_setAnimation"
val setAnimationIntName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name
add(ImmutableMethod(
LOTTIE_ANIMATION_VIEW_CLASS_TYPE,
addedMethodName,
listOf(ImmutableMethodParameter("I", null, null)),
"V",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
addInstructions(
"""
invoke-virtual { p0, p1 }, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationIntName(I)V
return-void
"""
)
})
val factoryStreamClass : CharSequence
val factoryStreamName : CharSequence
val factoryStreamReturnType : CharSequence
lottieCompositionFactoryFromJsonInputStreamFingerprint.match(
lottieCompositionFactoryZipFingerprint.originalClassDef
).originalMethod.apply {
factoryStreamClass = definingClass
factoryStreamName = name
factoryStreamReturnType = returnType
}
val setAnimationStreamName = lottieAnimationViewSetAnimationStreamFingerprint
.originalMethod.name
add(ImmutableMethod(
LOTTIE_ANIMATION_VIEW_CLASS_TYPE,
addedMethodName,
listOf(
ImmutableMethodParameter("Ljava/io/InputStream;", null, null),
ImmutableMethodParameter("Ljava/lang/String;", null, null)
),
"V",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(4),
).toMutable().apply {
addInstructions(
"""
invoke-static { p1, p2 }, $factoryStreamClass->$factoryStreamName(Ljava/io/InputStream;Ljava/lang/String;)$factoryStreamReturnType
move-result-object v0
invoke-virtual { p0, v0}, Lcom/airbnb/lottie/LottieAnimationView;->$setAnimationStreamName($factoryStreamReturnType)V
return-void
"""
)
})
}
// endregion

View File

@@ -1,66 +1,9 @@
package app.revanced.patches.youtube.layout.tablet
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.layout.formfactor.changeFormFactorPatch
const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/TabletLayoutPatch;"
val enableTabletLayoutPatch = bytecodePatch(
name = "Enable tablet layout",
description = "Adds an option to enable tablet layout.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
addResources("youtube", "layout.tablet.enableTabletLayoutPatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_tablet_layout"),
)
getFormFactorFingerprint.method.apply {
val returnIsLargeFormFactorIndex = instructions.lastIndex - 4
val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex)
addInstructionsWithLabels(
0,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getTabletLayoutEnabled()Z
move-result v0
if-nez v0, :is_large_form_factor
""",
ExternalLabel(
"is_large_form_factor",
returnIsLargeFormFactorLabel,
),
)
}
}
}
@Deprecated("Use 'Change form factor' instead.")
val enableTabletLayoutPatch = bytecodePatch {
dependsOn(changeFormFactorPatch)
}

View File

@@ -1,25 +0,0 @@
package app.revanced.patches.youtube.layout.tablet
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
internal val getFormFactorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("L")
parameters("Landroid/content/Context;", "Ljava/util/List;")
opcodes(
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.SGET_OBJECT,
Opcode.RETURN_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT,
)
strings("")
}

View File

@@ -13,6 +13,8 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.forEachChildElement
@@ -71,6 +73,7 @@ val themePatch = bytecodePatch(
dependsOn(
lithoColorHookPatch,
seekbarColorPatch,
versionCheckPatch,
resourcePatch {
dependsOn(
settingsPatch,
@@ -83,9 +86,15 @@ val themePatch = bytecodePatch(
PreferenceScreen.SEEKBAR.addPreferences(
SwitchPreference("revanced_seekbar_custom_color"),
TextPreference("revanced_seekbar_custom_color_value", inputType = InputType.TEXT_CAP_CHARACTERS),
TextPreference("revanced_seekbar_custom_color_primary", inputType = InputType.TEXT_CAP_CHARACTERS),
)
if (is_19_25_or_greater) {
PreferenceScreen.SEEKBAR.addPreferences(
TextPreference("revanced_seekbar_custom_color_accent", inputType = InputType.TEXT_CAP_CHARACTERS),
)
}
// Edit theme colors via resources.
document("res/values/colors.xml").use { document ->

View File

@@ -4,7 +4,7 @@ import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
val checkEnvironmentPatch = checkEnvironmentPatch(
internal val checkEnvironmentPatch = checkEnvironmentPatch(
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
extensionPatch = sharedExtensionPatch,
"com.google.android.youtube",

View File

@@ -1,47 +0,0 @@
package app.revanced.patches.youtube.misc.fix.cairo
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.youtube.misc.backgroundplayback.backgroundPlaybackPatch
import app.revanced.patches.youtube.misc.playservice.is_19_04_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal val disableCairoSettingsPatch = bytecodePatch(
description = "Disables Cairo Fragment from being used.",
) {
dependsOn(versionCheckPatch)
execute {
if (!is_19_04_or_greater) {
return@execute
}
/**
* <pre>
* Cairo Fragment was added since YouTube v19.04.38.
*
* Disable this for the following reasons:
* 1. [backgroundPlaybackPatch] does not activate the Minimized playback setting of Cairo Fragment.
* 2. Some patches do not yet support Cairo Fragments (ie: custom Seekbar color).
* 3. Settings preferences added by ReVanced are missing.
*
* Screenshots of the Cairo Fragment:
* <a href="https://github.com/qnblackcat/uYouPlus/issues/1468">uYouPlus#1468</a>.
*/
cairoFragmentConfigFingerprint.method.apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(CAIRO_CONFIG_LITERAL_VALUE)
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
addInstruction(
resultIndex + 1,
"const/16 v$register, 0x0",
)
}
}
}

View File

@@ -1,19 +0,0 @@
package app.revanced.patches.youtube.misc.fix.cairo
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Added in YouTube v19.04.38.
*
* When this value is true, Cairo Fragment is used.
* In this case, some of the patches may be broken, so set this value to FALSE.
*/
internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
internal val cairoFragmentConfigFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
literal { CAIRO_CONFIG_LITERAL_VALUE }
}

View File

@@ -105,3 +105,15 @@ internal val pivotBarConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
strings("com.google.android.apps.youtube.app.endpoint.flags")
}
internal val imageEnumConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
strings("TAB_ACTIVITY_CAIRO")
}
internal val setEnumMapFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
literal {
ytFillBellId
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.misc.navigation
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.PatchException
@@ -12,13 +13,16 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_35_or_greater
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
@@ -26,6 +30,8 @@ internal var imageOnlyTabResourceId = -1L
private set
internal var actionBarSearchResultsViewMicId = -1L
private set
internal var ytFillBellId = -1L
private set
private val navigationBarHookResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
@@ -33,6 +39,7 @@ private val navigationBarHookResourcePatch = resourcePatch {
execute {
imageOnlyTabResourceId = resourceMappings["layout", "image_only_tab"]
actionBarSearchResultsViewMicId = resourceMappings["layout", "action_bar_search_results_view_mic"]
ytFillBellId = resourceMappings["drawable", "yt_fill_bell_black_24"]
}
}
@@ -144,6 +151,36 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
)
}
// Fix YT bug of notification tab missing the filled icon.
if (is_19_35_or_greater) {
val cairoNotificationEnumReference = with(imageEnumConstructorFingerprint) {
val stringIndex = stringMatches!!.first().index
val cairoNotificationEnumIndex = method.indexOfFirstInstructionOrThrow(stringIndex) {
opcode == Opcode.SPUT_OBJECT
}
method.getInstruction<ReferenceInstruction>(cairoNotificationEnumIndex).reference
}
setEnumMapFingerprint.method.apply {
val enumMapIndex = indexOfFirstInstructionReversedOrThrow {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.definingClass == "Ljava/util/EnumMap;" &&
reference.name == "put" &&
reference.parameterTypes.firstOrNull() == "Ljava/lang/Enum;"
}
val instruction = getInstruction<FiveRegisterInstruction>(enumMapIndex)
addInstructions(
enumMapIndex + 1,
"""
sget-object v${instruction.registerD}, $cairoNotificationEnumReference
invoke-static { v${instruction.registerC}, v${instruction.registerD} }, $EXTENSION_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V
"""
)
}
}
}
}

View File

@@ -12,13 +12,23 @@ internal val playerTopControlsInflateFingerprint = fingerprint {
literal { controlsLayoutStub }
}
internal val playerControlsExtensionHookListenersExistFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("Z")
parameters()
custom { methodDef, classDef ->
methodDef.name == "fullscreenButtonVisibilityCallbacksExist" &&
classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}
internal val playerControlsExtensionHookFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("V")
parameters("Z")
custom { methodDef, classDef ->
methodDef.name == "fullscreenButtonVisibilityChanged" &&
classDef.type == "Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;"
classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}

View File

@@ -189,13 +189,18 @@ fun injectVisibilityCheckCall(descriptor: String) {
"invoke-static { p1 , p2 }, $descriptor->changeVisibility(ZZ)V",
)
if (!visibilityImmediateCallbacksExistModified) {
visibilityImmediateCallbacksExistModified = true
visibilityImmediateCallbacksExistMethod.returnEarly(true)
}
visibilityImmediateMethod.addInstruction(
visibilityImmediateInsertIndex++,
"invoke-static { p0 }, $descriptor->changeVisibilityImmediate(Z)V",
)
}
private const val EXTENSION_CLASS_DESCRIPTOR =
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;"
private lateinit var inflateTopControlMethod: MutableMethod
@@ -209,6 +214,9 @@ private var inflateBottomControlRegister: Int = -1
private lateinit var visibilityMethod: MutableMethod
private var visibilityInsertIndex: Int = 0
private var visibilityImmediateCallbacksExistModified = false
private lateinit var visibilityImmediateCallbacksExistMethod : MutableMethod
private lateinit var visibilityImmediateMethod: MutableMethod
private var visibilityImmediateInsertIndex: Int = 0
@@ -266,6 +274,7 @@ val playerControlsPatch = bytecodePatch(
)
}
visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistFingerprint.method
visibilityImmediateMethod = playerControlsExtensionHookFingerprint.method
// A/B test for a slightly different bottom overlay controls,

View File

@@ -41,6 +41,8 @@ var is_19_46_or_greater = false
private set
var is_19_47_or_greater = false
private set
var is_19_49_or_greater = false
private set
val versionCheckPatch = resourcePatch(
description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.",
@@ -74,5 +76,6 @@ val versionCheckPatch = resourcePatch(
is_19_43_or_greater = 244405000 <= playStoreServicesVersion
is_19_46_or_greater = 244705000 <= playStoreServicesVersion
is_19_47_or_greater = 244799000 <= playStoreServicesVersion
is_19_49_or_greater = 245005000 <= playStoreServicesVersion
}
}

View File

@@ -21,3 +21,14 @@ internal val setThemeFingerprint = fingerprint {
opcodes(Opcode.RETURN_OBJECT)
literal { appearanceStringId }
}
/**
* Added in YouTube v19.04.38.
*/
internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
internal val cairoFragmentConfigFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
literal { CAIRO_CONFIG_LITERAL_VALUE }
}

View File

@@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -17,11 +18,17 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPref
import app.revanced.patches.shared.misc.settings.settingsPatch
import app.revanced.patches.youtube.misc.check.checkEnvironmentPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.fix.cairo.disableCairoSettingsPatch
import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
import app.revanced.patches.youtube.misc.playservice.is_19_04_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import com.android.tools.smali.dexlib2.util.MethodUtil
// Used by a fingerprint() from SettingsPatch.
@@ -38,13 +45,28 @@ private val settingsResourcePatch = resourcePatch {
dependsOn(
resourceMappingPatch,
settingsPatch(
rootPreference = IntentPreference(
titleKey = "revanced_settings_title",
summaryKey = null,
intent = newIntent("revanced_settings_intent"),
) to "settings_fragment",
preferences,
),
listOf(
IntentPreference(
titleKey = "revanced_settings_title",
summaryKey = null,
intent = newIntent("revanced_settings_intent"),
) to "settings_fragment",
PreferenceCategory(
titleKey = "revanced_settings_title",
layout = "@layout/preference_group_title",
preferences = setOf(
IntentPreference(
titleKey = "revanced_settings_submenu_title",
summaryKey = null,
icon = "@drawable/revanced_settings_icon",
layout = "@layout/preference_with_icon",
intent = newIntent("revanced_settings_intent"),
)
)
) to "settings_fragment_cairo",
),
preferences
)
)
execute {
@@ -52,6 +74,7 @@ private val settingsResourcePatch = resourcePatch {
appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"]
arrayOf(
ResourceGroup("drawable", "revanced_settings_icon.xml"),
ResourceGroup("layout", "revanced_settings_with_toolbar.xml"),
).forEach { resourceGroup ->
copyResources("settings", resourceGroup)
@@ -74,7 +97,6 @@ private val settingsResourcePatch = resourcePatch {
// Remove horizontal divider from the settings Preferences
// To better match the appearance of the stock YouTube settings.
document("res/values/styles.xml").use { document ->
arrayOf(
"Theme.YouTube.Settings",
"Theme.YouTube.Settings.Dark",
@@ -94,7 +116,6 @@ private val settingsResourcePatch = resourcePatch {
// Some devices freak out if undeclared data is passed to an intent,
// and this change appears to fix the issue.
document("AndroidManifest.xml").use { document ->
val licenseElement = document.childNodes.findElementByAttributeValueOrThrow(
"android:name",
"com.google.android.libraries.social.licenses.LicenseActivity",
@@ -118,7 +139,7 @@ val settingsPatch = bytecodePatch(
sharedExtensionPatch,
settingsResourcePatch,
addResourcesPatch,
disableCairoSettingsPatch,
versionCheckPatch,
fixPlaybackSpeedWhilePlayingPatch,
// Currently there is no easy way to make a mandatory patch,
// so for now this is a dependent of this patch.
@@ -142,6 +163,12 @@ val settingsPatch = bytecodePatch(
selectable = true,
)
if (is_19_34_or_greater) {
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_restore_old_settings_menus")
)
}
PreferenceScreen.MISC.addPreferences(
TextPreference(
key = null,
@@ -150,6 +177,10 @@ val settingsPatch = bytecodePatch(
inputType = InputType.TEXT_MULTI_LINE,
tag = "app.revanced.extension.shared.settings.preference.ImportExportPreference",
),
ListPreference(
key = "revanced_language",
summaryKey = null
)
)
setThemeFingerprint.method.let { setThemeMethod ->
@@ -189,6 +220,40 @@ val settingsPatch = bytecodePatch(
licenseActivityOnCreateFingerprint.classDef.apply {
methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) }
}
// Add context override to force a specific settings language.
licenseActivityOnCreateFingerprint.classDef.apply {
val attachBaseContext = ImmutableMethod(
type,
"attachBaseContext",
listOf(ImmutableMethodParameter("Landroid/content/Context;", null, null)),
"V",
AccessFlags.PROTECTED.value,
null,
null,
MutableMethodImplementation(3),
).toMutable().apply {
addInstructions(
"""
invoke-static { p1 }, $activityHookClassDescriptor->getAttachBaseContext(Landroid/content/Context;)Landroid/content/Context;
move-result-object p1
invoke-super { p0, p1 }, $superclass->attachBaseContext(Landroid/content/Context;)V
return-void
"""
)
}
methods.add(attachBaseContext)
}
// Add setting to force cairo settings fragment on/off.
if (is_19_04_or_greater) {
cairoFragmentConfigFingerprint.method.insertFeatureFlagBooleanOverride(
CAIRO_CONFIG_LITERAL_VALUE,
"$activityHookClassDescriptor->useCairoSettingsFragment(Z)Z"
)
}
}
finalize {
@@ -224,17 +289,15 @@ object PreferenceScreen : BasePreferenceScreen() {
key = "revanced_settings_screen_03_feed",
summaryKey = null,
)
val PLAYER = Screen(
key = "revanced_settings_screen_04_player",
val GENERAL_LAYOUT = Screen(
key = "revanced_settings_screen_04_general",
summaryKey = null,
)
val GENERAL_LAYOUT = Screen(
key = "revanced_settings_screen_05_general",
val PLAYER = Screen(
key = "revanced_settings_screen_05_player",
summaryKey = null,
)
// Don't sort, as related preferences are scattered apart.
// Can use title sorting after PreferenceCategory support is added.
val SHORTS = Screen(
key = "revanced_settings_screen_06_shorts",
summaryKey = null,

View File

@@ -47,8 +47,11 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference"
),
ListPreference(
"revanced_spoof_video_streams_language",
summaryKey = null
key = "revanced_spoof_video_streams_language",
summaryKey = null,
// Language strings are declared in Setting patch.
entriesKey = "revanced_language_entries",
entryValuesKey = "revanced_language_entry_values"
),
SwitchPreference("revanced_spoof_video_streams_ios_force_avc"),
SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"),

View File

@@ -51,7 +51,7 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
}
}
val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint {
internal val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("Landroid/graphics/Bitmap;")

View File

@@ -57,7 +57,10 @@ val forceOriginalAudioPatch = bytecodePatch(
addResources("youtube", "video.audio.forceOriginalAudioPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_force_original_audio")
SwitchPreference(
key = "revanced_force_original_audio",
tag = "app.revanced.extension.youtube.settings.preference.ForceOriginalAudioSwitchPreference"
)
)
fun Method.firstFormatStreamingModelCall(

View File

@@ -0,0 +1,74 @@
package app.revanced.patches.youtube.video.hdr
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableHdrPatch;"
@Suppress("unused")
val disableHdrPatch = bytecodePatch(
name = "Disable HDR video",
description = "Adds an option to disable video HDR.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
addResources("youtube", "video.hdr.disableHdrPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_disable_hdr_video")
)
hdrCapabilityFingerprint.let {
it.originalMethod.apply {
val stringIndex = it.stringMatches!!.first().index
val navigateIndex = indexOfFirstInstructionOrThrow(stringIndex) {
val reference = getReference<MethodReference>()
reference?.parameterTypes == listOf("I", "Landroid/view/Display;") &&
reference.returnType == "Z"
}
// Modify the HDR lookup method (Method is in the same class as the fingerprint).
navigate(this).to(navigateIndex).stop().addInstructionsWithLabels(
0,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableHDRVideo()Z
move-result v0
if-nez v0, :useHdr
return v0
:useHdr
nop
"""
)
}
}
}
}

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.youtube.video.hdr
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val hdrCapabilityFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
strings(
"av1_profile_main_10_hdr_10_plus_supported",
"video/av01"
)
}

View File

@@ -17,22 +17,26 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.interaction.seekbar.disableFastForwardNoticeFingerprint
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableField
var speedUnavailableId = -1L
internal set
internal var speedUnavailableId = -1L
private set
private val customPlaybackSpeedResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
@@ -61,6 +65,7 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
recyclerViewTreeHookPatch,
customPlaybackSpeedResourcePatch,
addResourcesPatch,
versionCheckPatch
)
execute {
@@ -71,6 +76,12 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE),
)
if (is_19_25_or_greater) {
PreferenceScreen.VIDEO.addPreferences(
TextPreference("revanced_speed_tap_and_hold", inputType = InputType.NUMBER_DECIMAL),
)
}
// Replace the speeds float array with custom speeds.
speedArrayGeneratorFingerprint.method.apply {
val sizeCallIndex = indexOfFirstInstructionOrThrow { getReference<MethodReference>()?.name == "size" }
@@ -166,5 +177,27 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
// endregion
// region Custom tap and hold 2x speed.
if (is_19_25_or_greater) {
disableFastForwardNoticeFingerprint.method.apply {
val index = indexOfFirstInstructionOrThrow {
(this as? NarrowLiteralInstruction)?.narrowLiteral == 2.0f.toRawBits()
}
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->tapAndHoldSpeed()F
move-result v$register
"""
)
}
}
// endregion
}
}

View File

@@ -0,0 +1,40 @@
package app.revanced.util
import java.io.File
/**
* Comments out the non-standard <app> and <patch> tags.
*
* Previously this was done on Crowdin after pushing.
* But Crowdin preprocessing has randomly failed but still used the unmodified
* strings.xml file, which effectively deletes all patch strings from Crowdin.
*/
internal fun main(args: Array<String>) {
if (args.size != 2) {
throw RuntimeException("Exactly two arguments are required: <input_file> <output_file>")
}
val inputFilePath = args[0]
val inputFile = File(inputFilePath)
if (!inputFile.exists()) {
throw RuntimeException(
"Input file not found: $inputFilePath currentDirectory: " + File(".").canonicalPath
)
}
// Comment out the non-standard tags. Otherwise Crowdin interprets the file
// not as Android but instead a generic xml file where strings are
// identified by xml position and not key.
val content = inputFile.readText()
val tagRegex = """((<app\s+.*>)|(</app>)|(<patch\s+.*>)|(</patch>))""".toRegex()
val modifiedContent = content.replace(tagRegex, """<!-- $1 -->""")
// Write modified content to the output file (creates file if it doesn't exist).
val outputFilePath = args[1]
val outputFile = File(outputFilePath)
outputFile.parentFile?.mkdirs()
outputFile.writeText(modifiedContent)
println("Preprocessed strings.xml to: $outputFilePath")
}

View File

@@ -24,31 +24,31 @@ class StringResource(
if (value.startsWith('"') && value.endsWith('"')) {
// Raw strings allow unescaped single quote but not double quote.
if (!value.substring(1, value.length - 1).contains(Regex("(?<!\\\\)[\"]"))) {
return this;
return this
}
} else {
if (value.contains('\n')) {
// Don't throw an exception, otherwise unnoticed mistakes
// in Crowdin can cause patching failures.
// Incorrectly escaped strings still work but do not display as intended.
Logger.getLogger(StringResource.javaClass.name).severe(
Logger.getLogger(StringResource.javaClass.name).warning(
"String $name is not raw but contains encoded new line characters: $value")
}
if (!value.contains(Regex("(?<!\\\\)['\"]"))) {
return this;
return this
}
}
Logger.getLogger(StringResource.javaClass.name).severe(
Logger.getLogger(StringResource.javaClass.name).warning(
"String $name cannot contain unescaped quotes in value: $value")
return this;
return this
}
// if the string is un-formatted, explicitly add the formatted attribute
if (!formatted) setAttribute("formatted", "false")
textContent = value.validateAndroidStringEscaping();
textContent = value.validateAndroidStringEscaping()
}
companion object {

View File

@@ -136,6 +136,8 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
@@ -155,6 +157,8 @@ Second \"item\" text"</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
@@ -168,8 +172,6 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
</patch>
<patch id="layout.theme.themePatch">
@@ -213,11 +215,14 @@ Second \"item\" text"</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
</patch>
<patch id="video.hdr.disableHdrPatch">
</patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<!-- 'no auth' means no authentication -->
</patch>
</app>
<app id="twitch">

View File

@@ -136,6 +136,8 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
@@ -155,6 +157,8 @@ Second \"item\" text"</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
@@ -168,8 +172,6 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
</patch>
<patch id="layout.theme.themePatch">
@@ -213,11 +215,14 @@ Second \"item\" text"</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
</patch>
<patch id="video.hdr.disableHdrPatch">
</patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<!-- 'no auth' means no authentication -->
</patch>
</app>
<app id="twitch">

View File

@@ -33,7 +33,7 @@ Second \"item\" text"</string>
<string name="revanced_check_environment_not_near_patch_time_invalid">تاريخ إنشاء APK تالف</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_title">ReVanced</string>
<string name="revanced_settings_submenu_title">الإعدادات</string>
<string name="revanced_settings_confirm_user_dialog_title">هل ترغب في المتابعة؟</string>
<string name="revanced_settings_reset">إعادة التعيين</string>
<string name="revanced_settings_restart_title">تحديث وإعادة تشغيل</string>
@@ -43,6 +43,62 @@ Second \"item\" text"</string>
<string name="revanced_settings_import_reset">إعادة تعيين إعدادات ReVanced إلى الوضع الافتراضي</string>
<string name="revanced_settings_import_success">تم استيراد %d إعدادات</string>
<string name="revanced_settings_import_failure_parse">فشل الاستيراد: %s</string>
<string name="revanced_language_title">لغة ReVanced</string>
<string name="revanced_language_user_dialog_message">"قد تكون الترجمات لبعض اللغات مفقودة أو غير مكتملة.
لترجمة لغات جديدة، تفضل بزيارة translate.revanced.app"</string>
<string name="revanced_language_DEFAULT">لغة التطبيق</string>
<string name="revanced_language_AR">العربية</string>
<string name="revanced_language_AZ">Azerbaijani</string>
<string name="revanced_language_BG">Bulgarian</string>
<string name="revanced_language_BN">Bengali</string>
<string name="revanced_language_CA">Catalan</string>
<string name="revanced_language_CS">Czech</string>
<string name="revanced_language_DA">Danish</string>
<string name="revanced_language_DE">German</string>
<string name="revanced_language_EL">Greek</string>
<string name="revanced_language_EN">English</string>
<string name="revanced_language_ES">Spanish</string>
<string name="revanced_language_ET">Estonian</string>
<string name="revanced_language_FA">فارسى</string>
<string name="revanced_language_FI">Finnish</string>
<string name="revanced_language_FR">French - Français</string>
<string name="revanced_language_GU">Gujarati</string>
<string name="revanced_language_HI">Hindi</string>
<string name="revanced_language_HR">Croatian</string>
<string name="revanced_language_HU">Hungarian</string>
<string name="revanced_language_ID">Indonesian</string>
<string name="revanced_language_IT">Italian</string>
<string name="revanced_language_JA">Japanese</string>
<string name="revanced_language_KK">Kazakh</string>
<string name="revanced_language_KO">Korean</string>
<string name="revanced_language_LT">Lithuanian</string>
<string name="revanced_language_LV">Latvian</string>
<string name="revanced_language_MK">Macedonian</string>
<string name="revanced_language_MN">Mongolian</string>
<string name="revanced_language_MR">Marathi</string>
<string name="revanced_language_MS">Malay</string>
<string name="revanced_language_MY">Burmese</string>
<string name="revanced_language_NL">Dutch</string>
<string name="revanced_language_OR">Odia</string>
<string name="revanced_language_PA">Punjabi</string>
<string name="revanced_language_PL">Polish</string>
<string name="revanced_language_PT">Portugese</string>
<string name="revanced_language_RO">Romanian</string>
<string name="revanced_language_RU">Russian - Русский</string>
<string name="revanced_language_SK">Slovak</string>
<string name="revanced_language_SL">Slovene</string>
<string name="revanced_language_SR">Serbian</string>
<string name="revanced_language_SV">Swedish</string>
<string name="revanced_language_SW">Swahili</string>
<string name="revanced_language_TA">Tamil</string>
<string name="revanced_language_TE">Telugu</string>
<string name="revanced_language_TH">Thai</string>
<string name="revanced_language_TR">Turkish</string>
<string name="revanced_language_UK">Ukrainian</string>
<string name="revanced_language_UR">Urdu</string>
<string name="revanced_language_VI">Vietnamese</string>
<string name="revanced_language_ZH">Chinese</string>
<string name="revanced_pref_import_export_title">استيراد / تصدير</string>
<string name="revanced_pref_import_export_summary">استيراد / تصدير إعدادات ReVanced</string>
<!-- Settings about dialog. -->
@@ -77,12 +133,15 @@ Second \"item\" text"</string>
<string name="revanced_settings_screen_01_ads_title">الإعلانات</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">مُصغَّرات فيديو بديلة</string>
<string name="revanced_settings_screen_03_feed_title">الموجز</string>
<string name="revanced_settings_screen_04_player_title">المشغل</string>
<string name="revanced_settings_screen_05_general_title">التصميم العام</string>
<string name="revanced_settings_screen_04_general_title">عام</string>
<string name="revanced_settings_screen_05_player_title">المشغل</string>
<string name="revanced_settings_screen_07_seekbar_title">شريط تقدم الفيديو</string>
<string name="revanced_settings_screen_08_swipe_controls_title">التحكم عن طريق إيماءة التمرير</string>
<string name="revanced_settings_screen_11_misc_title">إعدادات متنوعة</string>
<string name="revanced_settings_screen_11_misc_title">خيارات متنوعة</string>
<string name="revanced_settings_screen_12_video_title">الفيديو</string>
<string name="revanced_restore_old_settings_menus_title">استعادة قوائم الإعدادات القديمة</string>
<string name="revanced_restore_old_settings_menus_summary_on">يتم عرض قوائم الإعدادات القديمة</string>
<string name="revanced_restore_old_settings_menus_summary_off">لا يتم عرض قوائم الإعدادات القديمة</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">تعطيل تشغيل فيديوهات Shorts في الخلفية</string>
@@ -109,9 +168,9 @@ Second \"item\" text"</string>
لن يتم إعلامك بأي أخطاء غير متوقعة."</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_disable_like_subscribe_glow_title">تعطيل توهج زر أعجبني / اشتراك</string>
<string name="revanced_disable_like_subscribe_glow_summary_on">لن يتوهج زر أعجبني واشتراك عند ذكره</string>
<string name="revanced_disable_like_subscribe_glow_summary_off">أعجبني واشتراك سوف يتوهج عند الإشارة</string>
<string name="revanced_disable_like_subscribe_glow_title">تعطيل توهُّج زِرّي أعجبني واشتراك</string>
<string name="revanced_disable_like_subscribe_glow_summary_on">لن يتوهَّج زرّي أعجبني واشتراك عند ذكرهما</string>
<string name="revanced_disable_like_subscribe_glow_summary_off">سيتوهَّج زرّي أعجبني واشتراك عند ذكرهما</string>
<string name="revanced_hide_album_cards_title">إخفاء بطاقات الألبوم</string>
<string name="revanced_hide_album_cards_summary_on">تم إخفاء بطاقات الألبوم</string>
<string name="revanced_hide_album_cards_summary_off">يتم عرض بطاقات الألبوم</string>
@@ -138,12 +197,12 @@ Second \"item\" text"</string>
<string name="revanced_hide_join_membership_button_summary_on">تم إخفاء الزر</string>
<string name="revanced_hide_join_membership_button_summary_off">يتم عرض الزر</string>
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<string name="revanced_hide_for_you_shelf_title">إخفاء رف \"من أجلك\" في صفحة القناة</string>
<string name="revanced_hide_for_you_shelf_title">إخفاء رف \"لـك\" في صفحة القناة</string>
<string name="revanced_hide_for_you_shelf_summary_on">تم إخفاء الرف</string>
<string name="revanced_hide_for_you_shelf_summary_off">يتم عرض الرف</string>
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<string name="revanced_hide_notify_me_button_title">إخفاء زر \'نبهني\'</string>
<string name="revanced_hide_notify_me_button_title">إخفاء زر \'تنبيهي\'</string>
<string name="revanced_hide_notify_me_button_summary_on">تم إخفاء الزر</string>
<string name="revanced_hide_notify_me_button_summary_off">يتم عرض الزر</string>
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
@@ -164,9 +223,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_chips_shelf_title">إخفاء رف الشرائح</string>
<string name="revanced_hide_chips_shelf_summary_on">تم إخفاء رف الشرائح</string>
<string name="revanced_hide_chips_shelf_summary_off">يتم عرض رف الشرائح</string>
<string name="revanced_hide_expandable_chip_title">إخفاء الشريحة القابلة للتوسيع تحت مقاطع الفيديو</string>
<string name="revanced_hide_expandable_chip_summary_on">تم إخفاء الرقائق القابلة للتوسيع</string>
<string name="revanced_hide_expandable_chip_summary_off">يتم عرض الرقائق القابلة للتوسيع</string>
<string name="revanced_hide_expandable_chip_title">إخفاء الشريحة القابلة للتوسيع تحت الفيديوهات</string>
<string name="revanced_hide_expandable_chip_summary_on">تم إخفاء الشرائح القابلة للتوسيع</string>
<string name="revanced_hide_expandable_chip_summary_off">يتم عرض الشرائح القابلة للتوسيع</string>
<string name="revanced_hide_community_posts_title">إخفاء مشاركات المجتمع</string>
<string name="revanced_hide_community_posts_summary_on">تم إخفاء مشاركات المجتمع</string>
<string name="revanced_hide_community_posts_summary_off">يتم عرض مشاركات المجتمع</string>
@@ -207,8 +266,8 @@ Second \"item\" text"</string>
<string name="revanced_hide_quick_actions_summary_on">تم إخفاء الإجراءات السريعة</string>
<string name="revanced_hide_quick_actions_summary_off">يتم عرض الإجراءات السريعة</string>
<string name="revanced_hide_related_videos_title">إخفاء الفيديوهات ذات الصلة في الإجراءات السريعة</string>
<string name="revanced_hide_related_videos_summary_on">تم إخفاء مقاطع الفيديو ذات الصلة</string>
<string name="revanced_hide_related_videos_summary_off">يتم عرض مقاطع الفيديو ذات الصلة</string>
<string name="revanced_hide_related_videos_summary_on">تم إخفاء الفيديوهات ذات الصلة</string>
<string name="revanced_hide_related_videos_summary_off">يتم عرض الفيديوهات ذات الصلة</string>
<string name="revanced_hide_image_shelf_title">إخفاء رفوف الصور في نتائج البحث</string>
<string name="revanced_hide_image_shelf_summary_on">تم إخفاء رفوف الصورة</string>
<string name="revanced_hide_image_shelf_summary_off">يتم عرض رفوف الصورة</string>
@@ -227,6 +286,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_chapters_section_title">إخفاء قسم الفصول</string>
<string name="revanced_hide_chapters_section_summary_on">تم إخفاء قسم الفصول</string>
<string name="revanced_hide_chapters_section_summary_off">يتم عرض قسم الفصول</string>
<string name="revanced_hide_how_this_was_made_section_title">إخفاء قسم \"كيف تم إنشاء هذا المحتوى\"</string>
<string name="revanced_hide_how_this_was_made_section_summary_on">تم إخفاء قسم \"كيف تم إنشاء هذا المحتوى\"</string>
<string name="revanced_hide_how_this_was_made_section_summary_off">يتم عرض قسم \"كيف تم إنشاء هذا المحتوى\"</string>
<string name="revanced_hide_podcast_section_title">إخفاء قسم \'استكشاف البودكاست\'</string>
<string name="revanced_hide_podcast_section_summary_on">تم إخفاء قسم \"استكشاف البودكاست\"</string>
<string name="revanced_hide_podcast_section_summary_off">يتم عرض قسم \"استكشاف البودكاست\"</string>
@@ -242,7 +304,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_description_components_screen_title">وصف الفيديو</string>
<string name="revanced_hide_description_components_screen_summary">إخفاء أو عرض مكونات وصف الفيديو</string>
<string name="revanced_hide_filter_bar_screen_title">شريط التصفية</string>
<string name="revanced_hide_filter_bar_screen_summary">إخفاء شريط التصفية أو عرضه في الموجز والبحث ومقاطع الفيديو ذات الصلة</string>
<string name="revanced_hide_filter_bar_screen_summary">إخفاء شريط التصفية أو عرضه في الموجز والبحث الفيديوهات ذات الصلة</string>
<string name="revanced_hide_filter_bar_feed_in_feed_title">إخفاء في الموجز</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_on">مخفي في الموجز</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_off">يعرض في الموجز</string>
@@ -293,15 +355,15 @@ Second \"item\" text"</string>
<string name="revanced_custom_filter_toast_invalid_syntax">فلتر مخصص غير صالح: %s</string>
<string name="revanced_hide_keyword_content_screen_title">إخفاء محتوى الكلمات الرئيسية</string>
<string name="revanced_hide_keyword_content_screen_summary">إخفاء فيديوهات البحث والموجز باستخدام فلاتر الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_home_title">إخفاء مقاطع فيديو الصفحة الرئيسية بواسطة الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_home_summary_on">تتم تصفية مقاطع الفيديو في علامة التبويب \"الصفحة الرئيسية\" حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_home_summary_off">لا تتم تصفية مقاطع الفيديو في علامة التبويب \"الصفحة الرئيسية\" حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_subscriptions_title">إخفاء مقاطع الفيديو الخاصة بالاشتراك عن طريق الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_on">يتم تصفية الفيديوهات في علامة التبويب الاشتراكات حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">لا يتم تصفية الفيديوهات في علامة التبويب الاشتراكات حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_home_title">إخفاء فيديوهات الصفحة الرئيسية بواسطة الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_home_summary_on">تتم تصفية الفيديوهات في علامة التبويب \"الصفحة الرئيسية\" حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_home_summary_off">لا تتم تصفية الفيديوهات في علامة التبويب \"الصفحة الرئيسية\" حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_search_title">إخفاء نتائج البحث عن طريق الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_search_summary_on">يتم تصفية نتائج البحث حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_search_summary_off">لا يتم تصفية نتائج البحث حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_subscriptions_title">إخفاء الفيديوهات الخاصة بالاشتراك عن طريق الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_on">يتم تصفية الفيديوهات في علامة التبويب الاشتراكات حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">لا يتم تصفية الفيديوهات في علامة التبويب الاشتراكات حسب الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_phrases_title">الكلمات المفتاحية المراد إخفاؤها</string>
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
@@ -348,6 +410,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_products_banner_title">إخفاء لافتة لعرض المنتجات</string>
<string name="revanced_hide_products_banner_summary_on">تم إخفاء البانر</string>
<string name="revanced_hide_products_banner_summary_off">يتم عرض البانر</string>
<string name="revanced_hide_end_screen_store_banner_title">إخفاء لافتة شاشة المتجر النهائية</string>
<string name="revanced_hide_end_screen_store_banner_summary_on">تم إخفاء لافتة المتجر</string>
<string name="revanced_hide_end_screen_store_banner_summary_off">يتم عرض لافتة المتجر</string>
<string name="revanced_hide_player_store_shelf_title">إخفاء رف مشغل التسوق</string>
<string name="revanced_hide_player_store_shelf_summary_on">تم إخفاء رفوف التسوق</string>
<string name="revanced_hide_player_store_shelf_summary_off">يتم عرض رفوف التسوق</string>
@@ -445,6 +510,9 @@ Second \"item\" text"</string>
<string name="revanced_swipe_overlay_background_alpha_summary">قيمة شفافية خلفية واجهة التمرير</string>
<string name="revanced_swipe_threshold_title">مقدار حد التمرير</string>
<string name="revanced_swipe_threshold_summary">الحد الأدنى من التمرير قبل اكتشاف الإيماءة</string>
<string name="revanced_swipe_change_video_title">تمكين إيماءة التمرير لتغيير الفيديو</string>
<string name="revanced_swipe_change_video_summary_on">سيؤدي التمرير في وضع ملء الشاشة إلى التغيير للفيديو التالي/السابق</string>
<string name="revanced_swipe_change_video_summary_off">لن يؤدي التمرير في وضع ملء الشاشة إلى التغيير للفيديو التالي/السابق</string>
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_auto_captions_title">تعطيل التَّرْجَمَة التلقائية</string>
@@ -516,14 +584,14 @@ Second \"item\" text"</string>
<string name="revanced_hide_navigation_button_labels_summary_on">تم إخفاء التسميات</string>
<string name="revanced_hide_navigation_button_labels_summary_off">يتم عرض التسميات</string>
<string name="revanced_disable_translucent_status_bar_title">تعطيل شريط الحالة الشفاف</string>
<string name="revanced_disable_translucent_status_bar_summary_on">شريط الحالة غير معتمة</string>
<string name="revanced_disable_translucent_status_bar_summary_off">شريط الحالة غير شفافة أو عميقة</string>
<string name="revanced_disable_translucent_navigation_bar_light_title">تعطيل شريط التنقل الشفاف الفاتح</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">شريط التنقل في الوضع الفاتح معتم</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">يكون شريط التنقل في الوضع الفاتح معتمًا أو نصف شفاف</string>
<string name="revanced_disable_translucent_navigation_bar_dark_title">تعطيل الشريط الداكنة</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_on">شريط التنقل في الوضع الداكن معتم</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_off">شريط التطبيق المصوري الشفاف غير عميقة أو عميقة</string>
<string name="revanced_disable_translucent_status_bar_summary_on">شريط الحالة غير شفاف</string>
<string name="revanced_disable_translucent_status_bar_summary_off">شريط الحالة معتم أو شفاف</string>
<string name="revanced_disable_translucent_navigation_bar_light_title">تعطيل الشريط الشفاف الفاتح</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">شريط التنقل في الوضع الفاتح غير شفاف</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">شريط التنقل في الوضع الفاتح معتم او شفاف</string>
<string name="revanced_disable_translucent_navigation_bar_dark_title">تعطيل الشريط الشفاف الداكن</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_on">شريط التنقل في الوضع الداكن غير شفاف</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_off">شريط التنقل في الوضع الداكن معتم او شفاف</string>
</patch>
<patch id="layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch">
<string name="revanced_hide_player_flyout_title">القائمة المنبثقة</string>
@@ -628,15 +696,18 @@ Second \"item\" text"</string>
<string name="revanced_shorts_player_screen_summary">إخفاء أو عرض المكونات في مشغل Shorts</string>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">إخفاء Shorts في موجز الصفحة الرئيسية</string>
<string name="revanced_hide_shorts_home_summary_on">تم إخفاء Shorts في موجز الصفحة الرئيسية</string>
<string name="revanced_hide_shorts_home_summary_off">يتم عرض Shorts في موجز الصفحة الرئيسية</string>
<string name="revanced_hide_shorts_home_summary_on">مخفية في الصفحة الرئيسية والفيديوهات ذات الصلة</string>
<string name="revanced_hide_shorts_home_summary_off">تعرض في الصفحة الرئيسية والفيديوهات ذات الصلة</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">إخفاء Shorts في موجز الاشتراكات</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">تم إخفاء Shorts في موجز الاشتراكات</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">يتم عرض Shorts في موجز الاشتراكات</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">مخفية في موجز الاشتراكات</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">تعرض في موجز الاشتراكات</string>
<string name="revanced_hide_shorts_search_title">إخفاء Shorts في نتائج البحث</string>
<string name="revanced_hide_shorts_search_summary_on">تم إخفاء Shorts في نتائج البحث</string>
<string name="revanced_hide_shorts_search_summary_off">يتم عرض Shorts في نتائج البحث</string>
<string name="revanced_hide_shorts_search_summary_on">مخفية في نتائج البحث</string>
<string name="revanced_hide_shorts_search_summary_off">تعرض في نتائج البحث</string>
<string name="revanced_hide_shorts_history_title">إخفاء Shorts في سجل المشاهدة</string>
<string name="revanced_hide_shorts_history_summary_on">مخفية في سجل المشاهدة</string>
<string name="revanced_hide_shorts_history_summary_off">تعرض في سجل المشاهدة</string>
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_join_button_title">إخفاء زر الانضمام</string>
<string name="revanced_hide_shorts_join_button_summary_on">تم إخفاء زر الانضمام</string>
@@ -681,7 +752,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_stickers_title">إخفاء الملصقات</string>
<string name="revanced_hide_shorts_stickers_summary_on">تم إخفاء الملصقات</string>
<string name="revanced_hide_shorts_stickers_summary_off">يتم عرض الملصقات</string>
<string name="revanced_hide_shorts_like_fountain_title">إخفاء فوران أعجبني</string>
<string name="revanced_hide_shorts_like_fountain_title">إخفاء التأثير الفوّار لـزر أعجبني</string>
<string name="revanced_hide_shorts_like_fountain_summary_on">تم إخفاء التأثير الفوّار لـزر أعجبني</string>
<string name="revanced_hide_shorts_like_fountain_summary_off">يتم عرض التأثير الفوّار لـزر أعجبني</string>
<string name="revanced_hide_shorts_like_button_title">إخفاء زر أعجبني</string>
@@ -738,8 +809,15 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_popup_panels_summary_on">تم إخفاء لوحات المشغل المنبثقة</string>
<string name="revanced_hide_player_popup_panels_summary_off">يتم عرض لوحات المشغل المنبثقة</string>
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
<string name="revanced_exit_fullscreen_title">الخروج من وضع ملء الشاشة عند انتهاء الفيديو</string>
<string name="revanced_exit_fullscreen_entry_1">معطل</string>
<string name="revanced_exit_fullscreen_entry_2">بالطول</string>
<string name="revanced_exit_fullscreen_entry_3">بالعرض</string>
<string name="revanced_exit_fullscreen_entry_4">بالطول والعرض</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">فتح مقاطع الفيديو في ملء الشاشة</string>
<string name="revanced_open_videos_fullscreen_portrait_title">فتح الفيديوهات في ملء الشاشة</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">يتم فتح الفيديوهات في وضع ملء الشاشة</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_off">لا يتم فتح الفيديوهات في وضع ملء الشاشة</string>
</patch>
@@ -758,12 +836,12 @@ Second \"item\" text"</string>
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">أعد تحميل الفيديو للتصويت بـ Return YouTube Dislike</string>
<string name="revanced_ryd_enable_summary_on">يتم عرض لم يعجبني</string>
<string name="revanced_ryd_enable_summary_off">لا يتم عرض لم يعجبني</string>
<string name="revanced_ryd_shorts_title">عرض لم يعجني في مقاطع Shorts</string>
<string name="revanced_ryd_shorts_summary_on">يتم عرض لم يعجني في مقاطع Shorts</string>
<string name="revanced_ryd_shorts_title">عرض لم يعجني في فيديوهات Shorts</string>
<string name="revanced_ryd_shorts_summary_on">يتم عرض لم يعجني في فيديوهات Shorts</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">"إبداءات لم يعجبني التي تظهر على فيديوهات Shorts
التقييد: قد لا تظهر إبداءات لم يعجبني في وضع التصفح المتخفي"</string>
<string name="revanced_ryd_shorts_summary_off">تم إخفاء لم يعجني في مقاطع Shorts</string>
<string name="revanced_ryd_shorts_summary_off">تم إخفاء لم يعجني في فيديوهات Shorts</string>
<string name="revanced_ryd_dislike_percentage_title">لم يعجبني كــ نسبة مئوية</string>
<string name="revanced_ryd_dislike_percentage_summary_on">يعرض عدد لم يعجبني كـ نسبة مئوية</string>
<string name="revanced_ryd_dislike_percentage_summary_off">يعرض عدد لم يعجبني كـ رَقَم</string>
@@ -816,7 +894,7 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.sponsorblock.sponsorBlockResourcePatch">
<string name="revanced_sb_enable_sb">تمكين SponsorBlock</string>
<string name="revanced_sb_enable_sb_sum">مانِع الرُعَاة هو نظام جماعي لتخطي الأجزاء المُمِلَّة في مقاطع YouTube</string>
<string name="revanced_sb_enable_sb_sum">SponsorBlock مانِع الرُعَاة هو نظام جماعي لتخطي الأجزاء المُمِلَّة في مقاطع YouTube</string>
<string name="revanced_sb_appearance_category">المظهر</string>
<string name="revanced_sb_enable_voting">عرض زر التصويت</string>
<string name="revanced_sb_enable_voting_sum_on">يتم عرض زر التصويت على المقطع</string>
@@ -881,7 +959,7 @@ Second \"item\" text"</string>
<string name="revanced_sb_segments_sponsor">الراعي</string>
<string name="revanced_sb_segments_sponsor_sum">الترويج المدفوع الأجر، والإحالات المدفوعة الأجر والإعلانات المباشرة. ليس للترويج الذاتي أو لصراعات مجانية للقضايا/المبدعين/المواقع الإلكترونية/المنتجات التي يحبون الحصول عليها</string>
<string name="revanced_sb_segments_selfpromo">ترويج شخصي/غير مدفوع الأجر</string>
<string name="revanced_sb_segments_selfpromo_sum">شبيهة بـ \"الراعي\" باستثناء ما يتعلق بالإعلانات غير المدفوعة الأجر أو الذاتية. ويشمل ذلك أقسام عن السلع أو التبرعات أو المعلومات المتعلقة بمن تعاونوا مع ناشر المحتوى</string>
<string name="revanced_sb_segments_selfpromo_sum">مشابهة لـ \"الراعي\" باستثناء ما يتعلق بالإعلانات غير المدفوعة الأجر أو الذاتية. ويشمل ذلك أقسام عن السلع أو التبرعات أو المعلومات المتعلقة بمن تعاونوا مع ناشر المحتوى</string>
<string name="revanced_sb_segments_interaction">تذكير بالتفاعل (اشتراك)</string>
<string name="revanced_sb_segments_interaction_sum">تذكير قصير للإعجاب أو الاشتراك أو المتابعة في منتصف المحتوى. إذا كانت طويلة أو تتعلق بشيء محدد، فيجب أن تكون خاضعة للترويج الشخصي بدلاً من ذلك</string>
<string name="revanced_sb_segments_highlight">الأبرز</string>
@@ -895,7 +973,7 @@ Second \"item\" text"</string>
<string name="revanced_sb_segments_filler">خارج الموضوع/النكات</string>
<string name="revanced_sb_segments_filler_sum">تم إضافة مشاهد ملتقطة خارج الموضوع أو الفكاهة التي ليست مطلوبة لفهم المحتوى الرئيسي للفيديو. لا تتضمن مقاطع توفر تَعبِير أو تفاصيل الخلفية</string>
<string name="revanced_sb_segments_nomusic">الموسيقى: مقطع غير موسيقي</string>
<string name="revanced_sb_segments_nomusic_sum">فقط للاستخدام في مقاطع الفيديو الموسيقية. أقسام مقاطع الفيديو الموسيقية بدون موسيقى، والتي لم يتم تغطيتها بالفعل من قبل فئة أخرى</string>
<string name="revanced_sb_segments_nomusic_sum">فقط للاستخدام في المقاطع الموسيقية. أقسام المقاطع الموسيقية بدون موسيقى، والتي لم يتم تغطيتها بالفعل من قبل فئة أخرى</string>
<string name="revanced_sb_skip_button_compact">تخطي</string>
<string name="revanced_sb_skip_button_compact_highlight">الأبرز</string>
<string name="revanced_sb_skip_button_sponsor">تخطي الراعي</string>
@@ -1006,6 +1084,23 @@ Second \"item\" text"</string>
<string name="revanced_sb_reset">إعادة التعيين</string>
<string name="revanced_sb_about">لمحة</string>
<string name="revanced_sb_about_api_sum">يتم توفير البيانات بواسطة SponsorBlock API. انقر هنا لمعرفة المزيد ومشاهدة التنزيلات لمنصات أخرى</string>
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
<string name="revanced_change_form_factor_title">شكل نموذج التخطيط</string>
<string name="revanced_change_form_factor_entry_1">الافتراضي</string>
<string name="revanced_change_form_factor_entry_2">الجوّال</string>
<string name="revanced_change_form_factor_entry_3">الجهاز اللوحي</string>
<string name="revanced_change_form_factor_entry_4">Automotive</string>
<string name="revanced_change_form_factor_user_dialog_message">"تتضمن التغييرات:
تخطيط الجهاز اللوحي
• إخفاء منشورات المجتمع
تخطيط Automotive
• إخفاء قائمة سجل المشاهدة
• استعادة علامة التبويب \"استكشاف\"
• فتح فيديوهات Shorts في المشغل العادي
• تنظيم الخلاصة حسب الموضوعات والقناة"</string>
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<string name="revanced_spoof_app_version_title">خِداع إصدار التطبيق</string>
@@ -1020,6 +1115,7 @@ Second \"item\" text"</string>
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<string name="revanced_spoof_app_version_target_title">الهدف من تغيير إصدار التطبيق</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - استعادة أيقونات مشغل Shorts القديمة</string>
<string name="revanced_spoof_app_version_target_entry_2">19.26.42 - استعادة أيقونات التنقل وشريط الأدوات القديمة</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_legacy_entry_1">18.33.40 - استعادة RYD على Shorts بوضع التخفي</string>
<string name="revanced_spoof_app_version_target_legacy_entry_2">18.20.39 - استعادة قائمة سرعة الفيديو العريضة &amp; الجودة</string>
@@ -1050,10 +1146,10 @@ Second \"item\" text"</string>
<string name="revanced_disable_resuming_shorts_player_summary_off">سيتم استئناف تشغيل مشغل Shorts عند بدء تشغيل التطبيق</string>
</patch>
<patch id="layout.shortsplayer.shortsPlayerTypePatch">
<string name="revanced_shorts_player_type_title">فتح مقاطع الـShorts باستخدام</string>
<string name="revanced_shorts_player_type_title">فتح فيديوهات Shorts باستخدام</string>
<string name="revanced_shorts_player_type_shorts">مشغل Shorts</string>
<string name="revanced_shorts_player_type_regular_player">مشغل عادي</string>
<string name="revanced_shorts_player_type_regular_player_fullscreen">شاشة كاملة - مشغل عادي</string>
<string name="revanced_shorts_player_type_regular_player">المشغل العادي</string>
<string name="revanced_shorts_player_type_regular_player_fullscreen">المشغل العادي في وضع ملء الشاشة</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
<string name="revanced_shorts_autoplay_title">التشغيل التلقائي لفيديوهات Shorts</string>
@@ -1063,12 +1159,6 @@ Second \"item\" text"</string>
<string name="revanced_shorts_autoplay_background_summary_on">سيتم تشغيل فيديوهات Shorts تلقائيًا في الخلفية</string>
<string name="revanced_shorts_autoplay_background_summary_off">سيتم تكرار فيديوهات Shorts في الخلفية</string>
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">تمكين تصميم الجهاز اللوحي</string>
<string name="revanced_tablet_layout_summary_on">تم تمكين تصميم الجهاز اللوحي</string>
<string name="revanced_tablet_layout_summary_off">تم تعطيل تصميم الجهاز اللوحي</string>
<string name="revanced_tablet_layout_user_dialog_message">لا تظهر منشورات المجتمع على تخطيطات الجهاز اللوحي</string>
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">المشغل المصغر</string>
<string name="revanced_miniplayer_screen_summary">تغيير نمط المشغل المصغر داخل التطبيق</string>
@@ -1129,8 +1219,10 @@ Second \"item\" text"</string>
<string name="revanced_seekbar_custom_color_title">تمكين لون شريط تقدم الفيديو المخصص</string>
<string name="revanced_seekbar_custom_color_summary_on">يتم عرض لون شريط تقدم الفيديو المخصص</string>
<string name="revanced_seekbar_custom_color_summary_off">يتم عرض لون شريط تقدم الفيديو الاصلي</string>
<string name="revanced_seekbar_custom_color_value_title">لون شريط تقدم الفيديو المخصص</string>
<string name="revanced_seekbar_custom_color_value_summary">لون شريط التقدم</string>
<string name="revanced_seekbar_custom_color_primary_title">لون شريط تقدم الفيديو المخصص</string>
<string name="revanced_seekbar_custom_color_primary_summary">لون شريط التقدم</string>
<string name="revanced_seekbar_custom_color_accent_title">لون مميز لشريط التقدم المخصص</string>
<string name="revanced_seekbar_custom_color_accent_summary">اللون المميز لشريط التقدم</string>
<string name="revanced_seekbar_custom_color_invalid">لون شريط التقدم غير صالح</string>
</patch>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
@@ -1166,7 +1258,7 @@ Second \"item\" text"</string>
<string name="revanced_alt_thumbnail_stills_about_title">لقطات الفيديو الثابتة</string>
<string name="revanced_alt_thumbnail_stills_about_summary">يتم التقاط اللقطات الثابتة من بداية/وسط/نهاية كل فيديو. هذه الصور مدمجة في YouTube ولا يتم استخدام أي واجهة برمجة تطبيقات خارجية</string>
<string name="revanced_alt_thumbnail_stills_fast_title">استخدم اللقطات الثابتة السريعة</string>
<string name="revanced_alt_thumbnail_stills_fast_summary_on">استخدام اللقطات متوسطة الجودة. سيتم تحميل المُصغَّرات بشكل أسرع، ولكن البث المباشر و المقاطع التي لم يتم إصدارها أو القديمة جدًا قد تعرض مُصغَّرات فارغة</string>
<string name="revanced_alt_thumbnail_stills_fast_summary_on">استخدام اللقطات متوسطة الجودة. سيتم تحميل المُصغَّرات بشكل أسرع، ولكن البث المباشر و الفيديوهات التي لم يتم إصدارها أو القديمة جدًا قد تعرض مُصغَّرات فارغة</string>
<string name="revanced_alt_thumbnail_stills_fast_summary_off">استخدام لقطات الفيديو الثابتة بجودة عالية</string>
<string name="revanced_alt_thumbnail_stills_time_title">وقت الفيديو لأخذ اللقطات الثابتة منه</string>
<string name="revanced_alt_thumbnail_stills_time_entry_1">بداية الفيديو</string>
@@ -1232,12 +1324,13 @@ Second \"item\" text"</string>
<string name="revanced_force_original_audio_title">فرض الصوت الأصلي</string>
<string name="revanced_force_original_audio_summary_on">استخدام الصوت الأصلي</string>
<string name="revanced_force_original_audio_summary_off">استخدام الصوت الافتراضي</string>
<string name="revanced_force_original_audio_not_available">لاستخدام هذه الميزة، قم بتغيير محاكاة بث المحتوى إلى نوع العميل iOS</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
<string name="revanced_video_quality_default_entry_1">تلقائي</string>
<string name="revanced_remember_video_quality_last_selected_title">تذكر تغييرات جودة الفيديو</string>
<string name="revanced_remember_video_quality_last_selected_summary_on">تنطبق تغييرات الجودة على جميع مقاطع الفيديو</string>
<string name="revanced_remember_video_quality_last_selected_summary_on">تنطبق تغييرات الجودة على جميع الفيديوهات</string>
<string name="revanced_remember_video_quality_last_selected_summary_off">تنطبق تغييرات الجودة على الفيديو الحالي فقط</string>
<string name="revanced_video_quality_default_wifi_title">جودة الفيديو الافتراضية على شبكة Wi-Fi</string>
<string name="revanced_video_quality_default_mobile_title">جودة الفيديو الافتراضية على شبكة الجوَّال</string>
@@ -1259,6 +1352,8 @@ Second \"item\" text"</string>
<string name="revanced_custom_playback_speeds_invalid">يجب أن تكون سرعات التشغيل المخصصة أقل من %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">سرعة التشغيل المخصصة غير صالحة</string>
<string name="revanced_custom_playback_speeds_auto">تلقائي</string>
<string name="revanced_speed_tap_and_hold_title">سرعة النقر مع الاستمرار المخصصة</string>
<string name="revanced_speed_tap_and_hold_summary">سرعة التشغيل بين 0-8</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
<string name="revanced_remember_playback_speed_last_selected_title">تذكر التغيرات في سرعة التشغيل</string>
@@ -1267,6 +1362,11 @@ Second \"item\" text"</string>
<string name="revanced_playback_speed_default_title">سرعة التشغيل الافتراضية</string>
<string name="revanced_remember_playback_speed_toast">تغيير السرعة الافتراضية إلى: %s</string>
</patch>
<patch id="video.hdr.disableHdrPatch">
<string name="revanced_disable_hdr_video_title">تعطيل فيديو HDR</string>
<string name="revanced_disable_hdr_video_summary_on">تم تعطيل فيديو HDR</string>
<string name="revanced_disable_hdr_video_summary_off">تم تمكين فيديو HDR</string>
</patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch">
<string name="revanced_restore_old_video_quality_menu_title">استعادة قائمة جودة الفيديو القديمة</string>
<string name="revanced_restore_old_video_quality_menu_summary_on">يتم عرض قائمة جودة الفيديو القديمة</string>
@@ -1287,62 +1387,28 @@ Second \"item\" text"</string>
قد لا يعمل تشغيل الفيديو"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">إيقاف تشغيل هذا الإعداد قد يسبب مشاكل في تشغيل الفيديو.</string>
<string name="revanced_spoof_video_streams_client_type_title">العميل الافتراضي</string>
<string name="revanced_spoof_video_streams_about_title">آثار جانبية وهمية</string>
<string name="revanced_spoof_video_streams_about_summary">"• القائمة الصوتية مفقودة
• الصوت الثابت غير متوفر
<!-- 'no auth' means no authentication -->
<string name="revanced_spoof_video_streams_client_type_android_vr_no_auth">Android VR (بدون مصادقة)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">فرض iOS AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">يتم فرض ترميز فيديو على AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">يتم تحديد ترميز الفيديو تلقائيًا</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"قد يؤدي تمكين هذا إلى تحسين عمر البطارية وإصلاح تقطيع التشغيل.
AVC لديه حد أقصى للدقة 1080p، لا يتوفر ترميز الصوت Opus، وسوف يستخدم تشغيل الفيديو بيانات إنترنت أكثر من VP9 أو AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">الآثار الجانبية لمحاكاة هوية iOS</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• قد لا يتم تشغيل الأفلام أو الفيديوهات المدفوعة
• مستوى الصوت الثابت غير متوفر
• تنتهي الفيديوهات قبل ب 1 ثانية"</string>
<string name="revanced_spoof_video_streams_about_android_title">الآثار الجانبية لمحاكاة هوية Android</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• قائمة المقطع الصوتي مفقودة
• مستوى الصوت الثابت غير متاح
• فرض الصوت الأصلي غير متوفر"</string>
<string name="revanced_spoof_video_streams_language_title">لغة البث الصوتي الافتراضية</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">لغة الحساب</string>
<string name="revanced_spoof_video_streams_language_AR">العربية</string>
<string name="revanced_spoof_video_streams_language_AZ">Azerbaijani</string>
<string name="revanced_spoof_video_streams_language_BG">Bulgarian</string>
<string name="revanced_spoof_video_streams_language_BN">Bengali</string>
<string name="revanced_spoof_video_streams_language_CA">Catalan</string>
<string name="revanced_spoof_video_streams_language_CS">Czech</string>
<string name="revanced_spoof_video_streams_language_DA">Danish</string>
<string name="revanced_spoof_video_streams_language_DE">German</string>
<string name="revanced_spoof_video_streams_language_EL">Greek</string>
<string name="revanced_spoof_video_streams_language_EN">English</string>
<string name="revanced_spoof_video_streams_language_ES">Spanish</string>
<string name="revanced_spoof_video_streams_language_ET">Estonian</string>
<string name="revanced_spoof_video_streams_language_FA">فارسى</string>
<string name="revanced_spoof_video_streams_language_FI">Finnish</string>
<string name="revanced_spoof_video_streams_language_FR">French</string>
<string name="revanced_spoof_video_streams_language_GU">Gujarati</string>
<string name="revanced_spoof_video_streams_language_HI">Hindi</string>
<string name="revanced_spoof_video_streams_language_HR">Croatian</string>
<string name="revanced_spoof_video_streams_language_HU">Hungarian</string>
<string name="revanced_spoof_video_streams_language_ID"> Indonesian</string>
<string name="revanced_spoof_video_streams_language_IT">Italian</string>
<string name="revanced_spoof_video_streams_language_JA">Japanese</string>
<string name="revanced_spoof_video_streams_language_KK">Kazakh</string>
<string name="revanced_spoof_video_streams_language_KO">Korean</string>
<string name="revanced_spoof_video_streams_language_LT">Lithuanian</string>
<string name="revanced_spoof_video_streams_language_LV">Latvian</string>
<string name="revanced_spoof_video_streams_language_MK">Macedonian</string>
<string name="revanced_spoof_video_streams_language_MN">Mongolian</string>
<string name="revanced_spoof_video_streams_language_MR">Marathi</string>
<string name="revanced_spoof_video_streams_language_MS">Malay</string>
<string name="revanced_spoof_video_streams_language_MY">Burmese</string>
<string name="revanced_spoof_video_streams_language_NL">Dutch</string>
<string name="revanced_spoof_video_streams_language_OR">Odia</string>
<string name="revanced_spoof_video_streams_language_PA">Punjabi</string>
<string name="revanced_spoof_video_streams_language_PL">Polish</string>
<string name="revanced_spoof_video_streams_language_RO">Romanian</string>
<string name="revanced_spoof_video_streams_language_RU">Russian</string>
<string name="revanced_spoof_video_streams_language_SK">Slovak</string>
<string name="revanced_spoof_video_streams_language_SL">Slovene</string>
<string name="revanced_spoof_video_streams_language_SR">Serbian</string>
<string name="revanced_spoof_video_streams_language_SV">Swedish</string>
<string name="revanced_spoof_video_streams_language_SW">Swahili</string>
<string name="revanced_spoof_video_streams_language_TA">Tamil</string>
<string name="revanced_spoof_video_streams_language_TE">Telugu</string>
<string name="revanced_spoof_video_streams_language_TH">Thai</string>
<string name="revanced_spoof_video_streams_language_TR">Turkish</string>
<string name="revanced_spoof_video_streams_language_UK">Ukrainian</string>
<string name="revanced_spoof_video_streams_language_UR">Urdu</string>
<string name="revanced_spoof_video_streams_language_VI">Vietnamese</string>
<string name="revanced_spoof_video_streams_language_ZH">Chinese</string>
<string name="revanced_spoof_video_streams_about_no_av1">• لا يوجد ترميز الفيديو AV1</string>
<string name="revanced_spoof_video_streams_about_kids_videos">• الفيديوات العربية لا يمكن تشغيل أو في وضع التشغيل أو في حالة الخفية</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">عرض في إحصاءات تقنية</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">يتم عرض نوع العميل في إحصاءات تقنية</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">تم إخفاء نوع العميل في إحصاءات تقنية</string>
<string name="revanced_spoof_video_streams_language_title">لغة البث الصوتي الافتراضية للواقع الافتراضي VR</string>
</patch>
</app>
<app id="twitch">

View File

@@ -136,6 +136,8 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
@@ -155,6 +157,8 @@ Second \"item\" text"</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
@@ -168,8 +172,6 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
</patch>
<patch id="layout.theme.themePatch">
@@ -215,11 +217,14 @@ Second \"item\" text"</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
</patch>
<patch id="video.hdr.disableHdrPatch">
</patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch">
</patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<!-- 'no auth' means no authentication -->
</patch>
</app>
<app id="twitch">

View File

@@ -22,8 +22,8 @@ Second \"item\" text"</string>
<resources>
<app id="shared">
<patch id="misc.checks.checkEnvironmentPatch">
<string name="revanced_check_environment_failed_title">\"Правядзенне праверкі асяроддзя не ўдалося\"</string>
<string name="revanced_check_environment_dialog_open_official_source_button">\"Адкрыць афіцыйны вэб-сайт\"</string>
<string name="revanced_check_environment_failed_title">Праверкі не пройдзены</string>
<string name="revanced_check_environment_dialog_open_official_source_button">Адкрыць афіцыйны вэб-сайт</string>
<string name="revanced_check_environment_dialog_ignore_button">Ігнараваць</string>
<string name="revanced_check_environment_failed_message">&lt;h5&gt;Гэта дадатак відавочна не з\'яўляецца патчам.&lt;/h5&gt;&lt;br&gt;Гэта дадатак можа працаваць няправільна, а таксама можа быць &lt;b&gt;небяспечным або нават небяспечным у выкарыстанні&lt;/b&gt;.&lt;br&gt;&lt;br&gt;Гэтыя праверкі азначаюць, што гэта дадатак было загаддзя перароблена або атрымана ад кагосьці іншага:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;Настойліва рэкамендуецца &lt;b&gt;выдаліць гэты дадатак і перарабіць яго самастойна&lt;/b&gt;, каб пераканацца, што вы выкарыстоўваеце правераны і бяспечны дадатак.&lt;p&gt;&lt;br&gt;Калі ігнараваць, гэта папярэджанне будзе паказана толькі два разы.</string>
<string name="revanced_check_environment_not_same_patching_device">Адкарэктавана на іншай прыладзе</string>
@@ -33,7 +33,7 @@ Second \"item\" text"</string>
<string name="revanced_check_environment_not_near_patch_time_invalid">Дата стварэння APK пашкоджана</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_title">ReVanced</string>
<string name="revanced_settings_submenu_title">Налады</string>
<string name="revanced_settings_confirm_user_dialog_title">Вы хочаце працягнуць?</string>
<string name="revanced_settings_reset">Скінуць</string>
<string name="revanced_settings_restart_title">Абнавіце і перазагрузіце</string>
@@ -43,6 +43,62 @@ Second \"item\" text"</string>
<string name="revanced_settings_import_reset">Налады ReVanced скінуты да стандартных</string>
<string name="revanced_settings_import_success">Імпартавана %d налад</string>
<string name="revanced_settings_import_failure_parse">Памылка імпарту: %s</string>
<string name="revanced_language_title">Мова ReVanced</string>
<string name="revanced_language_user_dialog_message">"Пераклады для некаторых моў могуць быць адсутнымі або няпоўнымі.
Каб дадаць новыя мовы, наведайце translate.revanced.app"</string>
<string name="revanced_language_DEFAULT">Мова праграмы</string>
<string name="revanced_language_AR">Арабская</string>
<string name="revanced_language_AZ">Азербайджанскі</string>
<string name="revanced_language_BG">Балгарская</string>
<string name="revanced_language_BN">Бенгальская</string>
<string name="revanced_language_CA">Каталонская</string>
<string name="revanced_language_CS">Чэшскі</string>
<string name="revanced_language_DA">Дацкі</string>
<string name="revanced_language_DE">Нямецкі</string>
<string name="revanced_language_EL">Грэцкі</string>
<string name="revanced_language_EN">Англійская</string>
<string name="revanced_language_ES">Іспанская</string>
<string name="revanced_language_ET">Эстонская</string>
<string name="revanced_language_FA">Персідская</string>
<string name="revanced_language_FI">Фінская</string>
<string name="revanced_language_FR">Французская</string>
<string name="revanced_language_GU">Гуджараці</string>
<string name="revanced_language_HI">Хіндзі</string>
<string name="revanced_language_HR">Харвацкая</string>
<string name="revanced_language_HU">Венгерская</string>
<string name="revanced_language_ID">Інданезійская</string>
<string name="revanced_language_IT">Італьянская</string>
<string name="revanced_language_JA">Японская</string>
<string name="revanced_language_KK">Казахская</string>
<string name="revanced_language_KO">Карэйская</string>
<string name="revanced_language_LT">Літоўская</string>
<string name="revanced_language_LV">Латышская</string>
<string name="revanced_language_MK">Македонская</string>
<string name="revanced_language_MN">Мангольская</string>
<string name="revanced_language_MR">Малаялам</string>
<string name="revanced_language_MS">Малайская</string>
<string name="revanced_language_MY">Бірманская</string>
<string name="revanced_language_NL">Нідэрландская</string>
<string name="revanced_language_OR">Одыя</string>
<string name="revanced_language_PA">Пенджабі</string>
<string name="revanced_language_PL">Польская</string>
<string name="revanced_language_PT">Партугальская</string>
<string name="revanced_language_RO">Румынская</string>
<string name="revanced_language_RU">Руская</string>
<string name="revanced_language_SK">Славацкая</string>
<string name="revanced_language_SL">Славенская</string>
<string name="revanced_language_SR">Сербская</string>
<string name="revanced_language_SV">Шведская</string>
<string name="revanced_language_SW">Суахілі</string>
<string name="revanced_language_TA">Тамільская</string>
<string name="revanced_language_TE">Тэлугу</string>
<string name="revanced_language_TH">Тайская</string>
<string name="revanced_language_TR">Турецкая</string>
<string name="revanced_language_UK">Украінская</string>
<string name="revanced_language_UR">Урду</string>
<string name="revanced_language_VI">В\'етнамская</string>
<string name="revanced_language_ZH">Кітайская</string>
<string name="revanced_pref_import_export_title">Імпарт / Экспарт</string>
<string name="revanced_pref_import_export_summary">Імпарт / Экспарт налад ReVanced</string>
<!-- Settings about dialog. -->
@@ -77,13 +133,15 @@ Second \"item\" text"</string>
<string name="revanced_settings_screen_01_ads_title">Аб\"явы</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">Альтэрнатыўныя мініяцюры</string>
<string name="revanced_settings_screen_03_feed_title">Карміць</string>
<string name="revanced_settings_screen_04_player_title">Гулец</string>
<string name="revanced_settings_screen_05_general_title">Генеральная планіроўка</string>
<string name="revanced_settings_screen_06_shorts_title">Shorts</string>
<string name="revanced_settings_screen_04_general_title">Агульнае</string>
<string name="revanced_settings_screen_05_player_title">Плэер</string>
<string name="revanced_settings_screen_07_seekbar_title">Панэль пошуку</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Элементы кіравання пальцам</string>
<string name="revanced_settings_screen_11_misc_title">Рознае</string>
<string name="revanced_settings_screen_12_video_title">Відэа</string>
<string name="revanced_restore_old_settings_menus_title">Аднавіць старое меню налад</string>
<string name="revanced_restore_old_settings_menus_summary_on">Старыя меню налад паказваюцца</string>
<string name="revanced_restore_old_settings_menus_summary_off">Старыя меню налад не паказваюцца</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">Адключыць прайграванне Shorts у фонавым</string>
@@ -110,9 +168,9 @@ Second \"item\" text"</string>
Вы не будзеце атрымліваць апавяшчэнні пра нечаканыя падзеі."</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_disable_like_subscribe_glow_title">Адключыць свячэнне кнопкі \"Падабаецца\" / \"Падпісацца\"</string>
<string name="revanced_disable_like_subscribe_glow_summary_on">Кнопка \"Падабаецца\" і \"Падпісацца\" не будуць свяціцца пры згадванні</string>
<string name="revanced_disable_like_subscribe_glow_summary_off">Кнопка \"Падабаецца\" і \"Падпісацца\" будуць свяціцца пры згадванні</string>
<string name="revanced_disable_like_subscribe_glow_title">Адключыць свецянне кнопак «Мне падабаецца» і «Падпісацца»</string>
<string name="revanced_disable_like_subscribe_glow_summary_on">Кнопкі «Падабаецца» і «Падпісацца» не будуць мігцець пры згадванні</string>
<string name="revanced_disable_like_subscribe_glow_summary_off">Кнопкі «Падабаецца» і «Падпісацца» будуць мігцець пры згадванні</string>
<string name="revanced_hide_album_cards_title">Схаваць карты альбома</string>
<string name="revanced_hide_album_cards_summary_on">Карткі альбомаў схаваныя</string>
<string name="revanced_hide_album_cards_summary_off">Паказваюцца альбомныя карткі</string>
@@ -165,9 +223,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_chips_shelf_title">Схавайце паліцу для чыпсаў</string>
<string name="revanced_hide_chips_shelf_summary_on">Паліца для чыпсаў схаваная</string>
<string name="revanced_hide_chips_shelf_summary_off">Паказана паліца для чыпсаў</string>
<string name="revanced_hide_expandable_chip_title">Схаваць пашыраемы чып пад відэа</string>
<string name="revanced_hide_expandable_chip_summary_on">Чыпы, якія пашыраюцца, схаваныя</string>
<string name="revanced_hide_expandable_chip_summary_off">Паказаны чыпы, якія пашыраюцца</string>
<string name="revanced_hide_expandable_chip_title">Схаваць пашыраную картку пад відэа</string>
<string name="revanced_hide_expandable_chip_summary_on">Пашыраныя карткі схаваныя</string>
<string name="revanced_hide_expandable_chip_summary_off">Пашыраныя карткі паказаныя</string>
<string name="revanced_hide_community_posts_title">Схаваць паведамленні ў супольнасці</string>
<string name="revanced_hide_community_posts_summary_on">Паведамленні ў супольнасці схаваны</string>
<string name="revanced_hide_community_posts_summary_off">Паказваюцца паведамленні ў супольнасці</string>
@@ -228,6 +286,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_chapters_section_title">Схаваць раздзел \"Раздзелы\"</string>
<string name="revanced_hide_chapters_section_summary_on">Раздзел раздзелаў схаваны</string>
<string name="revanced_hide_chapters_section_summary_off">Паказваецца раздзел раздзелаў</string>
<string name="revanced_hide_how_this_was_made_section_title">Схаваць раздзел «Як быў зроблены гэты кантэнт»</string>
<string name="revanced_hide_how_this_was_made_section_summary_on">Раздзел «Як быў зроблены гэты кантэнт» схаваны</string>
<string name="revanced_hide_how_this_was_made_section_summary_off">Раздзел «Як быў зроблены гэты кантэнт» паказаны</string>
<string name="revanced_hide_podcast_section_title">Схаваць раздзел \"Даследаваць падкаст\"</string>
<string name="revanced_hide_podcast_section_summary_on">Раздзел \"Агляд падкаста\" схаваны</string>
<string name="revanced_hide_podcast_section_summary_off">Паказаны раздзел \"Агляд падкаста\"</string>
@@ -270,7 +331,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_comments_preview_comment_title">Схаваць каментарый для папярэдняга прагляду</string>
<string name="revanced_hide_comments_preview_comment_summary_on">Каментарый перад праглядам схаваны</string>
<string name="revanced_hide_comments_preview_comment_summary_off">Паказваецца папярэдні прагляд каментарыя</string>
<string name="revanced_hide_comments_thanks_button_title">Схаваць кнопку падзякі</string>
<string name="revanced_hide_comments_thanks_button_title">Схаваць кнопку \"Дзякуй\"\"</string>
<string name="revanced_hide_comments_thanks_button_summary_on">Кнопка падзякі схавана</string>
<string name="revanced_hide_comments_thanks_button_summary_off">Паказана кнопка падзякі</string>
<string name="revanced_hide_comments_timestamp_and_emoji_buttons_title">Схаваць метку часу і кнопкі эмодзі</string>
@@ -297,12 +358,12 @@ Second \"item\" text"</string>
<string name="revanced_hide_keyword_content_home_title">Схаваць хатняе відэа па ключавых словах</string>
<string name="revanced_hide_keyword_content_home_summary_on">Відэа на ўкладцы \"Галоўная\" фільтруюцца па ключавых словах</string>
<string name="revanced_hide_keyword_content_home_summary_off">Відэа на ўкладцы \"Галоўная\" не фільтруюцца па ключавых словах</string>
<string name="revanced_hide_keyword_content_subscriptions_title">Схаваць відэа з падпіскі па ключавых словах</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_on">Відэа ва ўкладцы «Падпіскі» фільтруюцца па ключавых словах</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">Відэа ва ўкладцы \"Падпіскі\" не фільтруюцца па ключавых словах</string>
<string name="revanced_hide_keyword_content_search_title">Схаваць вынікі пошуку па ключавых словах</string>
<string name="revanced_hide_keyword_content_search_summary_on">Вынікі пошуку фільтруюцца па ключавых словах</string>
<string name="revanced_hide_keyword_content_search_summary_off">Вынікі пошуку не фільтруюцца па ключавых словах</string>
<string name="revanced_hide_keyword_content_subscriptions_title">Схаваць відэа з падпіскі па ключавых словах</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_on">Відэа ва ўкладцы «Падпіскі» фільтруюцца па ключавых словах</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">Відэа ва ўкладцы \"Падпіскі\" не фільтруюцца па ключавых словах</string>
<string name="revanced_hide_keyword_content_phrases_title">Ключавыя словы, якія трэба схаваць</string>
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
@@ -349,6 +410,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_products_banner_title">Схаваць банер для прагляду прадуктаў</string>
<string name="revanced_hide_products_banner_summary_on">Банэр схаваны</string>
<string name="revanced_hide_products_banner_summary_off">Паказваецца банэр</string>
<string name="revanced_hide_end_screen_store_banner_title">Схаваць банер крамы на канчатковым экране</string>
<string name="revanced_hide_end_screen_store_banner_summary_on">Банэр крамы схаваны</string>
<string name="revanced_hide_end_screen_store_banner_summary_off">Банэр крамы паказаны</string>
<string name="revanced_hide_player_store_shelf_title">Схаваць полку крамы прайгравальніка</string>
<string name="revanced_hide_player_store_shelf_summary_on">Паліца крамы схавана</string>
<string name="revanced_hide_player_store_shelf_summary_off">Паліца крамы паказана</string>
@@ -401,7 +465,7 @@ Second \"item\" text"</string>
<string name="revanced_external_downloader_summary_on">Кнопка загрузкі паказваецца ў плэеры</string>
<string name="revanced_external_downloader_summary_off">Кнопка загрузкі не адлюстроўваецца ў плэеры</string>
<!-- 'download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title' -->
<string name="revanced_external_downloader_action_button_title">Перавызначыць кнопку дзеяння спампоўкі</string>
<string name="revanced_external_downloader_action_button_title">Перавызначыць кнопку дзеянні спампоўкі</string>
<string name="revanced_external_downloader_action_button_summary_on">Кнопка \"Спампаваць\" адкрывае ваш знешні загрузнік</string>
<string name="revanced_external_downloader_action_button_summary_off">Кнопка \"Спампаваць\" адкрывае ўласную праграму загрузкі ў праграме</string>
<string name="revanced_external_downloader_name_title">Назва пакета загрузніка</string>
@@ -446,6 +510,9 @@ Second \"item\" text"</string>
<string name="revanced_swipe_overlay_background_alpha_summary">Бачнасць фону накладання пальцам</string>
<string name="revanced_swipe_threshold_title">Парог велічыні пальцам</string>
<string name="revanced_swipe_threshold_summary">Велічыня парогавага значэння для правядзення пальцам</string>
<string name="revanced_swipe_change_video_title">Уключыць зьмену відэа праз правядзенне пальцам</string>
<string name="revanced_swipe_change_video_summary_on">Правядзенне пальцам у рэжыме поўнага экрана зьменіць відэа на наступнае/папярэдняе</string>
<string name="revanced_swipe_change_video_summary_off">Правядзенне пальцам у рэжыме поўнага экрана не зьменіць відэа на наступнае/папярэдняе</string>
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_auto_captions_title">Адключыць аўтаматычныя цітры</string>
@@ -585,14 +652,14 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_previous_next_buttons_title">Схаваць папярэдні &amp; кнопкі наступнага відэа</string>
<string name="revanced_hide_player_previous_next_buttons_summary_on">Кнопкі схаваныя</string>
<string name="revanced_hide_player_previous_next_buttons_summary_off">Паказваюцца кнопкі</string>
<string name="revanced_hide_cast_button_title">Схаваць кнопку трансляцыі</string>
<string name="revanced_hide_cast_button_title">Схаваць кнопку «Трансляцыя»</string>
<string name="revanced_hide_cast_button_summary_on">Кнопка Cast схавана</string>
<string name="revanced_hide_cast_button_summary_off">Паказана кнопка Cast</string>
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
<string name="revanced_hide_captions_button_title">Кнопка «Схаваць цітры»</string>
<string name="revanced_hide_captions_button_title">Схаваць кнопку «Субцітры»</string>
<string name="revanced_hide_captions_button_summary_on">Кнопка субцітраў схавана</string>
<string name="revanced_hide_captions_button_summary_off">Паказана кнопка субцітраў</string>
<string name="revanced_hide_autoplay_button_title">Схаваць кнопку аўтазапуску</string>
<string name="revanced_hide_autoplay_button_title">Схаваць кнопку «Аўтапрайграванне»</string>
<string name="revanced_hide_autoplay_button_summary_on">Кнопка аўтазапуску схавана</string>
<string name="revanced_hide_autoplay_button_summary_off">Паказана кнопка аўтазапуску</string>
</patch>
@@ -602,7 +669,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_endscreen_cards_summary_off">Паказваюцца карткі канцавога экрана</string>
</patch>
<patch id="layout.hide.fullscreenambientmode.disableFullscreenAmbientModePatch">
<string name="revanced_disable_fullscreen_ambient_mode_title">Адключыць актыўны рэжым у поўнаэкранным рэжыме</string>
<string name="revanced_disable_fullscreen_ambient_mode_title">Адключыць рэжым навакольнага асяроддзя ў поўнаэкранным рэжыме</string>
<string name="revanced_disable_fullscreen_ambient_mode_summary_on">Навакольны рэжым адключаны</string>
<string name="revanced_disable_fullscreen_ambient_mode_summary_off">Неактыўны рэжым уключаны</string>
</patch>
@@ -629,48 +696,51 @@ Second \"item\" text"</string>
<string name="revanced_shorts_player_screen_summary">Схаваць або паказаць кампаненты ў прайгравальніку Shorts</string>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">Схаваць шорты ў хатняй стужцы</string>
<string name="revanced_hide_shorts_home_summary_on">Shorts у стужцы хатняй старонкі схаваны</string>
<string name="revanced_hide_shorts_home_summary_off">Shorts у стужцы хатняй старонкі паказаны</string>
<string name="revanced_hide_shorts_home_summary_on">Схаваны ў стужцы і звязаных відэа</string>
<string name="revanced_hide_shorts_home_summary_off">Паказаны ў стужцы і звязаных відэа</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">Схаваць Shorts у стужцы падпіскі</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Shorts у стужцы падпіскі схаваны</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Shorts у стужцы падпіскі паказаны</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Схавана ў стужцы падпісак</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Паказана ў стужцы падпісак</string>
<string name="revanced_hide_shorts_search_title">Схаваць Shorts у выніках пошуку</string>
<string name="revanced_hide_shorts_search_summary_on">Shorts у выніках пошуку схаваны</string>
<string name="revanced_hide_shorts_search_summary_off">Shorts у выніках пошуку паказаны</string>
<string name="revanced_hide_shorts_search_summary_on">Схаваны ў выніках пошуку</string>
<string name="revanced_hide_shorts_search_summary_off">Паказана ў выніках пошуку</string>
<string name="revanced_hide_shorts_history_title">Схаваць Shorts з гісторыі праглядаў</string>
<string name="revanced_hide_shorts_history_summary_on">Схавана ў гісторыі праглядаў</string>
<string name="revanced_hide_shorts_history_summary_off">Паказаны ў гісторыі праглядаў</string>
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_join_button_title">Схаваць кнопку далучыцца</string>
<string name="revanced_hide_shorts_join_button_title">Схаваць кнопку «Далучыцца»</string>
<string name="revanced_hide_shorts_join_button_summary_on">Кнопка «Далучыцца» схавана</string>
<string name="revanced_hide_shorts_join_button_summary_off">Паказана кнопка «Далучыцца»</string>
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_subscribe_button_title">Схаваць кнопку падпісацца</string>
<string name="revanced_hide_shorts_subscribe_button_title">Схаваць кнопку «Падпісацца»</string>
<string name="revanced_hide_shorts_subscribe_button_summary_on">Кнопка \"Падпісацца\" схавана</string>
<string name="revanced_hide_shorts_subscribe_button_summary_off">Паказана кнопка «Падпісацца»</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_title">Схаваць прыпыненыя кнопкі накладання</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_on">Прыпыненыя кнопкі накладання схаваны</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_off">Паказваюцца прыпыненыя кнопкі накладання</string>
<string name="revanced_hide_shorts_shop_button_title">Схаваць кнопку крамы</string>
<string name="revanced_hide_shorts_shop_button_title">Схаваць кнопку «Крама»</string>
<string name="revanced_hide_shorts_shop_button_summary_on">Кнопка крамы схавана</string>
<string name="revanced_hide_shorts_shop_button_summary_off">Паказваецца кнопка крамы</string>
<string name="revanced_hide_shorts_super_thanks_button_title">Схаваць кнопку супердзякуй</string>
<string name="revanced_hide_shorts_super_thanks_button_summary_on">Кнопка супердзякуй схавана</string>
<string name="revanced_hide_shorts_super_thanks_button_summary_off">Паказана кнопка \"Супер дзякуй\"</string>
<string name="revanced_hide_shorts_super_thanks_button_title">Схаваць кнопку «Купіць Super Thanks»</string>
<string name="revanced_hide_shorts_super_thanks_button_summary_on">Кнопка Super Thanks схавана</string>
<string name="revanced_hide_shorts_super_thanks_button_summary_off">Кнопка Super Thanks паказана</string>
<string name="revanced_hide_shorts_tagged_products_title">Схаваць пазначаныя прадукты</string>
<string name="revanced_hide_shorts_tagged_products_summary_on">Пазначаныя прадукты схаваны</string>
<string name="revanced_hide_shorts_tagged_products_summary_off">Прадукты з тэгамі паказаны</string>
<string name="revanced_hide_shorts_location_label_title">Схаваць метку месцазнаходжання</string>
<string name="revanced_hide_shorts_location_label_summary_on">Метка месцазнаходжання схавана</string>
<string name="revanced_hide_shorts_location_label_summary_off">Паказана метка месцазнаходжання</string>
<string name="revanced_hide_shorts_save_sound_button_title">Схаваць кнопку захавання музыкі</string>
<string name="revanced_hide_shorts_save_sound_button_title">Схаваць кнопку «Захаваць музыку»</string>
<string name="revanced_hide_shorts_save_sound_button_summary_on">Кнопка захавання музыкі схавана</string>
<string name="revanced_hide_shorts_save_sound_button_summary_off">Кнопка захавання музыкі паказана</string>
<string name="revanced_hide_shorts_use_template_button_title">Схаваць кнопку выкарыстання шаблону</string>
<string name="revanced_hide_shorts_use_template_button_title">Схаваць кнопку «Выкарыстаць шаблон»</string>
<string name="revanced_hide_shorts_use_template_button_summary_on">Кнопка выкарыстання шаблону схавана</string>
<string name="revanced_hide_shorts_use_template_button_summary_off">Кнопка выкарыстання шаблону паказана</string>
<string name="revanced_hide_shorts_upcoming_button_title">Схаваць кнопку будучых</string>
<string name="revanced_hide_shorts_upcoming_button_title">Схаваць кнопку «Наступныя»</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Кнопка Будущие ролики скрыта</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Кнопка Будущие ролики отображается</string>
<string name="revanced_hide_shorts_green_screen_button_title">Скрыть кнопку с зелёным экраном Shorts</string>
<string name="revanced_hide_shorts_green_screen_button_title">Схаваць кнопку «Зялёны экран»</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Кнопка с зелёным экраном Shorts скрыта</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Кнопка с зелёным экраном Shorts отображается</string>
<string name="revanced_hide_shorts_hashtag_button_title">Скрыть хештег-кнопку Shorts</string>
@@ -682,24 +752,24 @@ Second \"item\" text"</string>
<string name="revanced_hide_shorts_stickers_title">Скрыть стикеры</string>
<string name="revanced_hide_shorts_stickers_summary_on">Стикеры Shorts скрыты</string>
<string name="revanced_hide_shorts_stickers_summary_off">Стикеры Shorts отображаются</string>
<string name="revanced_hide_shorts_like_fountain_title">Скрыть всплывающее окно с лайками Shorts</string>
<string name="revanced_hide_shorts_like_fountain_title">Схаваць анімацыю кнопкі «Падабаецца»</string>
<string name="revanced_hide_shorts_like_fountain_summary_on">Анимация всплывающего окна с лайками Shorts скрыта</string>
<string name="revanced_hide_shorts_like_fountain_summary_off">Анимация всплывающего окна с лайками Shorts отображается</string>
<string name="revanced_hide_shorts_like_button_title">Схаваць кнопку \"Падабаецца\"</string>
<string name="revanced_hide_shorts_like_button_title">Схаваць кнопку «Падабаецца»</string>
<string name="revanced_hide_shorts_like_button_summary_on">Кнопка \"Падабаецца\" схавана</string>
<string name="revanced_hide_shorts_like_button_summary_off">Паказана кнопка \"Падабаецца\"</string>
<string name="revanced_hide_shorts_dislike_button_title">Схаваць кнопку \"не падабаецца\"</string>
<string name="revanced_hide_shorts_dislike_button_title">Схаваць кнопку «Не падабаецца»</string>
<string name="revanced_hide_shorts_dislike_button_summary_on">Кнопка \"Не падабаецца\" схавана</string>
<string name="revanced_hide_shorts_dislike_button_summary_off">Паказана кнопка \"Не падабаецца\"</string>
<string name="revanced_hide_shorts_comments_button_title">Кнопка \"Схаваць каментарыі\"</string>
<string name="revanced_hide_shorts_comments_button_title">Схаваць кнопку «Каментарыі»</string>
<string name="revanced_hide_shorts_comments_button_summary_on">Кнопка каментарыяў схавана</string>
<string name="revanced_hide_shorts_comments_button_summary_off">Паказана кнопка каментарыяў</string>
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_remix_button_title">Схаваць кнопку рэмікса</string>
<string name="revanced_hide_shorts_remix_button_title">Схаваць кнопку «Рэмікс»</string>
<string name="revanced_hide_shorts_remix_button_summary_on">Кнопка \"Рэмікс\" схавана</string>
<string name="revanced_hide_shorts_remix_button_summary_off">Паказана кнопка \"Рэмікс\"</string>
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_share_button_title">Схаваць кнопку абагульвання</string>
<string name="revanced_hide_shorts_share_button_title">Схаваць кнопку «Падзяліцца»</string>
<string name="revanced_hide_shorts_share_button_summary_on">Кнопка \"Падзяліцца\" схавана</string>
<string name="revanced_hide_shorts_share_button_summary_off">Паказана кнопка \"Падзяліцца\"</string>
<string name="revanced_hide_shorts_info_panel_title">Схаваць інфармацыйную панэль</string>
@@ -739,6 +809,13 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_popup_panels_summary_on">Усплывальныя панэлі прайгравальніка схаваныя</string>
<string name="revanced_hide_player_popup_panels_summary_off">Паказваюцца ўсплывальныя панэлі прайгравальніка</string>
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
<string name="revanced_exit_fullscreen_title">Выйсці з поўнаэкраннага рэжыму ў канцы відэа</string>
<string name="revanced_exit_fullscreen_entry_1">Адключана</string>
<string name="revanced_exit_fullscreen_entry_2">Партрэт</string>
<string name="revanced_exit_fullscreen_entry_3">Ландшафт</string>
<string name="revanced_exit_fullscreen_entry_4">Партрэт і ландшафт</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">Открывать видео на весь экран в портретном режиме</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">Видео открываются на весь экран</string>
@@ -769,7 +846,7 @@ Second \"item\" text"</string>
<string name="revanced_ryd_dislike_percentage_summary_on">Неўпадабанні паказаны ў працэнтах</string>
<string name="revanced_ryd_dislike_percentage_summary_off">Неўпадабанні паказаны ў выглядзе колькасці</string>
<!-- Translations should use language similar to 'revanced_sb_enable_compact_skip_button' -->
<string name="revanced_ryd_compact_layout_title">Кампактная кнопка</string>
<string name="revanced_ryd_compact_layout_title">Кампактны дызайн кнопак «Падабаецца» і «Не падабаецца»</string>
<string name="revanced_ryd_compact_layout_summary_on">Кнопка \"Падабаецца\" ў стылі мінімальнай шырыні</string>
<string name="revanced_ryd_compact_layout_summary_off">Кнопка \"Падабаецца\", аформленая для лепшага выгляду</string>
<string name="revanced_ryd_toast_on_connection_error_title">Паказаць тост, калі API недаступны</string>
@@ -817,7 +894,7 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.sponsorblock.sponsorBlockResourcePatch">
<string name="revanced_sb_enable_sb">Уключыць SponsorBlock</string>
<string name="revanced_sb_enable_sb_sum">SponsorBlock - гэта краўдсорсінгавая сістэма для пропуску раздражняльных частак відэа YouTube</string>
<string name="revanced_sb_enable_sb_sum">SponsorBlock гэта краўдсорсінгавая сістэма, якая дазваляе прапускаць раздражняльныя фрагменты відэа на YouTube</string>
<string name="revanced_sb_appearance_category">Паглядзіце</string>
<string name="revanced_sb_enable_voting">Паказаць кнопку галасавання</string>
<string name="revanced_sb_enable_voting_sum_on">Паказана кнопка сегментнага галасавання</string>
@@ -883,7 +960,7 @@ Second \"item\" text"</string>
<string name="revanced_sb_segments_sponsor">Спонсар</string>
<string name="revanced_sb_segments_sponsor_sum">Платнае прасоўванне, платныя рэфералы і прамая рэклама. Не для самарэкламы або бясплатных выкрыкаў справам/стваральнікам/вэб-сайтам/прадуктам, якія ім падабаюцца</string>
<string name="revanced_sb_segments_selfpromo">Неаплатнае/самарэклама</string>
<string name="revanced_sb_segments_selfpromo_sum">Падобна \"Спонсару\", за выключэннем неаплатнага або самарэкламы. Уключае раздзелы пра тавары, ахвяраванні або інфармацыю аб тым, з кім яны супрацоўнічалі</string>
<string name="revanced_sb_segments_selfpromo_sum">Аналогічна спонсарству, але для неаплачваемай або ўласнай рэкламы. Уключае раздзелы аб таварах, ахвяраваннях або інфармацыі аб тым, з кім яны супрацоўнічалі</string>
<string name="revanced_sb_segments_interaction">Напамін аб узаемадзеянні (падпісацца)</string>
<string name="revanced_sb_segments_interaction_sum">Кароткі напамін паставіць лайкі, падпісацца або падпісацца на іх у сярэдзіне кантэнту. Калі ён доўгі або прысвечаны нечаму канкрэтнаму, ён павінен адпавядаць самарэкламе</string>
<string name="revanced_sb_segments_highlight">Вылучыць</string>
@@ -1008,6 +1085,23 @@ Second \"item\" text"</string>
<string name="revanced_sb_reset">Скінуць</string>
<string name="revanced_sb_about">Пра праграму</string>
<string name="revanced_sb_about_api_sum">Дадзеныя прадастаўляюцца API SponsorBlock. Націсніце тут, каб даведацца больш і паглядзець спампоўкі для іншых платформаў</string>
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
<string name="revanced_change_form_factor_title">Фармат экрана</string>
<string name="revanced_change_form_factor_entry_1">Па змаўчанню</string>
<string name="revanced_change_form_factor_entry_2">Тэлефон</string>
<string name="revanced_change_form_factor_entry_3">Планшэт</string>
<string name="revanced_change_form_factor_entry_4">Аўтамабільны</string>
<string name="revanced_change_form_factor_user_dialog_message">"Змены ўключаюць:
Раскладка планшэта
• Паведамленні супольнасці схаваны
Раскладка аўтамабіля
• Меню гісторыі праглядаў схавана
• Адноўлена ўкладка «Даследаваць»
• Ролікі Shorts адкрываюцца ў звычайным прайгравальніку
• Стужка арганізавана па тэмах і каналах"</string>
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<string name="revanced_spoof_app_version_title">Версія праграмы Spoof</string>
@@ -1022,6 +1116,7 @@ Second \"item\" text"</string>
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<string name="revanced_spoof_app_version_target_title">Падробка мэтавай версіі праграмы</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 — Восстановить старые значки плеера Shorts</string>
<string name="revanced_spoof_app_version_target_entry_2">19.26.42 - Аднаўленне старых значкоў навігацыі</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_legacy_entry_1">18.33.40 - Аднаўленне RYD на Shorts у рэжыме інкогніта</string>
<string name="revanced_spoof_app_version_target_legacy_entry_2">18.20.39 - Аднавіць хуткасць шырокага відэа &amp; якаснае меню</string>
@@ -1065,12 +1160,6 @@ Second \"item\" text"</string>
<string name="revanced_shorts_autoplay_background_summary_on">Shorts в фоновом режиме будут воспроизводиться автоматически</string>
<string name="revanced_shorts_autoplay_background_summary_off">Shorts в фоновом режиме будут повторяться</string>
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">Уключыць макет планшэта</string>
<string name="revanced_tablet_layout_summary_on">Макет планшэта ўключаны</string>
<string name="revanced_tablet_layout_summary_off">Макет планшэта адключаны</string>
<string name="revanced_tablet_layout_user_dialog_message">Паведамленні ў супольнасці не адлюстроўваюцца на планшэце</string>
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">Міні-плэер</string>
<string name="revanced_miniplayer_screen_summary">Змяніце стыль мінімізаванага плэера ў праграме</string>
@@ -1131,8 +1220,10 @@ Second \"item\" text"</string>
<string name="revanced_seekbar_custom_color_title">Уключыць уласны колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_summary_on">Паказваецца карыстальніцкі колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_summary_off">Паказаны зыходны колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_value_title">Карыстальніцкі колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_value_summary">Колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_primary_title">Спецыяльны колер пошуку</string>
<string name="revanced_seekbar_custom_color_primary_summary">Колер пошуку</string>
<string name="revanced_seekbar_custom_color_accent_title">Спецыяльны акцэнтны колер пошуку</string>
<string name="revanced_seekbar_custom_color_accent_summary">Акцэнтны колер пошуку</string>
<string name="revanced_seekbar_custom_color_invalid">Неверное значение цвета ползунка прогресса</string>
</patch>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
@@ -1234,6 +1325,7 @@ Second \"item\" text"</string>
<string name="revanced_force_original_audio_title">Вымушанае арыгінальнае аўдыё</string>
<string name="revanced_force_original_audio_summary_on">Выкарыстанне арыгінальнага аўдыё</string>
<string name="revanced_force_original_audio_summary_off">Выкарыстанне аўдыё па змаўчанні</string>
<string name="revanced_force_original_audio_not_available">Каб выкарыстоўваць гэтую функцыю, зменіце спафінг патоку на тып кліента iOS</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -1261,6 +1353,8 @@ Second \"item\" text"</string>
<string name="revanced_custom_playback_speeds_invalid">Нестандартныя хуткасці павінны быць менш за %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">Несапраўдныя нестандартныя хуткасці прайгравання</string>
<string name="revanced_custom_playback_speeds_auto">Аўто</string>
<string name="revanced_speed_tap_and_hold_title">Уласны хуткасць націску і ўтрымання</string>
<string name="revanced_speed_tap_and_hold_summary">Хуткасць прайгравання між 0-8</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
<string name="revanced_remember_playback_speed_last_selected_title">Запомніце змены хуткасці прайгравання</string>
@@ -1269,6 +1363,11 @@ Second \"item\" text"</string>
<string name="revanced_playback_speed_default_title">Стандартная хуткасць прайгравання</string>
<string name="revanced_remember_playback_speed_toast">Хуткасць па змаўчанні зменена на: %s</string>
</patch>
<patch id="video.hdr.disableHdrPatch">
<string name="revanced_disable_hdr_video_title">Адключыць відэа ў фармаце HDR</string>
<string name="revanced_disable_hdr_video_summary_on">Відэа ў фармаце HDR адключана</string>
<string name="revanced_disable_hdr_video_summary_off">Відэа ў фармаце HDR уключана</string>
</patch>
<patch id="video.videoqualitymenu.restoreOldVideoQualityMenuResourcePatch">
<string name="revanced_restore_old_video_quality_menu_title">Аднавіць старое меню якасці відэа</string>
<string name="revanced_restore_old_video_quality_menu_summary_on">Паказана старое меню якасці відэа</string>
@@ -1289,61 +1388,28 @@ Second \"item\" text"</string>
Прайграванне відэа можа не працаваць"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">Адключэнне гэтай налады можа выклікаць праблемы з прайграваннем відэа.</string>
<string name="revanced_spoof_video_streams_client_type_title">Клиент по умолчанию</string>
<string name="revanced_spoof_video_streams_about_title">Падробка пабочных эфектаў</string>
<string name="revanced_spoof_video_streams_about_summary">"• Аўдыёдарожкі, пазначаныя як арыгінальныя, могуць быць зменены на іншыя
• Функцыя \"Стабільная гучнасць\" недаступная"</string>
<string name="revanced_spoof_video_streams_language_title">Язык потока аудио по умолчанию</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">Мова акаўнта</string>
<string name="revanced_spoof_video_streams_language_AR">Арабский</string>
<string name="revanced_spoof_video_streams_language_AZ">Азербайджанский</string>
<string name="revanced_spoof_video_streams_language_BG">Болгарский</string>
<string name="revanced_spoof_video_streams_language_BN">Бенгальский</string>
<string name="revanced_spoof_video_streams_language_CA">Каталонский</string>
<string name="revanced_spoof_video_streams_language_CS">Чешский</string>
<string name="revanced_spoof_video_streams_language_DA">Датский</string>
<string name="revanced_spoof_video_streams_language_DE">Немецкий</string>
<string name="revanced_spoof_video_streams_language_EL">Греческий</string>
<string name="revanced_spoof_video_streams_language_EN">Англійская</string>
<string name="revanced_spoof_video_streams_language_ES">Іспанская</string>
<string name="revanced_spoof_video_streams_language_ET">Эстонская</string>
<string name="revanced_spoof_video_streams_language_FA">Фарсі</string>
<string name="revanced_spoof_video_streams_language_FI">Фінская</string>
<string name="revanced_spoof_video_streams_language_FR">Французская</string>
<string name="revanced_spoof_video_streams_language_GU">Гуяраті</string>
<string name="revanced_spoof_video_streams_language_HI">Хянді</string>
<string name="revanced_spoof_video_streams_language_HR">Хорватская</string>
<string name="revanced_spoof_video_streams_language_HU">Венгерская</string>
<string name="revanced_spoof_video_streams_language_ID">Инданезійская</string>
<string name="revanced_spoof_video_streams_language_IT">Итальянская</string>
<string name="revanced_spoof_video_streams_language_JA">Японская</string>
<string name="revanced_spoof_video_streams_language_KK">Казахская</string>
<string name="revanced_spoof_video_streams_language_KO">Корэйская</string>
<string name="revanced_spoof_video_streams_language_LT">Літоуская</string>
<string name="revanced_spoof_video_streams_language_LV">Латышская</string>
<string name="revanced_spoof_video_streams_language_MK">Македонская</string>
<string name="revanced_spoof_video_streams_language_MN">Монгольская</string>
<string name="revanced_spoof_video_streams_language_MR">Маратхі</string>
<string name="revanced_spoof_video_streams_language_MS">Малайская</string>
<string name="revanced_spoof_video_streams_language_MY">Брыманская</string>
<string name="revanced_spoof_video_streams_language_NL">Нідрландский</string>
<string name="revanced_spoof_video_streams_language_OR">Орія</string>
<string name="revanced_spoof_video_streams_language_PA">Панджабский</string>
<string name="revanced_spoof_video_streams_language_PL">Польская</string>
<string name="revanced_spoof_video_streams_language_RO">Румынская</string>
<string name="revanced_spoof_video_streams_language_RU">Русская</string>
<string name="revanced_spoof_video_streams_language_SK">Словацкия</string>
<string name="revanced_spoof_video_streams_language_SL">Словенская</string>
<string name="revanced_spoof_video_streams_language_SR">Сербская</string>
<string name="revanced_spoof_video_streams_language_SV">Швецкая</string>
<string name="revanced_spoof_video_streams_language_SW">Суахілі</string>
<string name="revanced_spoof_video_streams_language_TA">Тамільская</string>
<string name="revanced_spoof_video_streams_language_TE">Тэлугу</string>
<string name="revanced_spoof_video_streams_language_TH">Тайская</string>
<string name="revanced_spoof_video_streams_language_TR">Турэцкая</string>
<string name="revanced_spoof_video_streams_language_UK">Украёнская</string>
<string name="revanced_spoof_video_streams_language_UR">Урду</string>
<string name="revanced_spoof_video_streams_language_VI">Віетнамская</string>
<string name="revanced_spoof_video_streams_language_ZH">Кітайская</string>
<!-- 'no auth' means no authentication -->
<string name="revanced_spoof_video_streams_client_type_android_vr_no_auth">Android VR (няма аўтэнтыфікацыі)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">Вымусіць iOS AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Відэакaдэк зафіксаваны ў AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Відэакaдэк вызначаецца аўтаматычна</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Уключэнне гэтага можа палепшыць час аўтаномнай працы і выправіць заіканне прайгравання.
AVC мае максімальнае дазвол 1080p, аўдыёкадэк Opus недаступны, а прайграванне відэа будзе выкарыстоўваць больш інтэрнэт-даных, чым VP9 або AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">Пабочныя эфекты падробкі iOS</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• Фільмы або платныя відэа могуць не прайгравацца
• Стабільная гучнасць недаступная
• Відэа заканчваюцца на 1 секунду раней"</string>
<string name="revanced_spoof_video_streams_about_android_title">Побічныя эфекты падробкі Android</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• Меню гукавой дарожкі адсутнічае
• Стабільны гук недаступны
• Прымусовае арыгінальнае аўдыё недаступна"</string>
<string name="revanced_spoof_video_streams_about_no_av1">• Няма відэакідавання AV1</string>
<string name="revanced_spoof_video_streams_about_kids_videos">• Дзіцячыя відэа могуць не прайгравацца ў стане выхаду з акаўнта або ў рэжыме інкогніта</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Паказаць у статыстыцы для спецыялістаў</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Тып кліента адлюстроўваецца ў статыстыцы для спецыялістаў</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Кліент схаваны ў статыстыцы для спецыялістаў</string>
<string name="revanced_spoof_video_streams_language_title">Мова гукавой дарожкі па змаўчанні для VR</string>
</patch>
</app>
<app id="twitch">

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