Compare commits

..

52 Commits

Author SHA1 Message Date
semantic-release-bot
edf20e397d chore: Release v5.23.0-dev.6 [skip ci]
# [5.23.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.5...v5.23.0-dev.6) (2025-05-06)

### Bug Fixes

* Correct incorrect fingerprint ([5f05414](5f0541407c))
2025-05-06 10:50:54 +00:00
oSumAtrIX
5f0541407c fix: Correct incorrect fingerprint 2025-05-06 12:47:06 +02:00
semantic-release-bot
56b7ba9ba7 chore: Release v5.23.0-dev.5 [skip ci]
# [5.23.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.4...v5.23.0-dev.5) (2025-05-06)

### Bug Fixes

* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([5028c1a](5028c1acb3))

### Features

* **Pandora:** Add `Disable audio ads` and `Unlimited skips` patch ([#4841](https://github.com/ReVanced/revanced-patches/issues/4841)) ([f4f36ff](f4f36ff273))
* **Prime Video:** Add `Skip ads` patch ([#4824](https://github.com/ReVanced/revanced-patches/issues/4824)) ([f8bdf74](f8bdf744ab))
2025-05-06 07:44:30 +00:00
hoodles
f8bdf744ab feat(Prime Video): Add Skip ads patch (#4824) 2025-05-06 11:40:45 +04:00
hoodles
f4f36ff273 feat(Pandora): Add Disable audio ads and Unlimited skips patch (#4841)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2025-05-06 11:39:07 +04:00
Nuckyz
5028c1acb3 fix(Spotify - Unlock Spotify Premium): Remove pop up premium ads (#4842) 2025-05-06 11:35:47 +04:00
semantic-release-bot
555c9a5823 chore: Release v5.23.0-dev.4 [skip ci]
# [5.23.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.3...v5.23.0-dev.4) (2025-05-06)

### Features

* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([777957e](777957e2d0))
2025-05-06 07:35:22 +00:00
Dawid Krajcarz
777957e2d0 feat(Spotify): Add Sanitize sharing links patch (#4829) 2025-05-06 11:31:56 +04:00
github-actions[bot]
b3316a5915 chore: Sync translations (#4915) 2025-05-06 11:31:12 +04:00
semantic-release-bot
2ca2bb7692 chore: Release v5.23.0-dev.3 [skip ci]
# [5.23.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.2...v5.23.0-dev.3) (2025-05-05)

### Bug Fixes

* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([23fd720](23fd720fa7))
2025-05-05 11:28:44 +00:00
LisoUseInAIKyrios
23fd720fa7 fix(YouTube): Simplify litho filtering patch (#4910) 2025-05-05 15:25:25 +04:00
semantic-release-bot
1f08586ae8 chore: Release v5.23.0-dev.2 [skip ci]
# [5.23.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.1...v5.23.0-dev.2) (2025-05-04)

### Bug Fixes

* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([60fdf4c](60fdf4c44c))
2025-05-04 09:58:25 +00:00
LisoUseInAIKyrios
60fdf4c44c fix(YouTube): Improve litho filtering performance (#4904) 2025-05-04 13:55:12 +04:00
semantic-release-bot
63f3342815 chore: Release v5.23.0-dev.1 [skip ci]
# [5.23.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0-dev.1) (2025-05-02)

### Features

* **Lightroom:** Constrain patches to last working version ([858c59d](858c59d728))
2025-05-02 13:02:25 +00:00
oSumAtrIX
858c59d728 feat(Lightroom): Constrain patches to last working version 2025-05-02 14:58:57 +02:00
semantic-release-bot
5debf9936d chore: Release v5.22.0 [skip ci]
# [5.22.0](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0) (2025-05-01)

### Bug Fixes

* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([b2453fe](b2453fecfc))
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([9b1013e](9b1013e1c2))
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([75d6cd7](75d6cd7c7b))
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([ef35ed7](ef35ed7335))
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f69eab3](f69eab3e3b))

### Features

* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([72e0c01](72e0c01922))
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([45b5a51](45b5a51da3))
2025-05-01 07:03:38 +00:00
LisoUseInAIKyrios
f1b85d20a1 chore: Merge branch dev to main (#4864) 2025-05-01 11:00:16 +04:00
github-actions[bot]
37d0de5e93 chore: Sync translations (#4894) 2025-05-01 10:59:24 +04:00
semantic-release-bot
96d08d5eb7 chore: Release v5.22.0-dev.4 [skip ci]
# [5.22.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.3...v5.22.0-dev.4) (2025-04-30)

### Bug Fixes

* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([9b1013e](9b1013e1c2))
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([75d6cd7](75d6cd7c7b))
2025-04-30 22:11:17 +00:00
Bceez
9b1013e1c2 fix(YouTube - Hide layout components): Hide new type of community posts (#4888) 2025-05-01 02:07:52 +04:00
LisoUseInAIKyrios
75d6cd7c7b fix(YouTube - Hide Shorts components): Hide action buttons A/B button layout (#4889) 2025-05-01 02:07:32 +04:00
github-actions[bot]
5a17f5e1c1 chore: Sync translations (#4890) 2025-05-01 02:04:21 +04:00
MarcaD
1d16de6617 bug(YouTube - Theme): Fix white system navigation bar in the ReVanced settings (#4875) 2025-04-29 23:27:14 +04:00
github-actions[bot]
aee7cba46d chore: Sync translations (#4884) 2025-04-29 23:26:47 +04:00
semantic-release-bot
ec3faf30a8 chore: Release v5.22.0-dev.3 [skip ci]
# [5.22.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.2...v5.22.0-dev.3) (2025-04-29)

### Features

* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([45b5a51](45b5a51da3))
2025-04-29 09:40:18 +00:00
LisoUseInAIKyrios
45b5a51da3 feat(YouTube - GmsCore support): Show troubleshooting in app text if the user recently changed their account details (#4879) 2025-04-29 13:36:29 +04:00
semantic-release-bot
8abf176bc9 chore: Release v5.22.0-dev.2 [skip ci]
# [5.22.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.1...v5.22.0-dev.2) (2025-04-27)

### Bug Fixes

* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([ef35ed7](ef35ed7335))
2025-04-27 14:28:33 +00:00
LisoUseInAIKyrios
ef35ed7335 fix(YouTube - Shorts autoplay): Fix autoplay with YT 20.12 2025-04-27 18:24:37 +04:00
semantic-release-bot
4fd666b667 chore: Release v5.22.0-dev.1 [skip ci]
# [5.22.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0-dev.1) (2025-04-26)

### Bug Fixes

* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([b2453fe](b2453fecfc))
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f69eab3](f69eab3e3b))

### Features

* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([72e0c01](72e0c01922))
2025-04-26 13:55:47 +00:00
3igcheeze
72e0c01922 feat(TikTok - Feed Filter): Remove TikTok Shop from feed. (#4851) 2025-04-26 17:52:17 +04:00
LisoUseInAIKyrios
f69eab3e3b fix(YouTube - Spoof app version): Do not hide spoof version in general settings menu (#4861) 2025-04-26 17:51:35 +04:00
github-actions[bot]
7c5c2d95bc chore: Sync translations (#4865) 2025-04-26 17:51:18 +04:00
Jaimy Smets
b2453fecfc fix(TikTok - Feed filter): Hide ads in following feed (#4844) 2025-04-26 17:49:28 +04:00
semantic-release-bot
0d54f8bd80 chore: Release v5.21.0 [skip ci]
# [5.21.0](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0) (2025-04-25)

### Bug Fixes

* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([5e069bd](5e069bde90))
* **GmsCore Support:** Correct the description to refer to the app being patched ([96512de](96512de6c9))
* **Wide search bar:** Fix patching `19.16.39` ([d7c9dd0](d7c9dd0f77))
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([896de89](896de8910a))
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([8efbaae](8efbaae65c))
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([2d94ba9](2d94ba9df6))
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([15053e2](15053e2b68))
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([77ea5c4](77ea5c4033))

### Features

* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([7cc6995](7cc6995682))
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([fc6282d](fc6282d0cb))
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([6d69f01](6d69f01421))
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([f216e16](f216e16c0b))
2025-04-25 17:21:09 +00:00
LisoUseInAIKyrios
fda16fad1a chore: Merge branch dev to main (#4803) 2025-04-25 21:17:55 +04:00
github-actions[bot]
ddd43acd73 chore: Sync translations (#4858) 2025-04-25 21:17:12 +04:00
semantic-release-bot
3451318d53 chore: Release v5.21.0-dev.12 [skip ci]
# [5.21.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.11...v5.21.0-dev.12) (2025-04-24)

### Bug Fixes

* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([2d94ba9](2d94ba9df6))
2025-04-24 19:26:35 +00:00
LisoUseInAIKyrios
2d94ba9df6 fix(YouTube - Hide video action buttons): Add option to hide 'Ask' button (#4852) 2025-04-24 23:23:10 +04:00
github-actions[bot]
aaf3437a5a chore: Sync translations (#4853) 2025-04-24 23:21:29 +04:00
semantic-release-bot
ec8bf06047 chore: Release v5.21.0-dev.11 [skip ci]
# [5.21.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.10...v5.21.0-dev.11) (2025-04-24)

### Bug Fixes

* **GmsCore Support:** Correct the description to refer to the app being patched ([96512de](96512de6c9))
2025-04-24 18:51:43 +00:00
oSumAtrIX
96512de6c9 fix(GmsCore Support): Correct the description to refer to the app being patched 2025-04-24 20:48:12 +02:00
semantic-release-bot
6114807c43 chore: Release v5.21.0-dev.10 [skip ci]
# [5.21.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.9...v5.21.0-dev.10) (2025-04-23)

### Features

* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([6d69f01](6d69f01421))
2025-04-23 11:34:08 +00:00
MarcaD
6d69f01421 feat(YouTube - Swipe controls): Add option for vertical progress bar (#4811) 2025-04-23 15:30:41 +04:00
github-actions[bot]
fd4218154d chore: Sync translations (#4845) 2025-04-23 15:30:22 +04:00
github-actions[bot]
8bed8a6622 chore: Sync translations (#4839) 2025-04-21 22:17:26 +02:00
semantic-release-bot
3174047223 chore: Release v5.21.0-dev.9 [skip ci]
# [5.21.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.8...v5.21.0-dev.9) (2025-04-21)

### Bug Fixes

* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([15053e2](15053e2b68))
2025-04-21 20:16:36 +00:00
LisoUseInAIKyrios
15053e2b68 fix(YouTube - Hide video action buttons): Hide A/B layout buttons 2025-04-21 22:13:04 +02:00
semantic-release-bot
e5b6aac018 chore: Release v5.21.0-dev.8 [skip ci]
# [5.21.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.7...v5.21.0-dev.8) (2025-04-20)

### Bug Fixes

* **Wide search bar:** Fix patching `19.16.39` ([d7c9dd0](d7c9dd0f77))
2025-04-20 11:31:26 +00:00
LisoUseInAIKyrios
d7c9dd0f77 fix(Wide search bar): Fix patching 19.16.39 2025-04-20 13:27:55 +02:00
github-actions[bot]
a0eb6d5fdb chore: Sync translations (#4833) 2025-04-20 13:27:33 +02:00
semantic-release-bot
55c5eb3d14 chore: Release v5.21.0-dev.7 [skip ci]
# [5.21.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.6...v5.21.0-dev.7) (2025-04-20)

### Bug Fixes

* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([896de89](896de8910a))
2025-04-20 08:24:42 +00:00
LisoUseInAIKyrios
896de8910a fix(YouTube - Change start page): Add option to always override start page on app launch (#4832) 2025-04-20 10:21:03 +02:00
165 changed files with 3997 additions and 1508 deletions

View File

@@ -1,3 +1,167 @@
# [5.23.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.5...v5.23.0-dev.6) (2025-05-06)
### Bug Fixes
* Correct incorrect fingerprint ([c3bab89](https://github.com/ReVanced/revanced-patches/commit/c3bab89fc4189e38c10eee0caa36289de7e29dfa))
# [5.23.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.4...v5.23.0-dev.5) (2025-05-06)
### Bug Fixes
* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([00aa200](https://github.com/ReVanced/revanced-patches/commit/00aa2000ba2eef15a0dd827c2bd84c2e85c412e0))
### Features
* **Pandora:** Add `Disable audio ads` and `Unlimited skips` patch ([#4841](https://github.com/ReVanced/revanced-patches/issues/4841)) ([0cf7a4c](https://github.com/ReVanced/revanced-patches/commit/0cf7a4c6be615ed0a52a6bacf87592f5f43ff575))
* **Prime Video:** Add `Skip ads` patch ([#4824](https://github.com/ReVanced/revanced-patches/issues/4824)) ([bb672c4](https://github.com/ReVanced/revanced-patches/commit/bb672c4674ddc201b8b2648c3906cfc31ef43f10))
# [5.23.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.3...v5.23.0-dev.4) (2025-05-06)
### Features
* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([2e3511d](https://github.com/ReVanced/revanced-patches/commit/2e3511d03c8198bbdb9336888df038a33fb3ab8c))
# [5.23.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.2...v5.23.0-dev.3) (2025-05-05)
### Bug Fixes
* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([bd53955](https://github.com/ReVanced/revanced-patches/commit/bd53955df738bb7b819eb91a3e776e9d2ca5c74a))
# [5.23.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.1...v5.23.0-dev.2) (2025-05-04)
### Bug Fixes
* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([7b43986](https://github.com/ReVanced/revanced-patches/commit/7b43986871a68e5cb43331d2fb2fdb9ef67438ad))
# [5.23.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0-dev.1) (2025-05-02)
### Features
* **Lightroom:** Constrain patches to last working version ([efef03b](https://github.com/ReVanced/revanced-patches/commit/efef03b80da21552d0d8be6913faba64e4fb5ed1))
# [5.22.0](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0) (2025-05-01)
### Bug Fixes
* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([c255ac1](https://github.com/ReVanced/revanced-patches/commit/c255ac18e0b2dcf917bd0559876be5a2a81023db))
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([f0c9c35](https://github.com/ReVanced/revanced-patches/commit/f0c9c35778ab43a99149ee5ad0ccfd8aeb09f638))
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([9dcd3d3](https://github.com/ReVanced/revanced-patches/commit/9dcd3d35dddf019547ab6ce431bac7a5a8a4c291))
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([06b35b2](https://github.com/ReVanced/revanced-patches/commit/06b35b2a7d7371915881e8f430c32ce15fa224de))
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f459c3c](https://github.com/ReVanced/revanced-patches/commit/f459c3c7fae3a1b8addf3354488dcef9f95255cc))
### Features
* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([f198bec](https://github.com/ReVanced/revanced-patches/commit/f198bece653e3e1adf083129dedb77c1d1a633d7))
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([ab4bdc8](https://github.com/ReVanced/revanced-patches/commit/ab4bdc8a2519cee15f79bf95d89e7ea56ea464ee))
# [5.22.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.3...v5.22.0-dev.4) (2025-04-30)
### Bug Fixes
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([f0c9c35](https://github.com/ReVanced/revanced-patches/commit/f0c9c35778ab43a99149ee5ad0ccfd8aeb09f638))
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([9dcd3d3](https://github.com/ReVanced/revanced-patches/commit/9dcd3d35dddf019547ab6ce431bac7a5a8a4c291))
# [5.22.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.2...v5.22.0-dev.3) (2025-04-29)
### Features
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([ab4bdc8](https://github.com/ReVanced/revanced-patches/commit/ab4bdc8a2519cee15f79bf95d89e7ea56ea464ee))
# [5.22.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.1...v5.22.0-dev.2) (2025-04-27)
### Bug Fixes
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([06b35b2](https://github.com/ReVanced/revanced-patches/commit/06b35b2a7d7371915881e8f430c32ce15fa224de))
# [5.22.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0-dev.1) (2025-04-26)
### Bug Fixes
* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([c255ac1](https://github.com/ReVanced/revanced-patches/commit/c255ac18e0b2dcf917bd0559876be5a2a81023db))
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f459c3c](https://github.com/ReVanced/revanced-patches/commit/f459c3c7fae3a1b8addf3354488dcef9f95255cc))
### Features
* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([f198bec](https://github.com/ReVanced/revanced-patches/commit/f198bece653e3e1adf083129dedb77c1d1a633d7))
# [5.21.0](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0) (2025-04-25)
### Bug Fixes
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
### Features
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([655b390](https://github.com/ReVanced/revanced-patches/commit/655b39043ad77efcb4380de67c3f603666e7bc49))
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
# [5.21.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.11...v5.21.0-dev.12) (2025-04-24)
### Bug Fixes
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
# [5.21.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.10...v5.21.0-dev.11) (2025-04-24)
### Bug Fixes
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
# [5.21.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.9...v5.21.0-dev.10) (2025-04-23)
### Features
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
# [5.21.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.8...v5.21.0-dev.9) (2025-04-21)
### Bug Fixes
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
# [5.21.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.7...v5.21.0-dev.8) (2025-04-20)
### Bug Fixes
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
# [5.21.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.6...v5.21.0-dev.7) (2025-04-20)
### Bug Fixes
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
# [5.21.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.5...v5.21.0-dev.6) (2025-04-19)

View File

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

View File

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

View File

@@ -0,0 +1,36 @@
package app.revanced.extension.primevideo.ads;
import com.amazon.avod.fsm.SimpleTrigger;
import com.amazon.avod.media.ads.AdBreak;
import com.amazon.avod.media.ads.internal.state.AdBreakTrigger;
import com.amazon.avod.media.ads.internal.state.AdEnabledPlayerTriggerType;
import com.amazon.avod.media.playback.VideoPlayer;
import com.amazon.avod.media.ads.internal.state.ServerInsertedAdBreakState;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public final class SkipAdsPatch {
public static void enterServerInsertedAdBreakState(ServerInsertedAdBreakState state, AdBreakTrigger trigger, VideoPlayer player) {
try {
AdBreak adBreak = trigger.getBreak();
// There are two scenarios when entering the original method:
// 1. Player naturally entered an ad break while watching a video.
// 2. User is skipped/scrubbed to a position on the timeline. If seek position is past an ad break,
// user is forced to watch an ad before continuing.
//
// Scenario 2 is indicated by trigger.getSeekStartPosition() != null, so skip directly to the scrubbing
// target. Otherwise, just calculate when the ad break should end and skip to there.
if (trigger.getSeekStartPosition() != null)
player.seekTo(trigger.getSeekTarget().getTotalMilliseconds());
else
player.seekTo(player.getCurrentPosition() + adBreak.getDurationExcludingAux().getTotalMilliseconds());
// Send "end of ads" trigger to state machine so everything doesn't get whacky.
state.doTrigger(new SimpleTrigger(AdEnabledPlayerTriggerType.NO_MORE_ADS_SKIP_TRANSITION));
} catch (Exception ex) {
Logger.printException(() -> "Failed skipping ads", ex);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
package com.amazon.avod.fsm;
public final class SimpleTrigger<T> implements Trigger<T> {
public SimpleTrigger(T triggerType) {
}
}

View File

@@ -0,0 +1,7 @@
package com.amazon.avod.fsm;
public abstract class StateBase<S, T> {
// This method orginally has protected access (modified in patch code).
public void doTrigger(Trigger<T> trigger) {
}
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.fsm;
public interface Trigger<T> {
}

View File

@@ -0,0 +1,7 @@
package com.amazon.avod.media;
public final class TimeSpan {
public long getTotalMilliseconds() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,7 @@
package com.amazon.avod.media.ads;
import com.amazon.avod.media.TimeSpan;
public interface AdBreak {
TimeSpan getDurationExcludingAux();
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.media.ads.internal.state;
public abstract class AdBreakState extends AdEnabledPlaybackState {
}

View File

@@ -0,0 +1,18 @@
package com.amazon.avod.media.ads.internal.state;
import com.amazon.avod.media.ads.AdBreak;
import com.amazon.avod.media.TimeSpan;
public class AdBreakTrigger {
public AdBreak getBreak() {
throw new UnsupportedOperationException();
}
public TimeSpan getSeekTarget() {
throw new UnsupportedOperationException();
}
public TimeSpan getSeekStartPosition() {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,8 @@
package com.amazon.avod.media.ads.internal.state;
import com.amazon.avod.fsm.StateBase;
import com.amazon.avod.media.playback.state.PlayerStateType;
import com.amazon.avod.media.playback.state.trigger.PlayerTriggerType;
public class AdEnabledPlaybackState extends StateBase<PlayerStateType, PlayerTriggerType> {
}

View File

@@ -0,0 +1,5 @@
package com.amazon.avod.media.ads.internal.state;
public enum AdEnabledPlayerTriggerType {
NO_MORE_ADS_SKIP_TRANSITION
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.media.ads.internal.state;
public class ServerInsertedAdBreakState extends AdBreakState {
}

View File

@@ -0,0 +1,7 @@
package com.amazon.avod.media.playback;
public interface VideoPlayer {
long getCurrentPosition();
void seekTo(long positionMs);
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.media.playback.state;
public interface PlayerStateType {
}

View File

@@ -0,0 +1,4 @@
package com.amazon.avod.media.playback.state.trigger;
public interface PlayerTriggerType {
}

View File

@@ -20,9 +20,7 @@ import androidx.annotation.RequiresApi;
import java.net.MalformedURLException;
import java.net.URL;
/**
* @noinspection unused
*/
@SuppressWarnings("unused")
public class GmsCoreSupport {
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";

View File

@@ -342,9 +342,12 @@ public abstract class Setting<T> {
/**
* Identical to calling {@link #save(Object)} using {@link #defaultValue}.
*
* @return The newly saved default value.
*/
public void resetToDefault() {
public T resetToDefault() {
save(defaultValue);
return defaultValue;
}
/**

View File

@@ -0,0 +1,43 @@
package app.revanced.extension.spotify.misc.privacy;
import android.net.Uri;
import java.util.List;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public final class SanitizeSharingLinksPatch {
/**
* Parameters that are considered undesirable and should be stripped away.
*/
private static final List<String> SHARE_PARAMETERS_TO_REMOVE = List.of(
"si", // Share tracking parameter.
"utm_source" // Share source, such as "copy-link".
);
/**
* Injection point.
*/
public static String sanitizeUrl(String url) {
try {
Uri uri = Uri.parse(url);
Uri.Builder builder = uri.buildUpon().clearQuery();
for (String paramName : uri.getQueryParameterNames()) {
if (!SHARE_PARAMETERS_TO_REMOVE.contains(paramName)) {
for (String value : uri.getQueryParameters(paramName)) {
builder.appendQueryParameter(paramName, value);
}
}
}
return builder.build().toString();
} catch (Exception ex) {
Logger.printException(() -> "sanitizeUrl failure", ex);
return url;
}
}
}

View File

@@ -2,6 +2,7 @@ package app.revanced.extension.tiktok.feedfilter;
import com.ss.android.ugc.aweme.feed.model.Aweme;
import com.ss.android.ugc.aweme.feed.model.FeedItemList;
import com.ss.android.ugc.aweme.follow.presenter.FollowFeedList;
import java.util.Iterator;
import java.util.List;
@@ -13,22 +14,41 @@ public final class FeedItemsFilter {
new StoryFilter(),
new ImageVideoFilter(),
new ViewCountFilter(),
new LikeCountFilter()
new LikeCountFilter(),
new ShopFilter()
);
public static void filter(FeedItemList feedItemList) {
Iterator<Aweme> feedItemListIterator = feedItemList.items.iterator();
while (feedItemListIterator.hasNext()) {
Aweme item = feedItemListIterator.next();
if (item == null) continue;
filterFeedList(feedItemList.items, item -> item);
}
for (IFilter filter : FILTERS) {
boolean enabled = filter.getEnabled();
if (enabled && filter.getFiltered(item)) {
feedItemListIterator.remove();
break;
}
public static void filter(FollowFeedList followFeedList) {
filterFeedList(followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null);
}
private static <T> void filterFeedList(List<T> list, AwemeExtractor<T> extractor) {
// Could be simplified with removeIf() but requires Android 7.0+ while TikTok supports 4.0+.
Iterator<T> iterator = list.iterator();
while (iterator.hasNext()) {
T container = iterator.next();
Aweme item = extractor.extract(container);
if (item != null && shouldFilter(item)) {
iterator.remove();
}
}
}
}
private static boolean shouldFilter(Aweme item) {
for (IFilter filter : FILTERS) {
if (filter.getEnabled() && filter.getFiltered(item)) {
return true;
}
}
return false;
}
@FunctionalInterface
interface AwemeExtractor<T> {
Aweme extract(T source);
}
}

View File

@@ -0,0 +1,17 @@
package app.revanced.extension.tiktok.feedfilter;
import app.revanced.extension.tiktok.settings.Settings;
import com.ss.android.ugc.aweme.feed.model.Aweme;
public class ShopFilter implements IFilter {
private static final String SHOP_INFO = "placeholder_product_id";
@Override
public boolean getEnabled() {
return Settings.HIDE_SHOP.get();
}
@Override
public boolean getFiltered(Aweme item) {
return item.getShareUrl().contains(SHOP_INFO);
}
}

View File

@@ -11,6 +11,7 @@ import app.revanced.extension.shared.settings.StringSetting;
public class Settings extends BaseSettings {
public static final BooleanSetting REMOVE_ADS = new BooleanSetting("remove_ads", TRUE, true);
public static final BooleanSetting HIDE_LIVE = new BooleanSetting("hide_live", FALSE, true);
public static final BooleanSetting HIDE_SHOP = new BooleanSetting("hide_shop", FALSE, true);
public static final BooleanSetting HIDE_STORY = new BooleanSetting("hide_story", FALSE, true);
public static final BooleanSetting HIDE_IMAGE = new BooleanSetting("hide_image", FALSE, true);
public static final StringSetting MIN_MAX_VIEWS = new StringSetting("min_max_views", "0-" + Long.MAX_VALUE, true);

View File

@@ -26,6 +26,11 @@ public class FeedFilterPreferenceCategory extends ConditionalPreferenceCategory
"Remove feed ads", "Remove ads from feed.",
Settings.REMOVE_ADS
));
addPreference(new TogglePreference(
context,
"Hide TikTok Shop", "Hide TikTok shop from feed.",
Settings.HIDE_SHOP
));
addPreference(new TogglePreference(
context,
"Hide livestreams", "Hide livestreams from feed.",

View File

@@ -33,4 +33,8 @@ public class Aweme {
public AwemeStatistics getStatistics() {
throw new UnsupportedOperationException("Stub");
}
public String getShareUrl() {
throw new UnsupportedOperationException("Stub");
}
}

View File

@@ -0,0 +1,8 @@
package com.ss.android.ugc.aweme.follow.presenter;
import com.ss.android.ugc.aweme.feed.model.Aweme;
//Dummy class
public class FollowFeed {
public Aweme aweme;
}

View File

@@ -0,0 +1,8 @@
package com.ss.android.ugc.aweme.follow.presenter;
import java.util.List;
//Dummy class
public class FollowFeedList {
public List<FollowFeed> mItems;
}

View File

@@ -2,6 +2,8 @@ package app.revanced.extension.youtube;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.Window;
import androidx.annotation.Nullable;
@@ -102,4 +104,21 @@ public class ThemeHelper {
return Utils.getColorFromString(colorName);
}
/**
* Sets the system navigation bar color for the activity.
* Applies the background color obtained from {@link #getBackgroundColor()} to the navigation bar.
* For Android 10 (API 29) and above, enforces navigation bar contrast to ensure visibility.
*/
public static void setNavigationBarColor(@Nullable Window window) {
if (window == null) {
Logger.printDebug(() -> "Cannot set navigation bar color, window is null");
return;
}
window.setNavigationBarColor(getBackgroundColor());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.setNavigationBarContrastEnforced(true);
}
}
}

View File

@@ -0,0 +1,28 @@
package app.revanced.extension.youtube.patches;
import static app.revanced.extension.shared.StringRef.sf;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
@SuppressWarnings("unused")
public class AccountCredentialsInvalidTextPatch {
/**
* Injection point.
*/
public static String getOfflineNetworkErrorString(String original) {
try {
if (Utils.isNetworkConnected()) {
Logger.printDebug(() -> "Network appears to be online, but app is showing offline error");
return '\n' + sf("microg_offline_account_login_error").toString();
}
Logger.printDebug(() -> "Network is offline");
} catch (Exception ex) {
Logger.printException(() -> "getOfflineNetworkErrorString failure", ex);
}
return original;
}
}

View File

@@ -9,6 +9,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -81,6 +82,13 @@ public final class ChangeStartPagePatch {
}
}
public static class ChangeStartPageTypeAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return Settings.CHANGE_START_PAGE.get() != StartPage.DEFAULT;
}
}
/**
* Intent action when YouTube is cold started from the launcher.
* <p>
@@ -93,6 +101,8 @@ public final class ChangeStartPagePatch {
private static final StartPage START_PAGE = Settings.CHANGE_START_PAGE.get();
private static final boolean CHANGE_START_PAGE_ALWAYS = Settings.CHANGE_START_PAGE_ALWAYS.get();
/**
* There is an issue where the back button on the toolbar doesn't work properly.
* As a workaround for this issue, instead of overriding the browserId multiple times, just override it once.
@@ -104,13 +114,13 @@ public final class ChangeStartPagePatch {
return original;
}
if (appLaunched) {
if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
Logger.printDebug(() -> "Ignore override browseId as the app already launched");
return original;
}
appLaunched = true;
Logger.printDebug(() -> "Changing browseId to " + START_PAGE.id);
Logger.printDebug(() -> "Changing browseId to: " + START_PAGE.id);
return START_PAGE.id;
}
@@ -125,14 +135,14 @@ public final class ChangeStartPagePatch {
return;
}
if (appLaunched) {
if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
Logger.printDebug(() -> "Ignore override intent action as the app already launched");
return;
}
appLaunched = true;
String intentAction = START_PAGE.id;
Logger.printDebug(() -> "Changing intent action to " + intentAction);
Logger.printDebug(() -> "Changing intent action to: " + intentAction);
intent.setAction(intentAction);
}
}

View File

@@ -17,8 +17,7 @@ public class CustomPlayerOverlayOpacityPatch {
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_player_overlay_opacity_invalid_toast"));
Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
opacity = Settings.PLAYER_OVERLAY_OPACITY.defaultValue;
opacity = Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
}
PLAYER_OVERLAY_OPACITY_LEVEL = (opacity * 255) / 100;

View File

@@ -162,8 +162,7 @@ public final class MiniplayerPatch {
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
Settings.MINIPLAYER_OPACITY.resetToDefault();
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
opacity = Settings.MINIPLAYER_OPACITY.resetToDefault();
}
OPACITY_LEVEL = (opacity * 255) / 100;

View File

@@ -2,6 +2,8 @@ package app.revanced.extension.youtube.patches;
import android.app.Activity;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -76,7 +78,7 @@ public class ShortsAutoplayPatch {
/**
* Injection point.
*/
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
public static Enum<?> changeShortsRepeatBehavior(@Nullable Enum<?> original) {
try {
final boolean autoplay;
@@ -98,17 +100,35 @@ public class ShortsAutoplayPatch {
: ShortsLoopBehavior.REPEAT;
if (behavior.ytEnumValue != null) {
Logger.printDebug(() -> behavior.ytEnumValue == original
? "Changing Shorts repeat behavior from: " + original.name() + " to: " + behavior.ytEnumValue
: "Behavior setting is same as original. Using original: " + original.name()
);
Logger.printDebug(() -> {
String name = (original == null ? "unknown (null)" : original.name());
return behavior == original
? "Behavior setting is same as original. Using original: " + name
: "Changing Shorts repeat behavior from: " + name + " to: " + behavior.name();
});
return behavior.ytEnumValue;
}
if (original == null) {
// Cannot return null, as null is used to indicate Short was auto played.
// Unpatched app replaces null with unknown enum type (appears to fix for bad api data).
Enum<?> unknown = ShortsLoopBehavior.UNKNOWN.ytEnumValue;
Logger.printDebug(() -> "Original is null, returning: " + unknown.name());
return unknown;
}
} catch (Exception ex) {
Logger.printException(() -> "changeShortsRepeatState failure", ex);
Logger.printException(() -> "changeShortsRepeatBehavior failure", ex);
}
return original;
}
/**
* Injection point.
*/
public static boolean isAutoPlay(Enum<?> original) {
return ShortsLoopBehavior.SINGLE_PLAY.ytEnumValue == original;
}
}

View File

@@ -31,7 +31,7 @@ final class ButtonsFilter extends Filter {
bufferFilterPathGroup = new StringFilterGroup(
null,
"|ContainerType|button.eml|"
"|ContainerType|button.eml"
);
addPathCallbacks(
@@ -43,7 +43,7 @@ final class ButtonsFilter extends Filter {
),
new StringFilterGroup(
Settings.HIDE_DOWNLOAD_BUTTON,
"|download_button.eml|"
"|download_button.eml"
),
new StringFilterGroup(
Settings.HIDE_PLAYLIST_BUTTON,
@@ -51,7 +51,7 @@ final class ButtonsFilter extends Filter {
),
new StringFilterGroup(
Settings.HIDE_CLIP_BUTTON,
"|clip_button.eml|"
"|clip_button.eml"
)
);
@@ -68,15 +68,19 @@ final class ButtonsFilter extends Filter {
Settings.HIDE_REMIX_BUTTON,
"yt_outline_youtube_shorts_plus"
),
new ByteArrayFilterGroup(
Settings.HIDE_THANKS_BUTTON,
"yt_outline_dollar_sign_heart"
),
new ByteArrayFilterGroup(
Settings.HIDE_ASK_BUTTON,
"yt_fill_spark"
),
// Check for clip button both here and using a path filter,
// as there's a chance the path is a generic action button and won't contain 'clip_button'
new ByteArrayFilterGroup(
Settings.HIDE_CLIP_BUTTON,
"yt_outline_scissors"
),
new ByteArrayFilterGroup(
Settings.HIDE_THANKS_BUTTON,
"yt_outline_dollar_sign_heart"
)
);
}

View File

@@ -75,7 +75,10 @@ public final class LayoutComponentsFilter extends Filter {
"post_base_wrapper_slim.eml",
"poll_post_root.eml",
"videos_post_root.eml",
"post_shelf_slim.eml"
"post_shelf_slim.eml",
"videos_post_responsive_root.eml",
"text_post_responsive_root.eml",
"poll_post_responsive_root.eml"
);
final var communityGuidelines = new StringFilterGroup(
@@ -211,7 +214,7 @@ public final class LayoutComponentsFilter extends Filter {
compactChannelBarInnerButton = new StringFilterGroup(
null,
"|button.eml|"
"|button.eml"
);
joinMembershipButton = new ByteArrayFilterGroup(

View File

@@ -87,6 +87,10 @@ public final class LithoFilterPatch {
* the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads.
*/
private static final ThreadLocal<ByteBuffer> bufferThreadLocal = new ThreadLocal<>();
/**
* Results of calling {@link #filter(String, StringBuilder)}.
*/
private static final ThreadLocal<Boolean> filterResult = new ThreadLocal<>();
static {
for (Filter filter : filters) {
@@ -140,11 +144,22 @@ public final class LithoFilterPatch {
}
}
/**
* Injection point.
*/
public static boolean shouldFilter() {
Boolean shouldFilter = filterResult.get();
return shouldFilter != null && shouldFilter;
}
/**
* Injection point. Called off the main thread, and commonly called by multiple threads at the same time.
*/
@SuppressWarnings("unused")
public static boolean filter(@Nullable String lithoIdentifier, @NonNull StringBuilder pathBuilder) {
public static void filter(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
filterResult.set(handleFiltering(lithoIdentifier, pathBuilder));
}
private static boolean handleFiltering(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
try {
if (pathBuilder.length() == 0) {
return false;

View File

@@ -40,7 +40,7 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
addPathCallbacks(
videoQualityMenuFooter,
new StringFilterGroup(null, "overflow_menu_item.eml|")
new StringFilterGroup(null, "overflow_menu_item.eml")
);
flyoutFilterGroupList.addAll(

View File

@@ -1,6 +1,5 @@
package app.revanced.extension.youtube.patches.components;
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.view.View;
@@ -52,6 +51,7 @@ public final class ShortsFilter extends Filter {
private final StringFilterGroup suggestedAction;
private final ByteArrayFilterGroupList suggestedActionsGroupList = new ByteArrayFilterGroupList();
private final StringFilterGroup shortsActionBar;
private final StringFilterGroup actionButton;
private final ByteArrayFilterGroupList videoActionButtonGroupList = new ByteArrayFilterGroupList();
@@ -141,6 +141,16 @@ public final class ShortsFilter extends Filter {
"like_fountain.eml"
);
StringFilterGroup likeButton = new StringFilterGroup(
Settings.HIDE_SHORTS_LIKE_BUTTON,
"shorts_like_button.eml"
);
StringFilterGroup dislikeButton = new StringFilterGroup(
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
"shorts_dislike_button.eml"
);
joinButton = new StringFilterGroup(
Settings.HIDE_SHORTS_JOIN_BUTTON,
"sponsor_button"
@@ -156,9 +166,15 @@ public final class ShortsFilter extends Filter {
"reel_player_disclosure.eml"
);
shortsActionBar = new StringFilterGroup(
null,
"shorts_action_bar.eml"
);
actionButton = new StringFilterGroup(
null,
"shorts_video_action_button.eml"
// Can be simply 'button.eml' or 'shorts_video_action_button.eml'
"button.eml"
);
suggestedAction = new StringFilterGroup(
@@ -167,27 +183,16 @@ public final class ShortsFilter extends Filter {
);
addPathCallbacks(
shortsCompactFeedVideoPath, suggestedAction, actionButton, joinButton, subscribeButton,
paidPromotionButton, pausedOverlayButtons, channelBar, fullVideoLinkLabel, videoTitle,
reelSoundMetadata, soundButton, infoPanel, stickers, likeFountain
shortsCompactFeedVideoPath, joinButton, subscribeButton, paidPromotionButton,
shortsActionBar, suggestedAction, pausedOverlayButtons, channelBar,
fullVideoLinkLabel, videoTitle, reelSoundMetadata, soundButton, infoPanel,
stickers, likeFountain, likeButton, dislikeButton
);
//
// Action buttons
// All other action buttons.
//
videoActionButtonGroupList.addAll(
// This also appears as the path item 'shorts_like_button.eml'
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_LIKE_BUTTON,
"reel_like_button",
"reel_like_toggled_button"
),
// This also appears as the path item 'shorts_dislike_button.eml'
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
"reel_dislike_button",
"reel_dislike_toggled_button"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_COMMENTS_BUTTON,
"reel_comment_button"
@@ -286,9 +291,11 @@ public final class ShortsFilter extends Filter {
return false;
}
// Video action buttons (like, dislike, comment, share, remix) have the same path.
if (matchedGroup == actionButton) {
if (videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
// Video action buttons (comment, share, remix) have the same path.
// Like and dislike are separate path filters and don't require buffer searching.
if (matchedGroup == shortsActionBar) {
if (actionButton.check(path).isFiltered()
&& videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
@@ -392,37 +399,6 @@ public final class ShortsFilter extends Filter {
return original;
}
// region Hide the buttons in older versions of YouTube. New versions use Litho.
public static void hideLikeButton(final View likeButtonView) {
// Cannot set the visibility to gone for like/dislike,
// as some other unknown YT code also sets the visibility after this hook.
//
// Setting the view to 0dp works, but that leaves a blank space where
// the button was (only relevant for dislikes button).
//
// Instead remove the view from the parent.
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView);
}
public static void hideDislikeButton(final View dislikeButtonView) {
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView);
}
public static void hideShortsCommentsButton(final View commentsButtonView) {
hideViewUnderCondition(Settings.HIDE_SHORTS_COMMENTS_BUTTON, commentsButtonView);
}
public static void hideShortsRemixButton(final View remixButtonView) {
hideViewUnderCondition(Settings.HIDE_SHORTS_REMIX_BUTTON, remixButtonView);
}
public static void hideShortsShareButton(final View shareButtonView) {
hideViewUnderCondition(Settings.HIDE_SHORTS_SHARE_BUTTON, shareButtonView);
}
// endregion
public static void setNavigationBar(PivotBar view) {
pivotBarRef = new WeakReference<>(view);
}

View File

@@ -54,12 +54,12 @@ public class CustomPlaybackSpeedPatch {
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();
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.resetToDefault();
}
loadCustomSpeeds();

View File

@@ -84,6 +84,7 @@ public class LicenseActivityHook {
public static void initialize(Activity licenseActivity) {
try {
ThemeHelper.setActivityTheme(licenseActivity);
ThemeHelper.setNavigationBarColor(licenseActivity.getWindow());
licenseActivity.setContentView(getResourceIdentifier(
"revanced_settings_with_toolbar", "layout"));
@@ -126,7 +127,7 @@ public class LicenseActivityHook {
// This is required to fix submenu title alignment issue with Android ASOP 15+
ViewGroup toolBarParent = activity.findViewById(
getResourceIdentifier("revanced_toolbar_parent", "id"));
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent, "revanced_toolbar");
toolbarLayoutParams = dummyToolbar.getLayoutParams();
toolBarParent.removeView(dummyToolbar);
@@ -149,5 +150,4 @@ public class LicenseActivityHook {
toolBarParent.addView(toolbar, 0);
}
}
}

View File

@@ -7,6 +7,7 @@ import static app.revanced.extension.shared.settings.Setting.migrateOldSettingTo
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.settings.Setting.parentsAny;
import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormFactor;
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.ChangeStartPageTypeAvailability;
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;
@@ -24,6 +25,7 @@ import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehavi
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
import android.graphics.Color;
@@ -197,6 +199,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
public static final BooleanSetting HIDE_ASK_BUTTON = new BooleanSetting("revanced_hide_ask_button", FALSE);
// Player flyout menu items
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
@@ -222,6 +225,8 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final BooleanSetting 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 BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
new ChangeStartPageTypeAvailability());
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
// Custom filter
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
@@ -320,12 +325,14 @@ public class Settings extends BaseSettings {
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL,true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final StringSetting SWIPE_OVERLAY_PROGRESS_COLOR = new StringSetting("revanced_swipe_overlay_progress_color", "#FFFFFF", true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));

View File

@@ -138,6 +138,9 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
.findViewById(android.R.id.content)
.getParent();
// Fix the system navigation bar color for submenus.
ThemeHelper.setNavigationBarColor(preferenceScreenDialog.getWindow());
// Fix edge-to-edge screen with Android 15 and YT 19.45+
// https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

View File

@@ -1,95 +1,98 @@
package app.revanced.extension.youtube.swipecontrols
import android.annotation.SuppressLint
import android.graphics.Color
import app.revanced.extension.shared.Logger
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
/**
* provider for configuration for volume and brightness swipe controls
* Provides configuration settings for volume and brightness swipe controls in the YouTube player.
* Manages enabling/disabling gestures, overlay appearance, and behavior preferences.
*/
class SwipeControlsConfigurationProvider {
//region swipe enable
//region swipe enable
/**
* should swipe controls be enabled? (global setting)
* Indicates whether swipe controls are enabled globally.
* Returns true if either volume or brightness controls are enabled and the video is in fullscreen mode.
*/
val enableSwipeControls: Boolean
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
/**
* should swipe controls for volume be enabled?
* Indicates whether swipe controls for adjusting volume are enabled.
*/
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
/**
* should swipe controls for volume be enabled?
* Indicates whether swipe controls for adjusting brightness are enabled.
*/
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
/**
* is the video player currently in fullscreen mode?
* Checks if the video player is currently in fullscreen mode.
*/
private val isFullscreenVideo: Boolean
get() = PlayerType.current == PlayerType.WATCH_WHILE_FULLSCREEN
//endregion
//endregion
//region keys enable
//region keys enable
/**
* should volume key controls be overwritten? (global setting)
* Indicates whether volume key controls should be overridden by swipe controls.
* Returns true if volume controls are enabled and the video is in fullscreen mode.
*/
val overwriteVolumeKeyControls: Boolean
get() = enableVolumeControls && isFullscreenVideo
//endregion
//endregion
//region gesture adjustments
//region gesture adjustments
/**
* should press-to-swipe be enabled?
* Indicates whether press-to-swipe mode is enabled, requiring a press before swiping to activate controls.
*/
val shouldEnablePressToSwipe: Boolean
get() = Settings.SWIPE_PRESS_TO_ENGAGE.get()
/**
* threshold for swipe detection
* this may be called rapidly in onScroll, so we have to load it once and then leave it constant
* The threshold for detecting swipe gestures, in pixels.
* Loaded once to ensure consistent behavior during rapid scroll events.
*/
val swipeMagnitudeThreshold: Int
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
/**
* How much volume will change by single swipe.
* If it is set to 0, it will reset to the default value because 0 would disable swiping.
* */
* The sensitivity of volume swipe gestures, determining how much volume changes per swipe.
* Resets to default if set to 0, as it would disable swiping.
*/
val volumeSwipeSensitivity: Int
get() {
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
if (sensitivity < 1) {
Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
return Settings.SWIPE_VOLUME_SENSITIVITY.get()
return Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
}
return sensitivity
}
//endregion
//endregion
//region overlay adjustments
//region overlay adjustments
/**
* should the overlay enable haptic feedback?
* Indicates whether haptic feedback should be enabled for swipe control interactions.
*/
val shouldEnableHapticFeedback: Boolean
get() = Settings.SWIPE_HAPTIC_FEEDBACK.get()
/**
* how long the overlay should be shown on changes
* The duration in milliseconds that the overlay should remain visible after a change.
*/
val overlayShowTimeoutMillis: Long
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
/**
* Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency.
* If the opacity value is out of range, it resets to the default and displays a warning message.
* The background opacity of the overlay, converted from a percentage (0-100) to an alpha value (0-255).
* Resets to default and shows a toast if the value is out of range.
*/
val overlayBackgroundOpacity: Int
get() {
@@ -97,8 +100,7 @@ class SwipeControlsConfigurationProvider {
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 = Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
}
opacity = opacity * 255 / 100
@@ -106,55 +108,125 @@ class SwipeControlsConfigurationProvider {
}
/**
* The color of the progress overlay.
* The color of the progress bar in the overlay.
* Resets to default and shows a toast if the color string is invalid or empty.
*/
val overlayProgressColor: Int
get() = 0xBFFFFFFF.toInt()
get() {
try {
@SuppressLint("UseKtx")
val color = Color.parseColor(Settings.SWIPE_OVERLAY_PROGRESS_COLOR.get())
return (0xBF000000.toInt() or (color and 0xFFFFFF))
} catch (ex: IllegalArgumentException) {
Logger.printDebug({ "Could not parse color" }, ex)
Utils.showToastLong(str("revanced_swipe_overlay_progress_color_invalid_toast"))
Settings.SWIPE_OVERLAY_PROGRESS_COLOR.resetToDefault()
return overlayProgressColor // Recursively return.
}
}
/**
* The color used for the background of the progress overlay fill.
* The background color used for the filled portion of the progress bar in the overlay.
*/
val overlayFillBackgroundPaint: Int
get() = 0x80D3D3D3.toInt()
/**
* The color used for the text and icons in the overlay.
* The color used for text and icons in the overlay.
*/
val overlayTextColor: Int
get() = Color.WHITE
/**
* A flag that determines if the overlay should only show the icon.
* The text size in the overlay, in density-independent pixels (dp).
* Must be between 1 and 30 dp; resets to default and shows a toast if invalid.
*/
val overlayShowOverlayMinimalStyle: Boolean
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get()
val overlayTextSize: Int
get() {
val size = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
if (size < 1 || size > 30) {
Utils.showToastLong(str("revanced_swipe_text_overlay_size_invalid_toast"))
return Settings.SWIPE_OVERLAY_TEXT_SIZE.resetToDefault()
}
return size
}
/**
* A flag that determines if the progress bar should be circular.
* Defines the style of the swipe controls overlay, determining its layout and appearance.
*
* @property isMinimal Indicates whether the style is minimalistic, omitting detailed progress indicators.
* @property isHorizontalMinimalCenter Indicates whether the style is a minimal horizontal bar centered vertically.
* @property isCircular Indicates whether the style uses a circular progress bar.
* @property isVertical Indicates whether the style uses a vertical progress bar.
*/
val isCircularProgressBar: Boolean
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get()
//endregion
@Suppress("unused")
enum class SwipeOverlayStyle(
val isMinimal: Boolean = false,
val isHorizontalMinimalCenter: Boolean = false,
val isCircular: Boolean = false,
val isVertical: Boolean = false
) {
/**
* A full horizontal progress bar with detailed indicators.
*/
HORIZONTAL,
//region behaviour
/**
* A minimal horizontal progress bar positioned at the top.
*/
HORIZONTAL_MINIMAL_TOP(isMinimal = true),
/**
* A minimal horizontal progress bar centered vertically.
*/
HORIZONTAL_MINIMAL_CENTER(isMinimal = true, isHorizontalMinimalCenter = true),
/**
* A full circular progress bar with detailed indicators.
*/
CIRCULAR(isCircular = true),
/**
* A minimal circular progress bar.
*/
CIRCULAR_MINIMAL(isMinimal = true, isCircular = true),
/**
* A full vertical progress bar with detailed indicators.
*/
VERTICAL(isVertical = true),
/**
* A minimal vertical progress bar.
*/
VERTICAL_MINIMAL(isMinimal = true, isVertical = true)
}
/**
* should the brightness be saved and restored when exiting or entering fullscreen
* The current style of the overlay, determining its layout and appearance.
*/
val overlayStyle: SwipeOverlayStyle
get() = Settings.SWIPE_OVERLAY_STYLE.get()
//endregion
//region behaviour
/**
* Indicates whether the brightness level should be saved and restored when entering or exiting fullscreen mode.
*/
val shouldSaveAndRestoreBrightness: Boolean
get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get()
/**
* should auto-brightness be enabled at the lowest value of the brightness gesture
* Indicates whether auto-brightness should be enabled when the brightness gesture reaches its lowest value.
*/
val shouldLowestValueEnableAutoBrightness: Boolean
get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get()
/**
* variable that stores the brightness gesture value in the settings
* The saved brightness value for the swipe gesture, used to restore brightness in fullscreen mode.
*/
var savedScreenBrightnessValue: Float
get() = Settings.SWIPE_BRIGHTNESS_VALUE.get()
set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value)
//endregion
}
//endregion
}

View File

@@ -23,9 +23,7 @@ import java.lang.ref.WeakReference
/**
* The main controller for volume and brightness swipe controls.
* note that the superclass is overwritten to the superclass of the MainActivity at patch time
*
* @smali Lapp/revanced/extension/swipecontrols/SwipeControlsHostActivity;
* note that the superclass is overwritten to the superclass of the MainActivity at patch time.
*/
class SwipeControlsHostActivity : Activity() {
/**

View File

@@ -1,8 +1,11 @@
package app.revanced.extension.youtube.swipecontrols.views
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.os.Handler
@@ -11,14 +14,23 @@ import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import android.view.View
import android.widget.RelativeLayout
import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
import kotlin.math.min
import kotlin.math.max
import kotlin.math.round
/**
* Main overlay layout for displaying volume and brightness level with both circular and horizontal progress bars.
* Convert dp to pixels based on system display density.
*/
fun Float.toDisplayPixels(): Float {
return this * Resources.getSystem().displayMetrics.density
}
/**
* Main overlay layout for displaying volume and brightness level with circular, horizontal and vertical progress bars.
*/
class SwipeControlsOverlayLayout(
context: Context,
@@ -51,18 +63,21 @@ class SwipeControlsOverlayLayout(
// Initialize progress bars
private val circularProgressView: CircularProgressView
private val horizontalProgressView: HorizontalProgressView
private val verticalBrightnessProgressView: VerticalProgressView
private val verticalVolumeProgressView: VerticalProgressView
init {
// Initialize circular progress bar
circularProgressView = CircularProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayShowOverlayMinimalStyle,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor
config.overlayTextColor,
config.overlayTextSize
).apply {
layoutParams = LayoutParams(300, 300).apply {
layoutParams = LayoutParams(100f.toDisplayPixels().toInt(), 100f.toDisplayPixels().toInt()).apply {
addRule(CENTER_IN_PARENT, TRUE)
}
visibility = GONE // Initially hidden
@@ -71,22 +86,65 @@ class SwipeControlsOverlayLayout(
// Initialize horizontal progress bar
val screenWidth = resources.displayMetrics.widthPixels
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
val layoutWidth = (screenWidth * 4 / 5).toInt() // Cap at ~360dp
horizontalProgressView = HorizontalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayShowOverlayMinimalStyle,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor
config.overlayTextColor,
config.overlayTextSize
).apply {
layoutParams = LayoutParams(layoutWidth, 100).apply {
layoutParams = LayoutParams(layoutWidth, 32f.toDisplayPixels().toInt()).apply {
addRule(CENTER_HORIZONTAL)
topMargin = 40 // Top margin
if (config.overlayStyle.isHorizontalMinimalCenter) {
addRule(CENTER_VERTICAL)
} else {
topMargin = 20f.toDisplayPixels().toInt()
}
}
visibility = GONE // Initially hidden
}
addView(horizontalProgressView)
// Initialize vertical progress bar for brightness (right side)
verticalBrightnessProgressView = VerticalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
).apply {
layoutParams = LayoutParams(40f.toDisplayPixels().toInt(), 150f.toDisplayPixels().toInt()).apply {
addRule(ALIGN_PARENT_RIGHT)
rightMargin = 40f.toDisplayPixels().toInt()
addRule(CENTER_VERTICAL)
}
visibility = GONE // Initially hidden
}
addView(verticalBrightnessProgressView)
// Initialize vertical progress bar for volume (left side)
verticalVolumeProgressView = VerticalProgressView(
context,
config.overlayBackgroundOpacity,
config.overlayStyle.isMinimal,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor,
config.overlayTextSize
).apply {
layoutParams = LayoutParams(40f.toDisplayPixels().toInt(), 150f.toDisplayPixels().toInt()).apply {
addRule(ALIGN_PARENT_LEFT)
leftMargin = 40f.toDisplayPixels().toInt()
addRule(CENTER_VERTICAL)
}
visibility = GONE // Initially hidden
}
addView(verticalVolumeProgressView)
}
// Handler and callback for hiding progress bars
@@ -94,6 +152,8 @@ class SwipeControlsOverlayLayout(
private val feedbackHideCallback = Runnable {
circularProgressView.visibility = GONE
horizontalProgressView.visibility = GONE
verticalBrightnessProgressView.visibility = GONE
verticalVolumeProgressView.visibility = GONE
}
/**
@@ -103,7 +163,11 @@ class SwipeControlsOverlayLayout(
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
val viewToShow = when {
config.overlayStyle.isCircular -> circularProgressView
config.overlayStyle.isVertical -> if (isBrightness) verticalBrightnessProgressView else verticalVolumeProgressView
else -> horizontalProgressView
}
viewToShow.apply {
setProgress(progress, max, value, isBrightness)
this.icon = icon
@@ -126,7 +190,9 @@ class SwipeControlsOverlayLayout(
// Handle brightness change
override fun onBrightnessChanged(brightness: Double) {
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
val displayText = if (config.overlayStyle.isVertical) "А"
else str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text")
showFeedbackView(displayText, 0, 100, autoBrightnessIcon, isBrightness = true)
} else {
val brightnessValue = round(brightness).toInt()
val icon = when {
@@ -135,7 +201,8 @@ class SwipeControlsOverlayLayout(
brightnessValue < 75 -> highBrightnessIcon
else -> fullBrightnessIcon
}
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
val displayText = if (config.overlayStyle.isVertical) "$brightnessValue" else "$brightnessValue%"
showFeedbackView(displayText, brightnessValue, 100, icon, isBrightness = true)
}
}
@@ -156,11 +223,12 @@ class SwipeControlsOverlayLayout(
*/
abstract class AbstractProgressView(
context: Context,
protected val overlayBackgroundOpacity: Int,
protected val overlayShowOverlayMinimalStyle: Boolean,
protected val overlayProgressColor: Int,
protected val overlayFillBackgroundPaint: Int,
protected val overlayTextColor: Int,
overlayBackgroundOpacity: Int,
protected val isMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
private val overlayTextColor: Int,
protected val overlayTextSize: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
@@ -174,26 +242,25 @@ abstract class AbstractProgressView(
}
// Initialize paints
public val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
public val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
public val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 6f.toDisplayPixels())
val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor
textAlign = Paint.Align.CENTER
textSize = 40f // Can adjust based on need
textSize = overlayTextSize.toFloat().toDisplayPixels()
}
// Rect for text measurement
protected val textBounds = Rect()
protected var progress = 0
protected var maxProgress = 100
protected var displayText: String = "0"
protected var isBrightness = true
public var icon: Drawable? = null
var icon: Drawable? = null
init {
// Stroke widths are now set in createPaint for progressPaint and fillBackgroundPaint
}
fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
open fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
progress = value
maxProgress = max
displayText = text
@@ -201,6 +268,11 @@ abstract class AbstractProgressView(
invalidate()
}
protected fun measureTextWidth(text: String, paint: Paint): Int {
paint.getTextBounds(text, 0, text.length, textBounds)
return textBounds.width()
}
override fun onDraw(canvas: Canvas) {
// Base class implementation can be empty
}
@@ -209,34 +281,36 @@ abstract class AbstractProgressView(
/**
* Custom view for rendering a circular progress indicator with icons and text.
*/
@SuppressLint("ViewConstructor")
class CircularProgressView(
context: Context,
overlayBackgroundOpacity: Int,
overlayShowOverlayMinimalStyle: Boolean,
isMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
overlayTextColor: Int,
overlayTextSize: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractProgressView(
context,
overlayBackgroundOpacity,
overlayShowOverlayMinimalStyle,
isMinimalStyle,
overlayProgressColor,
overlayFillBackgroundPaint,
overlayTextColor,
overlayTextSize,
attrs,
defStyleAttr
) {
private val rectF = RectF()
init {
textPaint.textSize = 40f // Override default text size for circular view
progressPaint.strokeWidth = 20f
fillBackgroundPaint.strokeWidth = 20f
progressPaint.strokeCap = Paint.Cap.ROUND
progressPaint.strokeWidth = 6f.toDisplayPixels()
fillBackgroundPaint.strokeWidth = 6f.toDisplayPixels()
progressPaint.strokeCap = Paint.Cap.ROUND
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.STROKE
progressPaint.style = Paint.Style.STROKE
fillBackgroundPaint.style = Paint.Style.STROKE
}
@@ -244,7 +318,8 @@ class CircularProgressView(
super.onDraw(canvas)
val size = min(width, height).toFloat()
rectF.set(20f, 20f, size - 20f, size - 20f)
val inset = 6f.toDisplayPixels()
rectF.set(inset, inset, size - inset, size - inset)
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
@@ -255,124 +330,307 @@ class CircularProgressView(
// Draw the icon in the center.
icon?.let {
val iconSize = if (overlayShowOverlayMinimalStyle) 100 else 80
val iconSize = (if (isMinimalStyle) 36f else 24f).toDisplayPixels().toInt()
val iconX = (width - iconSize) / 2
val iconY = (height / 2) - if (overlayShowOverlayMinimalStyle) 50 else 80
val iconY = if (isMinimalStyle) {
(height - iconSize) / 2
} else {
(height / 2) - 24f.toDisplayPixels().toInt()
}
it.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
it.draw(canvas)
}
// If not a minimal style mode, draw the text inside the ring.
if (!overlayShowOverlayMinimalStyle) {
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
if (!isMinimalStyle) {
canvas.drawText(displayText, width / 2f, height / 2f + 20f.toDisplayPixels(), textPaint)
}
}
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
super.setProgress(value, max, text, isBrightnessMode)
requestLayout()
}
}
/**
* Custom view for rendering a rectangular progress bar with icons and text.
*/
@SuppressLint("ViewConstructor")
class HorizontalProgressView(
context: Context,
overlayBackgroundOpacity: Int,
overlayShowOverlayMinimalStyle: Boolean,
isMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
overlayTextColor: Int,
overlayTextSize: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractProgressView(
context,
overlayBackgroundOpacity,
overlayShowOverlayMinimalStyle,
isMinimalStyle,
overlayProgressColor,
overlayFillBackgroundPaint,
overlayTextColor,
overlayTextSize,
attrs,
defStyleAttr
) {
private val iconSize = 60f
private val padding = 40f
private val iconSize = 20f.toDisplayPixels()
private val padding = 12f.toDisplayPixels()
private var textWidth = 0f
private val progressBarHeight = 3f.toDisplayPixels()
private val progressBarWidth: Float = resources.displayMetrics.widthPixels / 4f
init {
textPaint.textSize = 36f // Override default text size for horizontal view
progressPaint.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
fillBackgroundPaint.style = Paint.Style.FILL
}
/**
* Calculate required width based on content
* @return Required width to display all elements
*/
private fun calculateRequiredWidth(): Float {
textWidth = measureTextWidth(displayText, textPaint).toFloat()
return if (!isMinimalStyle) {
padding + iconSize + padding + progressBarWidth + padding + textWidth + padding
} else {
padding + iconSize + padding + textWidth + padding
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val suggestedWidth = MeasureSpec.getSize(widthMeasureSpec)
val suggestedHeight = MeasureSpec.getSize(heightMeasureSpec)
val height = suggestedHeight
val requiredWidth = calculateRequiredWidth().toInt()
val width = min(max(100, requiredWidth), suggestedWidth)
setMeasuredDimension(width, height)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val width = width.toFloat()
val height = height.toFloat()
val viewWidth = width.toFloat()
val viewHeight = height.toFloat()
val viewHeightHalf = viewHeight / 2
// Radius for rounded corners
val cornerRadius = min(width, height) / 2
textWidth = measureTextWidth(displayText, textPaint).toFloat()
// Calculate the total width for the elements
val minimalElementWidth = 5 * padding + iconSize
val cornerRadius = viewHeightHalf
// Calculate the starting point (X) to center the elements
val minimalStartX = (width - minimalElementWidth) / 2
val startX = padding
val iconEndX = startX + iconSize
// Draw the background
if (!overlayShowOverlayMinimalStyle) {
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
} else {
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
}
val textStartX = (viewWidth - 1.5f * padding - textWidth)
if (!overlayShowOverlayMinimalStyle) {
// Draw the fill background
val startX = 2 * padding + iconSize
val endX = width - 4 * padding
val fillWidth = endX - startX
canvas.drawRoundRect(
0f, 0f, viewWidth, viewHeight,
cornerRadius, cornerRadius, backgroundPaint
)
canvas.drawRoundRect(
startX,
height / 2 - 5f,
endX,
height / 2 + 5f,
10f, 10f,
fillBackgroundPaint
)
// Draw the progress
val progressWidth = (progress.toFloat() / maxProgress) * fillWidth
canvas.drawRoundRect(
startX,
height / 2 - 5f,
startX + progressWidth,
height / 2 + 5f,
10f, 10f,
progressPaint
)
}
// Draw the icon
icon?.let {
val iconX = if (!overlayShowOverlayMinimalStyle) {
padding
} else {
padding + minimalStartX
}
val iconY = height / 2 - iconSize / 2
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
val iconY = viewHeightHalf - iconSize / 2
it.setBounds(
startX.toInt(),
iconY.toInt(),
(startX + iconSize).toInt(),
(iconY + iconSize).toInt()
)
it.draw(canvas)
}
// Draw the text on the right
val textX = if (!overlayShowOverlayMinimalStyle) {
width - 2 * padding
} else {
minimalStartX + minimalElementWidth - 2 * padding
}
val textY = height / 2 + textPaint.textSize / 3
val textY = viewHeightHalf + textPaint.textSize / 3
textPaint.textAlign = Paint.Align.LEFT
// Draw the text
canvas.drawText(displayText, textX, textY, textPaint)
if (isMinimalStyle) {
canvas.drawText(displayText, textStartX, textY, textPaint)
} else {
val progressStartX = iconEndX + padding
val progressEndX = textStartX - padding
val progressWidth = progressEndX - progressStartX
if (progressWidth > 50) {
val progressBarHeightHalf = progressBarHeight / 2.0f
val viewHeightHalfMinusProgressBarHeightHalf = viewHeightHalf - progressBarHeightHalf
val viewHeightHalfPlusProgressBarHeightHalf = viewHeightHalf + progressBarHeightHalf
canvas.drawRoundRect(
progressStartX,
viewHeightHalfMinusProgressBarHeightHalf,
progressEndX,
viewHeightHalfPlusProgressBarHeightHalf,
progressBarHeightHalf,
progressBarHeightHalf,
fillBackgroundPaint
)
val progressValue = (progress.toFloat() / maxProgress) * progressWidth
canvas.drawRoundRect(
progressStartX,
viewHeightHalfMinusProgressBarHeightHalf,
progressStartX + progressValue,
viewHeightHalfPlusProgressBarHeightHalf,
progressBarHeightHalf,
progressBarHeightHalf,
progressPaint
)
}
canvas.drawText(displayText, textStartX, textY, textPaint)
}
}
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
super.setProgress(value, max, text, isBrightnessMode)
requestLayout()
}
}
/**
* Custom view for rendering a vertical progress bar with icons and text.
*/
@SuppressLint("ViewConstructor")
class VerticalProgressView(
context: Context,
overlayBackgroundOpacity: Int,
isMinimalStyle: Boolean,
overlayProgressColor: Int,
overlayFillBackgroundPaint: Int,
overlayTextColor: Int,
overlayTextSize: Int,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractProgressView(
context,
overlayBackgroundOpacity,
isMinimalStyle,
overlayProgressColor,
overlayFillBackgroundPaint,
overlayTextColor,
overlayTextSize,
attrs,
defStyleAttr
) {
private val iconSize = 20f.toDisplayPixels()
private val padding = 12f.toDisplayPixels()
private val progressBarWidth = 3f.toDisplayPixels()
private val progressBarHeight: Float = resources.displayMetrics.widthPixels / 3f
init {
progressPaint.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL
fillBackgroundPaint.style = Paint.Style.FILL
}
/**
* Calculate required height based on content
* @return Required height to display all elements
*/
private fun calculateRequiredHeight(): Float {
return if (!isMinimalStyle) {
padding + iconSize + padding + progressBarHeight + padding + textPaint.textSize + padding
} else {
padding + iconSize + padding + textPaint.textSize + padding
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val suggestedWidth = MeasureSpec.getSize(widthMeasureSpec)
val suggestedHeight = MeasureSpec.getSize(heightMeasureSpec)
val requiredHeight = calculateRequiredHeight().toInt()
val height = min(max(100, requiredHeight), suggestedHeight)
setMeasuredDimension(suggestedWidth, height)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val viewWidth = width.toFloat()
val viewHeight = height.toFloat()
val viewWidthHalf = viewWidth / 2
val cornerRadius = viewWidthHalf
val startY = padding
val iconEndY = startY + iconSize
val textStartY = viewHeight - padding - textPaint.textSize / 2
canvas.drawRoundRect(
0f, 0f, viewWidth, viewHeight,
cornerRadius, cornerRadius, backgroundPaint
)
icon?.let {
val iconX = viewWidthHalf - iconSize / 2
it.setBounds(
iconX.toInt(),
startY.toInt(),
(iconX + iconSize).toInt(),
(startY + iconSize).toInt()
)
it.draw(canvas)
}
val textX = viewWidthHalf
textPaint.textAlign = Paint.Align.CENTER
if (isMinimalStyle) {
canvas.drawText(displayText, textX, textStartY, textPaint)
} else {
val progressStartY = (iconEndY + padding).toFloat()
val progressEndY = textStartY - textPaint.textSize - padding
val progressHeight = progressEndY - progressStartY
if (progressHeight > 50) {
val progressBarWidthHalf = progressBarWidth / 2
val viewHeightHalfMinusProgressBarHeightHalf = viewWidthHalf - progressBarWidthHalf
val viewHeightHalfPlusProgressBarHeightHalf = viewWidthHalf + progressBarWidthHalf
canvas.drawRoundRect(
viewHeightHalfMinusProgressBarHeightHalf,
progressStartY,
viewHeightHalfPlusProgressBarHeightHalf,
progressEndY,
progressBarWidthHalf,
progressBarWidthHalf,
fillBackgroundPaint
)
val progressValue = (progress.toFloat() / maxProgress) * progressHeight
canvas.drawRoundRect(
viewHeightHalfMinusProgressBarHeightHalf,
progressEndY - progressValue,
viewHeightHalfPlusProgressBarHeightHalf,
progressEndY,
progressBarWidthHalf,
progressBarWidthHalf,
progressPaint
)
}
canvas.drawText(displayText, textX, textStartY, textPaint)
}
}
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
super.setProgress(value, max, text, isBrightnessMode)
requestLayout()
}
}

View File

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

View File

@@ -380,6 +380,14 @@ public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatc
public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/pandora/ads/DisableAudioAdsPatchKt {
public static final fun getDisableAudioAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatchKt {
public static final fun getEnableUnlimitedSkipsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatchKt {
public static final fun getGetDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -412,6 +420,14 @@ public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/primevideo/ads/SkipAdsPatchKt {
public static final fun getSkipAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/primevideo/misc/extension/ExtensionPatchKt {
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatchKt {
public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
@@ -642,14 +658,12 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP
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;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;
public fun hashCode ()I
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
@@ -854,6 +868,10 @@ public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatchKt {
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt {
public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1536,6 +1554,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
public final class app/revanced/util/BytecodeUtilsKt {
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/util/smali/ExternalLabel;)V
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z

View File

@@ -7,7 +7,7 @@ import app.revanced.patcher.patch.bytecodePatch
val disableMandatoryLoginPatch = bytecodePatch(
name = "Disable mandatory login",
) {
compatibleWith("com.adobe.lrmobile")
compatibleWith("com.adobe.lrmobile"("10.0.2"))
execute {
isLoggedInFingerprint.method.apply {

View File

@@ -7,7 +7,7 @@ import app.revanced.patcher.patch.bytecodePatch
val unlockPremiumPatch = bytecodePatch(
name = "Unlock premium",
) {
compatibleWith("com.adobe.lrmobile")
compatibleWith("com.adobe.lrmobile"("10.0.2"))
execute {
// Set hasPremium = true.

View File

@@ -0,0 +1,30 @@
package app.revanced.patches.pandora.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.pandora.shared.constructUserDataFingerprint
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val disableAudioAdsPatch = bytecodePatch(
name = "Disable audio ads",
) {
compatibleWith("com.pandora.android")
execute {
constructUserDataFingerprint.method.apply {
// First match is "hasAudioAds".
val hasAudioAdsStringIndex = constructUserDataFingerprint.stringMatches!!.first().index
val moveResultIndex = indexOfFirstInstructionOrThrow(hasAudioAdsStringIndex, Opcode.MOVE_RESULT)
val hasAudioAdsRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
addInstruction(
moveResultIndex + 1,
"const/4 v$hasAudioAdsRegister, 0"
)
}
}
}

View File

@@ -0,0 +1,31 @@
package app.revanced.patches.pandora.misc
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.pandora.shared.constructUserDataFingerprint
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val enableUnlimitedSkipsPatch = bytecodePatch(
name = "Enable unlimited skips",
) {
compatibleWith("com.pandora.android")
execute {
constructUserDataFingerprint.method.apply {
// Last match is "skipLimitBehavior".
val skipLimitBehaviorStringIndex = constructUserDataFingerprint.stringMatches!!.last().index
val moveResultObjectIndex =
indexOfFirstInstructionOrThrow(skipLimitBehaviorStringIndex, Opcode.MOVE_RESULT_OBJECT)
val skipLimitBehaviorRegister = getInstruction<OneRegisterInstruction>(moveResultObjectIndex).registerA
addInstruction(
moveResultObjectIndex + 1,
"const-string v$skipLimitBehaviorRegister, \"unlimited\""
)
}
}
}

View File

@@ -0,0 +1,7 @@
package app.revanced.patches.pandora.shared
import app.revanced.patcher.fingerprint
internal val constructUserDataFingerprint = fingerprint {
strings("hasAudioAds", "skipLimitBehavior")
}

View File

@@ -0,0 +1,33 @@
package app.revanced.patches.primevideo.ads
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val enterServerInsertedAdBreakStateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
parameters("Lcom/amazon/avod/fsm/Trigger;")
returns("V")
opcodes(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CONST_4,
Opcode.CONST_4
)
custom { method, classDef ->
method.name == "enter" && classDef.type == "Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;"
}
}
internal val doTriggerFingerprint = fingerprint {
accessFlags(AccessFlags.PROTECTED)
returns("V")
opcodes(
Opcode.IGET_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.RETURN_VOID
)
custom { method, classDef ->
method.name == "doTrigger" && classDef.type == "Lcom/amazon/avod/fsm/StateBase;"
}
}

View File

@@ -0,0 +1,45 @@
package app.revanced.patches.primevideo.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.primevideo.misc.extension.sharedExtensionPatch
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val skipAdsPatch = bytecodePatch(
name = "Skip ads",
description = "Automatically skips video stream ads.",
) {
compatibleWith("com.amazon.avod.thirdpartyclient"("3.0.403.257"))
dependsOn(sharedExtensionPatch)
// Skip all the logic in ServerInsertedAdBreakState.enter(), which plays all the ad clips in this
// ad break. Instead, force the video player to seek over the entire break and reset the state machine.
execute {
// Force doTrigger() access to public so we can call it from our extension.
doTriggerFingerprint.method.accessFlags = AccessFlags.PUBLIC.value;
val getPlayerIndex = enterServerInsertedAdBreakStateFingerprint.patternMatch!!.startIndex
enterServerInsertedAdBreakStateFingerprint.method.apply {
// Get register that stores VideoPlayer:
// invoke-virtual ->getPrimaryPlayer()
// move-result-object { playerRegister }
val playerRegister = getInstruction<OneRegisterInstruction>(getPlayerIndex + 1).registerA
// Reuse the params from the original method:
// p0 = ServerInsertedAdBreakState
// p1 = AdBreakTrigger
addInstructions(
getPlayerIndex + 2,
"""
invoke-static { p0, p1, v$playerRegister }, Lapp/revanced/extension/primevideo/ads/SkipAdsPatch;->enterServerInsertedAdBreakState(Lcom/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState;Lcom/amazon/avod/media/ads/internal/state/AdBreakTrigger;Lcom/amazon/avod/media/playback/VideoPlayer;)V
return-void
"""
)
}
}
}

View File

@@ -0,0 +1,5 @@
package app.revanced.patches.primevideo.misc.extension
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch("primevideo", applicationInitHook)

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.primevideo.misc.extension
import app.revanced.patches.shared.misc.extension.extensionHook
internal val applicationInitHook = extensionHook {
custom { method, classDef ->
method.name == "onCreate" && classDef.endsWith("/SplashScreenActivity;")
}
}

View File

@@ -52,8 +52,8 @@ fun gmsCoreSupportPatch(
block: BytecodePatchBuilder.() -> Unit = {},
) = bytecodePatch(
name = "GmsCore support",
description = "Allows patched Google apps to run without root and under a different package name " +
"by using GmsCore instead of Google Play Services.",
description = "Allows the app to work without root by using a different package name when patched " +
"using a GmsCore instead of Google Play Services.",
) {
val gmsCoreVendorGroupIdOption = stringOption(
key = "gmsCoreVendorGroupId",

View File

@@ -51,26 +51,6 @@ abstract class BasePreference(
layout?.let { setAttribute("android:layout", layout) }
}
override fun hashCode(): Int {
var result = key?.hashCode() ?: 0
result = 31 * result + titleKey.hashCode()
result = 31 * result + tag.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as BasePreference
if (key != other.key) return false
if (titleKey != other.titleKey) return false
if (tag != other.tag) return false
return true
}
companion object {
fun Element.addSummary(summaryKey: String, summaryType: SummaryType = SummaryType.DEFAULT) =
setAttribute("android:${summaryType.type}", "@string/$summaryKey")

View File

@@ -2,8 +2,12 @@ package app.revanced.patches.spotify.misc
import app.revanced.patcher.fingerprint
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
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.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
internal val accountAttributeFingerprint = fingerprint {
custom { _, classDef ->
@@ -15,7 +19,7 @@ internal val accountAttributeFingerprint = fingerprint {
}
}
internal val productStateProtoFingerprint = fingerprint {
internal val productStateProtoGetMapFingerprint = fingerprint {
returns("Ljava/util/Map;")
custom { _, classDef ->
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
@@ -56,16 +60,40 @@ internal val readPlayerOptionOverridesFingerprint = fingerprint {
}
}
internal val homeSectionFingerprint = fingerprint {
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
}
internal val protobufListsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
custom { method, _ -> method.name == "emptyProtobufList" }
}
internal val homeStructureFingerprint = fingerprint {
opcodes(Opcode.IGET_OBJECT, Opcode.RETURN_OBJECT)
custom { _, classDef -> classDef.endsWith("homeapi/proto/HomeStructure;") }
internal val protobufListRemoveFingerprint = fingerprint {
custom { method, _ -> method.name == "remove" }
}
internal val homeSectionFingerprint = fingerprint {
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
}
internal val homeStructureGetSectionsFingerprint = fingerprint {
custom { method, classDef ->
classDef.endsWith("homeapi/proto/HomeStructure;") && method.indexOfFirstInstruction {
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.name == "sections_"
} >= 0
}
}
internal fun reactivexFunctionApplyWithClassInitFingerprint(className: String) = fingerprint {
returns("Ljava/lang/Object;")
parameters("Ljava/lang/Object;")
custom { method, _ -> method.name == "apply" && method.indexOfFirstInstruction {
opcode == Opcode.NEW_INSTANCE && getReference<TypeReference>()?.type?.endsWith(className) == true
} >= 0
}
}
internal const val PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME = "FetchMessageRequest;"
internal val pendragonJsonFetchMessageRequestFingerprint =
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME)
internal const val PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME = "FetchMessageListRequest;"
internal val pendragonProtoFetchMessageListRequestFingerprint =
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME)

View File

@@ -60,4 +60,4 @@ val spoofPackageInfoPatch = bytecodePatch(
// endregion
}
}
}
}

View File

@@ -0,0 +1,41 @@
package app.revanced.patches.spotify.misc.privacy
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
internal val shareCopyUrlFingerprint = fingerprint {
returns("Ljava/lang/Object;")
parameters("Ljava/lang/Object;")
strings("clipboard", "Spotify Link")
custom { method, _ ->
method.name == "invokeSuspend"
}
}
internal val shareCopyUrlLegacyFingerprint = fingerprint {
returns("Ljava/lang/Object;")
parameters("Ljava/lang/Object;")
strings("clipboard", "createNewSession failed")
custom { method, _ ->
method.name == "apply"
}
}
internal val formatAndroidShareSheetUrlFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Ljava/lang/String;")
parameters("L", "Ljava/lang/String;")
literal {
'\n'.code.toLong()
}
}
internal val formatAndroidShareSheetUrlLegacyFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
returns("Ljava/lang/String;")
parameters("Lcom/spotify/share/social/sharedata/ShareData;", "Ljava/lang/String;")
literal {
'\n'.code.toLong()
}
}

View File

@@ -0,0 +1,70 @@
package app.revanced.patches.spotify.misc.privacy
import app.revanced.patcher.Fingerprint
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/spotify/misc/privacy/SanitizeSharingLinksPatch;"
@Suppress("unused")
val sanitizeSharingLinksPatch = bytecodePatch(
name = "Sanitize sharing links",
description = "Removes the tracking query parameters from links before they are shared.",
) {
compatibleWith("com.spotify.music")
dependsOn(sharedExtensionPatch)
execute {
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" +
"sanitizeUrl(Ljava/lang/String;)Ljava/lang/String;"
val copyFingerprint = if (IS_SPOTIFY_LEGACY_APP_TARGET) {
shareCopyUrlLegacyFingerprint
} else {
shareCopyUrlFingerprint
}
copyFingerprint.method.apply {
val newPlainTextInvokeIndex = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "newPlainText"
}
val register = getInstruction<FiveRegisterInstruction>(newPlainTextInvokeIndex).registerD
addInstructions(
newPlainTextInvokeIndex,
"""
invoke-static { v$register }, $extensionMethodDescriptor
move-result-object v$register
"""
)
}
// Android native share sheet is used for all other quick share types (X, WhatsApp, etc).
val shareUrlParameter : String
val shareSheetFingerprint : Fingerprint
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
shareSheetFingerprint = formatAndroidShareSheetUrlLegacyFingerprint
shareUrlParameter = "p2"
} else {
shareSheetFingerprint = formatAndroidShareSheetUrlFingerprint
shareUrlParameter = "p1"
}
shareSheetFingerprint.method.addInstructions(
0,
"""
invoke-static { $shareUrlParameter }, $extensionMethodDescriptor
move-result-object $shareUrlParameter
"""
)
}
}

View File

@@ -14,4 +14,4 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
}
}
}

View File

@@ -9,6 +9,8 @@ import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/tiktok/feedfilter/FeedItemsFilter;"
@Suppress("unused")
val feedFilterPatch = bytecodePatch(
name = "Feed filter",
@@ -26,14 +28,15 @@ val feedFilterPatch = bytecodePatch(
)
execute {
feedApiServiceLIZFingerprint.method.apply {
val returnFeedItemInstruction = instructions.first { it.opcode == Opcode.RETURN_OBJECT }
val feedItemsRegister = (returnFeedItemInstruction as OneRegisterInstruction).registerA
addInstruction(
returnFeedItemInstruction.location.index,
"invoke-static { v$feedItemsRegister }, " +
"Lapp/revanced/extension/tiktok/feedfilter/FeedItemsFilter;->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V",
arrayOf(
feedApiServiceLIZFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V",
followFeedFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V"
).forEach { (method, filterSignature) ->
val returnInstruction = method.instructions.first { it.opcode == Opcode.RETURN_OBJECT }
val register = (returnInstruction as OneRegisterInstruction).registerA
method.addInstruction(
returnInstruction.location.index,
"invoke-static { v$register }, $filterSignature"
)
}
@@ -42,4 +45,5 @@ val feedFilterPatch = bytecodePatch(
"invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableFeedFilter()V",
)
}
}

View File

@@ -1,9 +1,22 @@
package app.revanced.patches.tiktok.feedfilter
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val feedApiServiceLIZFingerprint = fingerprint {
custom { method, classDef ->
classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList"
}
}
internal val followFeedFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;")
strings("getFollowFeedList")
opcodes(
Opcode.INVOKE_INTERFACE_RANGE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE
)
}

View File

@@ -6,6 +6,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.ListPreference
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
@@ -42,9 +43,13 @@ private val swipeControlsResourcePatch = resourcePatch {
SwitchPreference("revanced_swipe_haptic_feedback"),
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
SwitchPreference("revanced_swipe_show_circular_overlay"),
SwitchPreference("revanced_swipe_overlay_minimal_style"),
ListPreference(
"revanced_swipe_overlay_style",
summaryKey = null,
),
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_progress_color", inputType = InputType.TEXT_CAP_CHARACTERS),
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_volume_sensitivity", inputType = InputType.NUMBER),

View File

@@ -46,10 +46,11 @@ val hideButtonsPatch = resourcePatch(
SwitchPreference("revanced_hide_remix_button"),
SwitchPreference("revanced_hide_download_button"),
SwitchPreference("revanced_hide_thanks_button"),
SwitchPreference("revanced_hide_ask_button"),
SwitchPreference("revanced_hide_clip_button"),
SwitchPreference("revanced_hide_playlist_button"),
),
),
)
)
)
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;")

View File

@@ -25,11 +25,6 @@ internal val shortsBottomBarContainerFingerprint = fingerprint {
literal { bottomBarContainer }
}
internal val createShortsButtonsFingerprint = fingerprint {
returns("V")
literal { reelPlayerRightCellButtonHeight }
}
internal val renderBottomNavigationBarFingerprint = fingerprint {
returns("V")
parameters("Ljava/lang/String;")

View File

@@ -6,7 +6,6 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
@@ -22,14 +21,14 @@ import app.revanced.patches.youtube.misc.playservice.is_19_41_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.*
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.forEachLiteralValueInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal var reelPlayerRightCellButtonHeight = -1L
private set
internal var bottomBarContainer = -1L
private set
internal var reelPlayerRightPivotV2Size = -1L
@@ -137,11 +136,6 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
}
}
reelPlayerRightCellButtonHeight = resourceMappings[
"dimen",
"reel_player_right_cell_button_height",
]
bottomBarContainer = resourceMappings[
"id",
"bottom_bar_container",
@@ -186,15 +180,6 @@ val hideShortsComponentsPatch = bytecodePatch(
hideShortsWidgetOption()
execute {
// region Hide the Shorts buttons in older versions of YouTube.
// Some Shorts buttons are views, hide them by setting their visibility to GONE.
ShortsButtons.entries.forEach { button -> button.injectHideCall(createShortsButtonsFingerprint.method) }
// endregion
// region Hide the Shorts buttons in newer versions of YouTube.
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
forEachLiteralValueInstruction(
@@ -211,7 +196,7 @@ val hideShortsComponentsPatch = bytecodePatch(
"""
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
move-result v$sizeRegister
""",
"""
)
}
@@ -261,31 +246,10 @@ val hideShortsComponentsPatch = bytecodePatch(
"""
invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I
move-result v$heightRegister
""",
"""
)
}
// endregion
}
}
private enum class ShortsButtons(private val resourceName: String, private val methodName: String) {
LIKE("reel_dyn_like", "hideLikeButton"),
DISLIKE("reel_dyn_dislike", "hideDislikeButton"),
COMMENTS("reel_dyn_comment", "hideShortsCommentsButton"),
REMIX("reel_dyn_remix", "hideShortsRemixButton"),
SHARE("reel_dyn_share", "hideShortsShareButton"),
;
fun injectHideCall(method: MutableMethod) {
val referencedIndex = method.indexOfFirstResourceIdOrThrow(resourceName)
val setIdIndex = method.indexOfFirstInstructionOrThrow(referencedIndex) {
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.name == "setId"
}
val viewRegister = method.getInstruction<FiveRegisterInstruction>(setIdIndex).registerC
method.injectHideViewCall(setIdIndex + 1, viewRegister, FILTER_CLASS_DESCRIPTOR, methodName)
}
}

View File

@@ -5,18 +5,6 @@ import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val conversionContextFingerprint = fingerprint {
returns("Ljava/lang/String;")
parameters()
strings(
", widthConstraint=",
", heightConstraint=",
", templateLoggerFactory=",
", rootDisposableContainer=",
"ConversionContext{containerInternal=",
)
}
internal val dislikeFingerprint = fingerprint {
returns("V")
strings("like/dislike")

View File

@@ -18,6 +18,7 @@ import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.addSettingPreference
import app.revanced.patches.youtube.misc.settings.newIntent
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
import app.revanced.patches.youtube.shared.rollingNumberTextViewAnimationUpdateFingerprint
import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
import app.revanced.patches.youtube.video.videoid.hookVideoId
@@ -113,11 +114,11 @@ val returnYouTubeDislikePatch = bytecodePatch(
// This hook handles all situations, as it's where the created Spans are stored and later reused.
// Find the field name of the conversion context.
val conversionContextField = textComponentConstructorFingerprint.originalClassDef.fields.find {
it.type == conversionContextFingerprint.originalClassDef.type
it.type == conversionContextFingerprintToString.originalClassDef.type
} ?: throw PatchException("Could not find conversion context field")
textComponentLookupFingerprint.match(textComponentConstructorFingerprint.originalClassDef)
textComponentLookupFingerprint.method.apply {
.method.apply {
// Find the instruction for creating the text data object.
val textDataClassType = textComponentDataFingerprint.originalClassDef.type
@@ -160,12 +161,12 @@ val returnYouTubeDislikePatch = bytecodePatch(
addInstructionsAtControlFlowLabel(
insertIndex,
"""
# Copy conversion context
move-object/from16 v$tempRegister, p0
iget-object v$tempRegister, v$tempRegister, $conversionContextField
invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister
""",
# Copy conversion context
move-object/from16 v$tempRegister, p0
iget-object v$tempRegister, v$tempRegister, $conversionContextField
invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister
"""
)
}
@@ -201,11 +202,9 @@ val returnYouTubeDislikePatch = bytecodePatch(
val charSequenceFieldReference =
getInstruction<ReferenceInstruction>(dislikesIndex).reference
val registerCount = implementation!!.registerCount
val conversionContextRegister = implementation!!.registerCount - parameters.size + 1
// This register is being overwritten, so it is free to use.
val freeRegister = registerCount - 1
val conversionContextRegister = registerCount - parameters.size + 1
val freeRegister = findFreeRegister(insertIndex, charSequenceInstanceRegister, conversionContextRegister)
addInstructions(
insertIndex,

View File

@@ -11,8 +11,8 @@ internal val setWordmarkHeaderFingerprint = fingerprint {
returns("V")
parameters("Landroid/widget/ImageView;")
custom { methodDef, _ ->
methodDef.containsLiteralInstruction(ytWordmarkHeaderId) &&
methodDef.containsLiteralInstruction(ytPremiumWordmarkHeaderId)
methodDef.containsLiteralInstruction(ytPremiumWordmarkHeaderId) &&
methodDef.containsLiteralInstruction(ytWordmarkHeaderId)
}
}

View File

@@ -17,7 +17,6 @@ import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
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
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@@ -48,7 +47,7 @@ private val wideSearchbarResourcePatch = resourcePatch {
actionBarRingoId = resourceMappings[
"layout",
"action_bar_ringo_background",
"action_bar_ringo",
]
}
}
@@ -111,18 +110,18 @@ val wideSearchbarPatch = bytecodePatch(
// Fix missing left padding when using wide searchbar.
wideSearchbarLayoutFingerprint.method.apply {
val layoutIndex = indexOfFirstLiteralInstructionOrThrow(actionBarRingoId)
val inflateIndex = indexOfFirstInstructionOrThrow(layoutIndex) {
findInstructionIndicesReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Landroid/view/LayoutInflater;"
&& reference.name == "inflate"
}
val register = getInstruction<OneRegisterInstruction>(inflateIndex + 1).registerA
}.forEach { inflateIndex ->
val register = getInstruction<OneRegisterInstruction>(inflateIndex + 1).registerA
addInstruction(
inflateIndex + 2,
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setActionBar(Landroid/view/View;)V"
)
addInstruction(
inflateIndex + 2,
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setActionBar(Landroid/view/View;)V"
)
}
}
}
}

View File

@@ -1,8 +1,13 @@
package app.revanced.patches.youtube.layout.shortsautoplay
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
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val reelEnumConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
@@ -20,3 +25,27 @@ internal val reelPlaybackRepeatFingerprint = fingerprint {
parameters("L")
strings("YoutubePlayerState is in throwing an Error.")
}
internal val reelPlaybackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("J")
custom { method, _ ->
indexOfMilliSecondsInstruction(method) >= 0 &&
indexOfInitializationInstruction(method) >= 0
}
}
private fun indexOfMilliSecondsInstruction(method: Method) =
method.indexOfFirstInstruction {
getReference<FieldReference>()?.name == "MILLISECONDS"
}
internal fun indexOfInitializationInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_DIRECT &&
reference?.name == "<init>" &&
reference.parameterTypes.size == 3 &&
reference.parameterTypes.firstOrNull() == "I"
}

View File

@@ -2,21 +2,32 @@ package app.revanced.patches.youtube.layout.shortsautoplay
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
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.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ShortsAutoplayPatch;"
@@ -98,5 +109,84 @@ val shortsAutoplayPatch = bytecodePatch(
)
}
}
// As of YouTube 20.09, Google has removed the code for 'Autoplay' and 'Pause' from this method.
// Manually restore the removed 'Autoplay' code.
if (is_20_09_or_greater) {
// Variable names are only a rough guess of what these methods do.
val userActionMethodIndex = indexOfInitializationInstruction(reelPlaybackFingerprint.method)
val userActionMethodReference = reelPlaybackFingerprint.method
.getInstruction<ReferenceInstruction>(userActionMethodIndex).reference as MethodReference
val reelSequenceControllerMethodIndex = reelPlaybackFingerprint.method
.indexOfFirstInstructionOrThrow(userActionMethodIndex, Opcode.INVOKE_VIRTUAL)
val reelSequenceControllerMethodReference = reelPlaybackFingerprint.method
.getInstruction<ReferenceInstruction>(reelSequenceControllerMethodIndex).reference as MethodReference
reelPlaybackRepeatFingerprint.method.apply {
// Find the first call modified by extension code above.
val extensionReturnResultIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_STATIC &&
getReference<MethodReference>()?.definingClass == EXTENSION_CLASS_DESCRIPTOR
} + 1
val enumRegister = getInstruction<OneRegisterInstruction>(extensionReturnResultIndex).registerA
val getReelSequenceControllerIndex = indexOfFirstInstructionOrThrow(extensionReturnResultIndex) {
val reference = getReference<FieldReference>()
opcode == Opcode.IGET_OBJECT &&
reference?.definingClass == definingClass &&
reference.type == reelSequenceControllerMethodReference.definingClass
}
val getReelSequenceControllerReference =
getInstruction<ReferenceInstruction>(getReelSequenceControllerIndex).reference
// Add a helper method to avoid finding multiple free registers.
// If enum is autoplay then method performs autoplay and returns null,
// otherwise returns the same enum.
val helperClass = definingClass
val helperName = "patch_handleAutoPlay"
val helperReturnType = "Ljava/lang/Enum;"
val helperMethod = ImmutableMethod(
helperClass,
helperName,
listOf(ImmutableMethodParameter("Ljava/lang/Enum;", null, null)),
helperReturnType,
AccessFlags.PRIVATE.value,
null,
null,
MutableMethodImplementation(7),
).toMutable().apply {
addInstructionsWithLabels(
0,
"""
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->isAutoPlay(Ljava/lang/Enum;)Z
move-result v0
if-eqz v0, :ignore
new-instance v0, ${userActionMethodReference.definingClass}
const/4 v1, 0x3
const/4 v2, 0x0
invoke-direct { v0, v1, v2, v2 }, $userActionMethodReference
iget-object v3, p0, $getReelSequenceControllerReference
invoke-virtual { v3, v0 }, $reelSequenceControllerMethodReference
const/4 v4, 0x0
return-object v4
:ignore
return-object p1
"""
)
}
reelPlaybackRepeatFingerprint.classDef.methods.add(helperMethod)
addInstructionsWithLabels(
extensionReturnResultIndex + 1,
"""
invoke-direct { p0, v$enumRegister }, $helperClass->$helperName(Ljava/lang/Enum;)$helperReturnType
move-result-object v$enumRegister
if-nez v$enumRegister, :ignore
return-void # Autoplay was performed.
:ignore
nop
"""
)
}
}
}
}

View File

@@ -73,7 +73,7 @@ val spoofAppVersionPatch = bytecodePatch(
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
// Group the switch and list preference together, since General menu is sorted by name
// and the preferences can be scattered apart with non English langauges.
// and the preferences can be scattered apart with non English languages.
PreferenceCategory(
titleKey = null,
sorting = Sorting.UNSORTED,
@@ -122,16 +122,17 @@ val spoofAppVersionPatch = bytecodePatch(
)
}
val insertIndex = spoofAppVersionFingerprint.patternMatch!!.startIndex + 1
val buildOverrideNameRegister =
spoofAppVersionFingerprint.method.getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
spoofAppVersionFingerprint.apply {
val startIndex = patternMatch!!.startIndex
val buildOverrideNameRegister = method.getInstruction<OneRegisterInstruction>(startIndex).registerA
spoofAppVersionFingerprint.method.addInstructions(
insertIndex,
"""
invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$buildOverrideNameRegister
"""
)
method.addInstructions(
startIndex + 1,
"""
invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$buildOverrideNameRegister
"""
)
}
}
}

View File

@@ -7,6 +7,9 @@ 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.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
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
@@ -43,10 +46,18 @@ val changeStartPagePatch = bytecodePatch(
addResources("youtube", "layout.startpage.changeStartPagePatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
ListPreference(
key = "revanced_change_start_page",
summaryKey = null,
),
PreferenceCategory(
titleKey = null,
sorting = Sorting.UNSORTED,
tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory",
preferences = setOf(
ListPreference(
key = "revanced_change_start_page",
summaryKey = null,
),
SwitchPreference("revanced_change_start_page_always")
)
)
)
// Hook browseId.

View File

@@ -0,0 +1,83 @@
package app.revanced.patches.youtube.misc.gms
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/AccountCredentialsInvalidTextPatch;"
internal var ic_offline_no_content_upside_down = -1L
private set
internal var offline_no_content_body_text_not_offline_eligible = -1L
private set
private val accountCredentialsInvalidTextResourcePatch = resourcePatch {
execute {
ic_offline_no_content_upside_down = resourceMappings[
"drawable",
"ic_offline_no_content_upside_down"
]
offline_no_content_body_text_not_offline_eligible = resourceMappings[
"string",
"offline_no_content_body_text_not_offline_eligible"
]
}
}
internal val accountCredentialsInvalidTextPatch = bytecodePatch {
dependsOn(
sharedExtensionPatch,
accountCredentialsInvalidTextResourcePatch,
addResourcesPatch
)
execute {
addResources("youtube", "misc.gms.accountCredentialsInvalidTextPatch")
// If the user recently changed their account password,
// the app can show "You're offline. Check your internet connection."
// even when the internet is available. For this situation
// YouTube + MicroG shows an offline error message.
//
// Change the error text to inform the user to uninstall and reinstall MicroG.
// The user can also fix this by deleting the MicroG account but
// MicroG accounts look almost identical to Google device accounts
// and it's more foolproof to instead uninstall/reinstall.
arrayOf(
specificNetworkErrorViewControllerFingerprint,
loadingFrameLayoutControllerFingerprint
).forEach { fingerprint ->
fingerprint.method.apply {
val resourceIndex = indexOfFirstLiteralInstructionOrThrow(
offline_no_content_body_text_not_offline_eligible
)
val getStringIndex = indexOfFirstInstructionOrThrow(resourceIndex) {
val reference = getReference<MethodReference>()
reference?.name == "getString"
}
val register = getInstruction<OneRegisterInstruction>(getStringIndex + 1).registerA
addInstructions(
getStringIndex + 2,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getOfflineNetworkErrorString(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$register
"""
)
}
}
}
}

View File

@@ -0,0 +1,27 @@
package app.revanced.patches.youtube.misc.gms
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import com.android.tools.smali.dexlib2.AccessFlags
internal val specificNetworkErrorViewControllerFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters()
custom { method, _ ->
method.containsLiteralInstruction(ic_offline_no_content_upside_down)
&& method.containsLiteralInstruction(offline_no_content_body_text_not_offline_eligible)
}
}
// It's not clear if this second class is ever used and it may be dead code,
// but it the layout image/text is identical to the network error fingerprint above.
internal val loadingFrameLayoutControllerFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("L")
custom { method, _ ->
method.containsLiteralInstruction(ic_offline_no_content_upside_down)
&& method.containsLiteralInstruction(offline_no_content_body_text_not_offline_eligible)
}
}

View File

@@ -68,5 +68,5 @@ private fun gmsCoreSupportResourcePatch(
)
},
) {
dependsOn(settingsPatch, addResourcesPatch)
dependsOn(settingsPatch, addResourcesPatch, accountCredentialsInvalidTextPatch)
}

View File

@@ -5,10 +5,6 @@ import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* In 19.17 and earlier, this resolves to the same method as [readComponentIdentifierFingerprint].
* In 19.18+ this resolves to a different method.
*/
internal val componentContextParserFingerprint = fingerprint {
strings(
"TreeNode result must be set.",
@@ -17,11 +13,21 @@ internal val componentContextParserFingerprint = fingerprint {
)
}
/**
* Resolves to the class found in [componentContextParserFingerprint].
* When patching 19.16 this fingerprint matches the same method as [componentContextParserFingerprint].
*/
internal val componentContextSubParserFingerprint = fingerprint {
strings(
"Number of bits must be positive"
)
}
internal val lithoFilterFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
returns("V")
custom { _, classDef ->
classDef.endsWith("LithoFilterPatch;")
classDef.endsWith("/LithoFilterPatch;")
}
}
@@ -37,14 +43,6 @@ internal val protobufBufferReferenceFingerprint = fingerprint {
)
}
/**
* In 19.17 and earlier, this resolves to the same method as [componentContextParserFingerprint].
* In 19.18+ this resolves to a different method.
*/
internal val readComponentIdentifierFingerprint = fingerprint {
strings("Number of bits must be positive")
}
internal val emptyComponentFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
parameters()

View File

@@ -4,25 +4,25 @@ package app.revanced.patches.youtube.misc.litho.filter
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_18_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_05_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findFreeRegister
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@@ -53,42 +53,33 @@ val lithoFilterPatch = bytecodePatch(
* The buffer is a large byte array that represents the component tree.
* This byte array is searched for strings that indicate the current component.
*
* The following pseudocode shows how the patch works:
* All modifications done here must allow all the original code to still execute
* even when filtering, otherwise memory leaks or poor app performance may occur.
*
* The following pseudocode shows how this patch works:
*
* class SomeOtherClass {
* // Called before ComponentContextParser.parseBytesToComponentContext method.
* // Called before ComponentContextParser.parseComponent() method.
* public void someOtherMethod(ByteBuffer byteBuffer) {
* ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch.
* ...
* }
* }
*
* When patching 19.17 and earlier:
*
* class ComponentContextParser {
* public ComponentContext ReadComponentIdentifierFingerprint(...) {
* public Component parseComponent() {
* ...
* if (extensionClass.filter(identifier, pathBuilder)); // Inserted by this patch.
*
* // Checks if the component should be filtered.
* // Sets a thread local with the filtering result.
* extensionClass.filter(identifier, pathBuilder); // Inserted by this patch.
*
* ...
*
* if (extensionClass.shouldFilter()) { // Inserted by this patch.
* return emptyComponent;
* ...
* }
* }
*
* When patching 19.18 and later:
*
* class ComponentContextParser {
* public ComponentContext parseBytesToComponentContext(...) {
* ...
* if (ReadComponentIdentifierFingerprint() == null); // Inserted by this patch.
* return emptyComponent;
* ...
* }
*
* public ComponentIdentifierObj readComponentIdentifier(...) {
* ...
* if (extensionClass.filter(identifier, pathBuilder)); // Inserted by this patch.
* return null;
* ...
* }
* return originalUnpatchedComponent; // Original code.
* }
* }
*/
@@ -103,7 +94,7 @@ val lithoFilterPatch = bytecodePatch(
2,
"""
new-instance v1, $classDescriptor
invoke-direct {v1}, $classDescriptor-><init>()V
invoke-direct { v1 }, $classDescriptor-><init>()V
const/16 v2, ${filterCount++}
aput-object v1, v0, v2
""",
@@ -115,110 +106,105 @@ val lithoFilterPatch = bytecodePatch(
protobufBufferReferenceFingerprint.method.addInstruction(
0,
" invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
"invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
)
// endregion
// region Hook the method that parses bytes into a ComponentContext.
val readComponentMethod = readComponentIdentifierFingerprint.originalMethod
// Get the only static method in the class.
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.first { method ->
AccessFlags.STATIC.isSet(method.accessFlags)
}
// Only one field.
val emptyComponentField = classBy { classDef ->
builderMethodDescriptor.returnType == classDef.type
}!!.immutableClass.fields.single()
// Returns an empty component instead of the original component.
fun createReturnEmptyComponentInstructions(register: Int): String =
"""
move-object/from16 v$register, p1
invoke-static { v$register }, $builderMethodDescriptor
move-result-object v$register
iget-object v$register, v$register, $emptyComponentField
return-object v$register
"""
// Allow the method to run to completion, and override the
// return value with an empty component if it should be filtered.
// It is important to allow the original code to always run to completion,
// otherwise memory leaks and poor app performance can occur.
//
// The extension filtering result needs to be saved off somewhere, but cannot
// save to a class field since the target class is called by multiple threads.
// It would be great if there was a way to change the register count of the
// method implementation and save the result to a high register to later use
// in the method, but there is no simple way to do that.
// Instead save the extension filter result to a thread local and check the
// filtering result at each method return index.
// String field for the litho identifier.
componentContextParserFingerprint.method.apply {
// 19.18 and later require patching 2 methods instead of one.
// Otherwise the modifications done here are the same for all targets.
if (is_19_18_or_greater) {
// Get the method name of the ReadComponentIdentifierFingerprint call.
val readComponentMethodCallIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == readComponentMethod.definingClass &&
reference.name == readComponentMethod.name
val conversionContextClass = conversionContextFingerprintToString.originalClassDef
val conversionContextIdentifierField = componentContextSubParserFingerprint.match(
componentContextParserFingerprint.originalClassDef
).let {
// Identifier field is loaded just before the string declaration.
val index = it.method.indexOfFirstInstructionReversedOrThrow(
it.stringMatches!!.first().index
) {
val reference = getReference<FieldReference>()
reference?.definingClass == conversionContextClass.type
&& reference.type == "Ljava/lang/String;"
}
// Result of read component, and also a free register.
val register = getInstruction<OneRegisterInstruction>(readComponentMethodCallIndex + 1).registerA
// Insert after 'move-result-object'
val insertHookIndex = readComponentMethodCallIndex + 2
// Return an EmptyComponent instead of the original component if the filterState method returns true.
addInstructionsWithLabels(
insertHookIndex,
"""
if-nez v$register, :unfiltered
# Component was filtered in ReadComponentIdentifierFingerprint hook
${createReturnEmptyComponentInstructions(register)}
""",
ExternalLabel("unfiltered", getInstruction(insertHookIndex)),
)
it.method.getInstruction<ReferenceInstruction>(index).getReference<FieldReference>()
}
}
// endregion
// StringBuilder field for the litho path.
val conversionContextPathBuilderField = conversionContextClass.fields
.single { field -> field.type == "Ljava/lang/StringBuilder;" }
// region Read component then store the result.
val conversionContextResultIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.returnType == conversionContextClass.type
} + 1
readComponentIdentifierFingerprint.method.apply {
val insertHookIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/StringBuilder;"
}
val stringBuilderRegister = getInstruction<TwoRegisterInstruction>(insertHookIndex).registerA
// Identifier is saved to a field just before the string builder.
val identifierRegister = getInstruction<TwoRegisterInstruction>(
indexOfFirstInstructionReversedOrThrow(insertHookIndex) {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Ljava/lang/String;"
},
val conversionContextResultRegister = getInstruction<OneRegisterInstruction>(
conversionContextResultIndex
).registerA
val freeRegister = findFreeRegister(insertHookIndex, identifierRegister, stringBuilderRegister)
val invokeFilterInstructions = """
invoke-static { v$identifierRegister, v$stringBuilderRegister }, $EXTENSION_CLASS_DESCRIPTOR->filter(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
move-result v$freeRegister
if-eqz v$freeRegister, :unfiltered
"""
addInstructionsWithLabels(
insertHookIndex,
if (is_19_18_or_greater) {
"""
$invokeFilterInstructions
# Return null, and the ComponentContextParserFingerprint hook
# handles returning an empty component.
const/4 v$freeRegister, 0x0
return-object v$freeRegister
"""
} else {
"""
$invokeFilterInstructions
${createReturnEmptyComponentInstructions(freeRegister)}
"""
},
ExternalLabel("unfiltered", getInstruction(insertHookIndex)),
val identifierRegister = findFreeRegister(
conversionContextResultIndex, conversionContextResultRegister
)
val stringBuilderRegister = findFreeRegister(
conversionContextResultIndex, conversionContextResultRegister, identifierRegister
)
// Check if the component should be filtered, and save the result to a thread local.
addInstructionsAtControlFlowLabel(
conversionContextResultIndex + 1,
"""
iget-object v$identifierRegister, v$conversionContextResultRegister, $conversionContextIdentifierField
iget-object v$stringBuilderRegister, v$conversionContextResultRegister, $conversionContextPathBuilderField
invoke-static { v$identifierRegister, v$stringBuilderRegister }, $EXTENSION_CLASS_DESCRIPTOR->filter(Ljava/lang/String;Ljava/lang/StringBuilder;)V
"""
)
// Get the only static method in the class.
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.single {
method -> AccessFlags.STATIC.isSet(method.accessFlags)
}
// Only one field.
val emptyComponentField = classBy { classDef ->
classDef.type == builderMethodDescriptor.returnType
}!!.immutableClass.fields.single()
// Check at each return value if the component is filtered,
// and return an empty component if filtering is needed.
findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { returnIndex ->
val freeRegister = findFreeRegister(returnIndex)
addInstructionsAtControlFlowLabel(
returnIndex,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->shouldFilter()Z
move-result v$freeRegister
if-eqz v$freeRegister, :unfiltered
move-object/from16 v$freeRegister, p1
invoke-static { v$freeRegister }, $builderMethodDescriptor
move-result-object v$freeRegister
iget-object v$freeRegister, v$freeRegister, $emptyComponentField
return-object v$freeRegister
:unfiltered
nop
"""
)
}
}
// endregion

View File

@@ -4,6 +4,21 @@ import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val conversionContextFingerprintToString = fingerprint {
parameters()
strings(
"ConversionContext{containerInternal=",
", widthConstraint=",
", heightConstraint=",
", templateLoggerFactory=",
", rootDisposableContainer=",
", identifierProperty="
)
custom { method, _ ->
method.name == "toString"
}
}
internal val autoRepeatFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")

View File

@@ -11,12 +11,14 @@ import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.util.InstructionUtils.Companion.branchOpcodes
import app.revanced.util.InstructionUtils.Companion.returnOpcodes
import app.revanced.util.InstructionUtils.Companion.writeOpcodes
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.Opcode.*
import com.android.tools.smali.dexlib2.iface.Method
@@ -168,6 +170,15 @@ internal val Instruction.isBranchInstruction: Boolean
internal val Instruction.isReturnInstruction: Boolean
get() = this.opcode in returnOpcodes
/**
* Adds public [AccessFlags] and removes private and protected flags (if present).
*/
internal fun Int.toPublicAccessFlags() : Int {
return this.or(AccessFlags.PUBLIC.value)
.and(AccessFlags.PROTECTED.value.inv())
.and(AccessFlags.PRIVATE.value.inv())
}
/**
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
*
@@ -207,6 +218,26 @@ fun MutableMethod.injectHideViewCall(
"invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V",
)
/**
* Inserts instructions at a given index, using the existing control flow label at that index.
* Inserted instructions can have it's own control flow labels as well.
*
* Effectively this changes the code from:
* :label
* (original code)
*
* Into:
* :label
* (patch code)
* (original code)
*/
// TODO: delete this on next major version bump.
fun MutableMethod.addInstructionsAtControlFlowLabel(
insertIndex: Int,
instructions: String
) = addInstructionsAtControlFlowLabel(insertIndex, instructions, *arrayOf<ExternalLabel>())
/**
* Inserts instructions at a given index, using the existing control flow label at that index.
* Inserted instructions can have it's own control flow labels as well.
@@ -223,13 +254,14 @@ fun MutableMethod.injectHideViewCall(
fun MutableMethod.addInstructionsAtControlFlowLabel(
insertIndex: Int,
instructions: String,
vararg externalLabels: ExternalLabel
) {
// Duplicate original instruction and add to +1 index.
addInstruction(insertIndex + 1, getInstruction(insertIndex))
// Add patch code at same index as duplicated instruction,
// so it uses the original instruction control flow label.
addInstructionsWithLabels(insertIndex + 1, instructions)
addInstructionsWithLabels(insertIndex + 1, instructions, *externalLabels)
// Remove original non duplicated instruction.
removeInstruction(insertIndex)

View File

@@ -84,6 +84,8 @@ Second \"item\" text"</string>
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
@@ -195,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -84,6 +84,8 @@ Second \"item\" text"</string>
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
@@ -195,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -459,29 +459,38 @@ Second \"item\" text"</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">تمكين إيماءة السطوع التلقائي</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">التمرير لأسفل إلى أدنى قيمة للسطوع يمكّن السطوع التلقائي</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">لا يؤدي التمرير لأسفل إلى أدنى قيمة إلى تمكين السطوع التلقائي</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">تلقائي</string>
<string name="revanced_swipe_overlay_timeout_title">مهلة واجهة التمرير</string>
<string name="revanced_swipe_overlay_timeout_summary">مقدار الوقت الذي تظهر فيه واجهة التمرير بعد التغيير بجزء الثانية</string>
<string name="revanced_swipe_overlay_background_opacity_title">تعتيم خلفية واجهة التمرير السريع</string>
<string name="revanced_swipe_overlay_background_opacity_summary">قيمة التعتيم بين 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">يجب أن يكون تعتيم التمرير السريع بين 0-100</string>
<string name="revanced_swipe_overlay_progress_color_title">لون شريط تقدم واجهة التمرير</string>
<string name="revanced_swipe_overlay_progress_color_summary">لون شريط التقدم لعناصر التحكم في مستوى الصوت والسطوع</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">لون شريط التقدم غير صالح</string>
<string name="revanced_swipe_text_overlay_size_title">حجم نص واجهة التمرير</string>
<string name="revanced_swipe_text_overlay_size_summary">حجم النص لواجهة التمرير بين 1-30</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">يجب أن يكون حجم النص بين 1-30</string>
<string name="revanced_swipe_threshold_title">مقدار حد التمرير</string>
<string name="revanced_swipe_threshold_summary">الحد الأدنى من التمرير قبل اكتشاف الإيماءة</string>
<string name="revanced_swipe_volume_sensitivity_title">حساسية إيماءة تمرير مستوى الصوت</string>
<string name="revanced_swipe_volume_sensitivity_summary">مقدار تغير مستوى الصوت لكل تمريرة</string>
<string name="revanced_swipe_show_circular_overlay_title">عرض الواجهة الدائرية</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">يتم عرض التراكب الدائري</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">يتم عرض التراكب الأفقي</string>
<string name="revanced_swipe_overlay_minimal_style_title">تمكين النمط الأدنى</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">تم تمكين النمط الواجهة الأدنى</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">تم تعطيل نمط الواجهة الأدنى</string>
<string name="revanced_swipe_overlay_style_title">نمط واجهة التمرير</string>
<string name="revanced_swipe_overlay_style_entry_1">واجهة أفقية</string>
<string name="revanced_swipe_overlay_style_entry_2">واجهة أفقية (الأدنى - الأعلى)</string>
<string name="revanced_swipe_overlay_style_entry_3">واجهة أفقية (الأدنى - المنتصف)</string>
<string name="revanced_swipe_overlay_style_entry_4">واجهة دائرية</string>
<string name="revanced_swipe_overlay_style_entry_5">واجهة دائرية (الأدنى)</string>
<string name="revanced_swipe_overlay_style_entry_6">واجهة عمودية</string>
<string name="revanced_swipe_overlay_style_entry_7">واجهة عمودية (الأدنى)</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_disable_auto_captions_title">تعطيل التَّرْجَمَة التلقائية</string>
<string name="revanced_disable_auto_captions_summary_on">تم تعطيل التَّرْجَمَة التلقائية</string>
<string name="revanced_disable_auto_captions_summary_off">تم تمكين التَّرْجَمَة التلقائية</string>
<string name="revanced_disable_auto_captions_title">تعطيل الترجمة التلقائية</string>
<string name="revanced_disable_auto_captions_summary_on">تم تعطيل الترجمة التلقائية</string>
<string name="revanced_disable_auto_captions_summary_off">تم تمكين الترجمة التلقائية</string>
</patch>
<patch id="layout.buttons.action.hideButtonsPatch">
<string name="revanced_hide_buttons_screen_title">أزرار الإجراء</string>
@@ -513,6 +522,11 @@ Second \"item\" text"</string>
<string name="revanced_hide_thanks_button_title">إخفاء شكرًا</string>
<string name="revanced_hide_thanks_button_summary_on">تم إخفاء زر شكرًا</string>
<string name="revanced_hide_thanks_button_summary_off">يتم عرض زر شكرًا</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">إخفاء \"Ask\"</string>
<string name="revanced_hide_ask_button_summary_on">تم إخفاء زر \"Ask\"</string>
<string name="revanced_hide_ask_button_summary_off">يتم عرض زر \"Ask\"</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">إخفاء المقطع</string>
<string name="revanced_hide_clip_button_summary_on">تم إخفاء زر إنشاء مقطع</string>
@@ -1107,7 +1121,7 @@ Second \"item\" text"</string>
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - استعادة أيقونات التنقل القديمة</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">تعيين صفحة البداية</string>
<string name="revanced_change_start_page_title">تغيير صفحة البداية</string>
<string name="revanced_change_start_page_entry_default">الافتراضي</string>
<string name="revanced_change_start_page_entry_all_subscriptions">كلّ الاشتراكات</string>
<string name="revanced_change_start_page_entry_browse">تصفح القنوات</string>
@@ -1132,6 +1146,11 @@ Second \"item\" text"</string>
<string name="revanced_change_start_page_entry_virtual_reality">الواقع الافتراضي</string>
<string name="revanced_change_start_page_entry_watch_later">شاهد لاحقًا</string>
<string name="revanced_change_start_page_entry_your_clips">فيديوهاتك</string>
<string name="revanced_change_start_page_always_title">تغيير صفحة البداية دائمًا</string>
<string name="revanced_change_start_page_always_summary_on">"يتم تغيير صفحة البداية دائمًا
القيد: قد لا يعمل استخدام زر الرجوع على شريط الأدوات"</string>
<string name="revanced_change_start_page_always_summary_off">يتم تغيير صفحة البداية فقط عند بدء تشغيل التطبيق</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">تعطيل استئناف مشغل Shorts</string>
@@ -1292,6 +1311,9 @@ Second \"item\" text"</string>
<string name="microg_settings_title">إعدادات GmsCore</string>
<string name="microg_settings_summary">إعدادات لـ GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">إذا قمت مؤخرًا بتغيير تفاصيل تسجيل الدخول إلى حسابك، فأزل تثبيت MicroG ثم أعد تثبيته.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">تجاوز إعادة توجيه URL</string>
<string name="revanced_bypass_url_redirects_summary_on">تم تجاوز إعادة توجيه عنوان URL</string>

View File

@@ -84,6 +84,8 @@ Second \"item\" text"</string>
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
@@ -197,6 +199,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -136,7 +136,7 @@ Gözlənilməz hallardan xəbərdar olmayacaqsınız."</string>
• Son xəbərlər
• İzləməyə davam et
• Daha çox kanallar kəşf et
Alış-veriş
Mağaza
• Təkrar izlə"</string>
<string name="revanced_hide_horizontal_shelves_summary_off">Hissələr göstərilir</string>
<!-- 'Join' should be translated using the same localized wording YouTube displays.
@@ -355,7 +355,7 @@ Bu xüsusiyyət yalnız köhnə cihazlar üçün mövcuddur"</string>
<string name="revanced_hide_buttoned_ads_title">Düyməli reklamları gizlət</string>
<string name="revanced_hide_buttoned_ads_summary_on">Düyməli reklamlar gizlədilir</string>
<string name="revanced_hide_buttoned_ads_summary_off">Düyməli reklamlar göstərilir</string>
<string name="revanced_hide_paid_promotion_label_title">Ödənişli reklam etiketini gizlət</string>
<string name="revanced_hide_paid_promotion_label_title">Ödənişli tanıtım etiketini gizlət</string>
<string name="revanced_hide_paid_promotion_label_summary_on">Ödənişli reklam etiketi gizlədilib</string>
<string name="revanced_hide_paid_promotion_label_summary_off">Ödənişli reklam etiketi göstərilir</string>
<string name="revanced_hide_self_sponsor_ads_title">Öz-sponsorlu kartları gizlət</string>
@@ -367,14 +367,14 @@ Bu xüsusiyyət yalnız köhnə cihazlar üçün mövcuddur"</string>
<string name="revanced_hide_end_screen_store_banner_title">Son ekran mağaza etiketini gizlət</string>
<string name="revanced_hide_end_screen_store_banner_summary_on">Mağaza etiketi gizlidir</string>
<string name="revanced_hide_end_screen_store_banner_summary_off">Mağaza etiketi görünür</string>
<string name="revanced_hide_player_store_shelf_title">Oynadıcı alış-veriş bölməsin gizlət</string>
<string name="revanced_hide_player_store_shelf_title">Oynadıcı mağaza bölməsin gizlət</string>
<string name="revanced_hide_player_store_shelf_summary_on">Alış-veriş rəfi gizlidir</string>
<string name="revanced_hide_player_store_shelf_summary_off">Alış-veriş rəfi göstərilir</string>
<string name="revanced_hide_shopping_links_title">Video ıqlamada alış-veriş linklərin gizlə</string>
<string name="revanced_hide_shopping_links_title">Video təsvirdə mağaza linklərin gizlə</string>
<string name="revanced_hide_shopping_links_summary_on">Video təsvirində alış-veriş linkləri gizlədilib</string>
<string name="revanced_hide_shopping_links_summary_off">Video təsvirində alış-veriş linkləri görünür</string>
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_visit_store_button_title">Kanalda \"Mağazaya ziyarət\" düyməsin gizlə</string>
<string name="revanced_hide_visit_store_button_title">Kanalda \"Mağazaya baxın\" düyməsin gizlə</string>
<string name="revanced_hide_visit_store_button_summary_on">Kanal səhifəsindəki düymə gizlidir</string>
<string name="revanced_hide_visit_store_button_summary_off">Kanal səhifəsindəki düymə görünür</string>
<string name="revanced_hide_web_search_results_title">Veb axtarış nəticələrini gizlət</string>
@@ -459,27 +459,38 @@ Ekranın sağ tərəfində düzünə sürüşdürərək səs səviyyəsini tənz
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Avto-parlaqlıq jestini aktivləşdir</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Parlaqlıq ən aşağı dəyərinə sürüşdürüləndə avto-parlaqlıq aktivləşir</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Parlaqlığı ən aşağı dəyərə sürüşdürəndə avto-parlaqlıq aktivləşmir</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Avtomatik</string>
<string name="revanced_swipe_overlay_timeout_title">Sürüşdürmə örtüyü müddəti</string>
<string name="revanced_swipe_overlay_timeout_summary">Örtüyün göründüyü millisaniyələrin sayı</string>
<string name="revanced_swipe_overlay_background_opacity_title">Sürüşdürmə cildi arxa plan qeyri-şəffaflığı</string>
<string name="revanced_swipe_overlay_background_opacity_summary">0-100 arası qeyri-şəffaflıq dəyəri</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Sürüşmə qeyri-şəffaflığı 0-100 arası olmalıdır</string>
<string name="revanced_swipe_overlay_progress_color_title">Sürüşdürmə örtüyü irəliləyiş cizgisi rəngi</string>
<string name="revanced_swipe_overlay_progress_color_summary">Səs səviyyəsinə və parlaqlığa nəzarət üçün irəliləyiş cizgisi rəngi</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Yanlış irəliləyiş cizgisi rəngi</string>
<string name="revanced_swipe_text_overlay_size_title">Sürüşdürmə örtüyü mətn ölçüsü</string>
<string name="revanced_swipe_text_overlay_size_summary">Sürüşmə üçün mətn ölçüsü 1-30 arasındadır</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">Mətn ölçüsü 1-30 arası olmalıdır</string>
<string name="revanced_swipe_threshold_title">Sürüşdürmə böyüklük həddi</string>
<string name="revanced_swipe_threshold_summary">Sürüşdürmənin icra edilməsi üçün son dəyər</string>
<string name="revanced_swipe_volume_sensitivity_title">Səs səviyyəsin sürüşdürmə təzyiqi</string>
<string name="revanced_swipe_volume_sensitivity_summary">Hər sürüşdürmədə səs səviyyəsi nə qədər dəyişir</string>
<string name="revanced_swipe_show_circular_overlay_title">Dairəvi örtüyü göstər</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Dairəvi örtük göstərilir</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Düzünə örtük göstərilir</string>
<string name="revanced_swipe_overlay_minimal_style_title">Ən kiçik üslubu aktivləşdir</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Ən kiçik örtük üslubu aktivləşdirilib</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">Ən kiçik örtük üslubu qapalıdır</string>
<string name="revanced_swipe_overlay_style_title">Sürüşmə örtüyü üslubu</string>
<string name="revanced_swipe_overlay_style_entry_1">Üfüqi örtük</string>
<string name="revanced_swipe_overlay_style_entry_2">Üfüqi örtük (ən kiçik- yüksək)</string>
<string name="revanced_swipe_overlay_style_entry_3">Üfüqi örtük (ən kiçik - mərkəz)</string>
<string name="revanced_swipe_overlay_style_entry_4">Dairəvi örtük</string>
<string name="revanced_swipe_overlay_style_entry_5">Dairəvi örtük (ən kiçik)</string>
<string name="revanced_swipe_overlay_style_entry_6">Şaquli örtük</string>
<string name="revanced_swipe_overlay_style_entry_7">Şaquli örtük (ən kiçik)</string>
<string name="revanced_swipe_change_video_title">Videoları ötürmək üçün sürüşdürməni aktiv et</string>
<string name="revanced_swipe_change_video_summary_on">Tam ekran rejimində sürüşdürmə növbəti/əvvəlki videoya ötürəcək</string>
<string name="revanced_swipe_change_video_summary_off">Tam ekran rejimində sürüşdürmə növbəti/əvvəlki videoya ötürməyəcək</string>
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_disable_auto_captions_title">Avtomatik titrləri qeyri-aktiv et</string>
<string name="revanced_disable_auto_captions_title">Avtomatik titrləri qapat</string>
<string name="revanced_disable_auto_captions_summary_on">Avtomatik titrlər qapalıdır</string>
<string name="revanced_disable_auto_captions_summary_off">Avtomatik titrlər aktivdir</string>
</patch>
<patch id="layout.buttons.action.hideButtonsPatch">
<string name="revanced_hide_buttons_screen_title">Fəaliyyət düymələri</string>
@@ -511,6 +522,11 @@ Ekranın sağ tərəfində düzünə sürüşdürərək səs səviyyəsini tənz
<string name="revanced_hide_thanks_button_title">\"Təşəkkürlər\"i gizlət</string>
<string name="revanced_hide_thanks_button_summary_on">Təşəkkür düyməsi gizlidir</string>
<string name="revanced_hide_thanks_button_summary_off">Təşəkkür düyməsi göstərilir</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">Soruş\'u Gizlət</string>
<string name="revanced_hide_ask_button_summary_on">Soruş düyməsi gizlidir</string>
<string name="revanced_hide_ask_button_summary_off">\"Soruş\" düyməsi göstərilir</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Kəsmə/ gizlət</string>
<string name="revanced_hide_clip_button_summary_on">Kəsmə düyməsi gizlidir</string>
@@ -829,6 +845,8 @@ Məhdudiyyət: Bəyənməmələr gizli rejimdə görünməyə bilər"</string>
<string name="revanced_ryd_compact_layout_summary_on">Daha kiçik en üçün hazırlanmış Bəyən düyməsi</string>
<string name="revanced_ryd_compact_layout_summary_off">Ən yaxşı görünüş üçün tərtib edilmiş Bəyən düyməsi</string>
<string name="revanced_ryd_estimated_like_title">Təxmini bəyənmələri göstər</string>
<string name="revanced_ryd_estimated_like_summary_on">Bəyənmələri olmayan videolar təxmini bəyənmə sayını göstərir</string>
<string name="revanced_ryd_estimated_like_summary_off">Təxmini bəyənmələr göstərilmir</string>
<string name="revanced_ryd_toast_on_connection_error_title">API əlçatan deyilsə ani bildiriş göstər</string>
<string name="revanced_ryd_toast_on_connection_error_summary_on">Return YouTube Dislike əlçatan deyilsə ani bildiriş göstər</string>
<string name="revanced_ryd_toast_on_connection_error_summary_off">Return YouTube Dislike əlçatan deyilsə ani bildiriş göstərmə</string>
@@ -1102,7 +1120,7 @@ Sonradan qapadılarsa, UI səhvlərin önləmək üçün tətbiq məlumatların
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Köhnə fəaliyyət simvolların bərpa et</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Başlaıc səhifəsini təyin et</string>
<string name="revanced_change_start_page_title">Başlatma səhifəsini dəyişdir</string>
<string name="revanced_change_start_page_entry_default">İlkin</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Bütün abunəliklər</string>
<string name="revanced_change_start_page_entry_browse">Kanallara nəzər yetir</string>
@@ -1120,13 +1138,18 @@ Sonradan qapadılarsa, UI səhvlərin önləmək üçün tətbiq məlumatların
<string name="revanced_change_start_page_entry_notifications">Bildirişlər</string>
<string name="revanced_change_start_page_entry_playlists">Pleylistlər</string>
<string name="revanced_change_start_page_entry_search">Axtarış</string>
<string name="revanced_change_start_page_entry_shopping">Alış-Veriş</string>
<string name="revanced_change_start_page_entry_shopping">Mağaza</string>
<string name="revanced_change_start_page_entry_sports">İdman</string>
<string name="revanced_change_start_page_entry_subscriptions">Abunəliklər</string>
<string name="revanced_change_start_page_entry_trending">Trendlər</string>
<string name="revanced_change_start_page_entry_virtual_reality">Faktiki Həyat</string>
<string name="revanced_change_start_page_entry_watch_later">Sonra izlə</string>
<string name="revanced_change_start_page_entry_your_clips">Klipləriniz</string>
<string name="revanced_change_start_page_always_title"> Başlatma səhifəsini həmişə dəyişdir</string>
<string name="revanced_change_start_page_always_summary_on">"Başlatma səhifəsi həmişə dəyişdirilir
Məhdudiyyət: Alətlər cizgisindəki geri düyməsin istifadə işləməyə bilər"</string>
<string name="revanced_change_start_page_always_summary_off">Başlatma səhifəsi yalnız tətbiq işə salındıqda dəyişdirilir</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Shorts oynadıcı başladıcını qapat</string>
@@ -1232,7 +1255,7 @@ Bunu aktivləşdirmə, bəzi regionlarda əngəllənib silinən şəkilləri dü
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow &amp; Orijinal miniatürlər</string>
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow &amp; Kadr çəkilişlər</string>
<string name="revanced_alt_thumbnail_options_entry_4">Kadr çəkilişləri</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow YouTube videoları üçün izdiham mənbəli miniatürlər təqdim edir. Bu miniatürlər YouTube tərəfindən təqdim edilənlərdən dəfələrlə daha uyğundur.
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow YouTube videoları üçün çox mənbəli miniatürlər təqdim edir. Bu miniatürlər YouTube tərəfindən təqdim edilənlərdən dəfələrlə daha uyğundur.
Aktivləşdirilərsə, video URL-lər API alıcısına göndəriləcək və başqa məlumat göndərilməyəcək. Videonun DeArrow miniatürləri yoxdursa, orijinal və ya hələ də çəkilişlər göstərilir.
@@ -1287,6 +1310,9 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
<string name="microg_settings_title">GmsCore Tənzimləmələri</string>
<string name="microg_settings_summary">GmsCore üçün Tənzimləmələr</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Bu yaxınlarda hesabınıza giriş məlumatlarınızı dəyişmisinizsə, MicroG-ni silin və təkrar quraşdırın.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL yönləndirmələrini ötür</string>
<string name="revanced_bypass_url_redirects_summary_on">URL yönləndirmələri ötürülür</string>

View File

@@ -459,21 +459,30 @@ Second \"item\" text"</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Уключыць жэст аўтаматычнай яркасці</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Правядзіце пальцам уніз да самага нізкага значэння яркасці, каб уключыць аўтаматычную яркасць</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Правядзенне пальцам уніз да самага нізкага значэння не ўключае аўтаматычную яркасць</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Аўто</string>
<string name="revanced_swipe_overlay_timeout_title">Тайм-аўт накладання пальцам</string>
<string name="revanced_swipe_overlay_timeout_summary">Працягласць бачнага накладання ў мілісекундах</string>
<string name="revanced_swipe_overlay_background_opacity_title">Непразрыстасць фону накладкі пракруткі</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Значэнне непразрыстасці паміж 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Непразрыстасць пракруткі павінна быць паміж 0-100</string>
<string name="revanced_swipe_overlay_progress_color_title">Колер слупка прагрэсу накладкі правядзення</string>
<string name="revanced_swipe_overlay_progress_color_summary">Колер слупка прагрэсу для рэгулявання гучнасці і яркасці</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Несапраўдны колер слупка прагрэсу</string>
<string name="revanced_swipe_text_overlay_size_title">Памер тэксту накладкі правядзення</string>
<string name="revanced_swipe_text_overlay_size_summary">Памер тэксту для накладкі правядзення ад 1 да 30</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">Памер тэксту павінен быць у межах ад 1 да 30</string>
<string name="revanced_swipe_threshold_title">Парог велічыні пальцам</string>
<string name="revanced_swipe_threshold_summary">Велічыня парогавага значэння для правядзення пальцам</string>
<string name="revanced_swipe_volume_sensitivity_title">Адчувальнасць правядзення для гучнасці</string>
<string name="revanced_swipe_volume_sensitivity_summary">Наколькі змяняецца гучнасць пры кожным правядзенні</string>
<string name="revanced_swipe_show_circular_overlay_title">Паказваць кругавое накладанне</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Кругавое накладанне паказваецца</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Гарызантальнае накладанне паказваецца</string>
<string name="revanced_swipe_overlay_minimal_style_title">Уключыць мінімальны стыль</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Уключаны мінімальны стыль накладання</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">Мінімальны стыль накладання выключаны</string>
<string name="revanced_swipe_overlay_style_title">Стыль накладкі правядзення</string>
<string name="revanced_swipe_overlay_style_entry_1">Гарызантальная накладка</string>
<string name="revanced_swipe_overlay_style_entry_2">Гарызантальная накладка (мінімальная — зверху)</string>
<string name="revanced_swipe_overlay_style_entry_3">Гарызантальная накладка (мінімальная — па цэнтры)</string>
<string name="revanced_swipe_overlay_style_entry_4">Кругавая накладка</string>
<string name="revanced_swipe_overlay_style_entry_5">Кругавая накладка (мінімальная)</string>
<string name="revanced_swipe_overlay_style_entry_6">Вертыкальная накладка</string>
<string name="revanced_swipe_overlay_style_entry_7">Вертыкальная накладка (мінімальная)</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>
@@ -513,6 +522,11 @@ Second \"item\" text"</string>
<string name="revanced_hide_thanks_button_title">Схаваць Дзякуй</string>
<string name="revanced_hide_thanks_button_summary_on">Кнопка падзякі схавана</string>
<string name="revanced_hide_thanks_button_summary_off">Паказана кнопка падзякі</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">Схаваць кнопку «Запытацца»</string>
<string name="revanced_hide_ask_button_summary_on">Кнопка «Запытацца» схаваная</string>
<string name="revanced_hide_ask_button_summary_off">Кнопка «Запытацца» паказаная</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Схаваць кліп</string>
<string name="revanced_hide_clip_button_summary_on">Кнопка кліпа схавана</string>
@@ -1108,7 +1122,7 @@ Second \"item\" text"</string>
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Аднаўленне старых значкоў навігацыі</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Усталяваць стартавую старонку</string>
<string name="revanced_change_start_page_title">Змяніць стартавую старонку</string>
<string name="revanced_change_start_page_entry_default">Па змаўчанні</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Усе падпіскі</string>
<string name="revanced_change_start_page_entry_browse">Обзор каналов</string>
@@ -1133,6 +1147,11 @@ Second \"item\" text"</string>
<string name="revanced_change_start_page_entry_virtual_reality">Віртуальная рэальнасць</string>
<string name="revanced_change_start_page_entry_watch_later">Посмотреть позже</string>
<string name="revanced_change_start_page_entry_your_clips">Вашыя кліпы</string>
<string name="revanced_change_start_page_always_title">Заўсёды змяняць стартавую старонку</string>
<string name="revanced_change_start_page_always_summary_on">"Стартавая старонка заўсёды зменена
Абмежаванне: выкарыстанне кнопкі «Назад» на панэлі інструментаў можа не працаваць"</string>
<string name="revanced_change_start_page_always_summary_off">Стартавая старонка змяняецца толькі пры запуску праграмы</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Адключыць аднаўленне прайгравання Shorts</string>
@@ -1293,6 +1312,9 @@ Second \"item\" text"</string>
<string name="microg_settings_title">Налады GmsCore</string>
<string name="microg_settings_summary">Налады для GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Калі вы нядаўна змянілі даныя для ўваходу ў свой уліковы запіс, выдаліце і пераўсталюйце MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Абыход URL-перанакіраванняў</string>
<string name="revanced_bypass_url_redirects_summary_on">Перанакіраванне URL абыходзіць</string>

View File

@@ -459,21 +459,30 @@ Second \"item\" text"</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Задаване на яркост чрез плъзгане</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Плъзгането надолу до най-ниската стойност на жеста за яркост, за да се активира автоматичната яркост</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Плъзгането надолу до най-ниската стойност на жеста за яркост, без дасе активира автоматичната яркост</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Авто</string>
<string name="revanced_swipe_overlay_timeout_title">Задръжка на плъзгащата контрола за показване</string>
<string name="revanced_swipe_overlay_timeout_summary">Време за което плъзгащата контрола е видима.</string>
<string name="revanced_swipe_overlay_background_opacity_title">Плъзгане на фона на наслагването непрозрачност</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Стойност на непрозрачността между 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Непрозрачността на плъзгането трябва да е между 0-100</string>
<string name="revanced_swipe_overlay_progress_color_title">Цвят на лентата за напредък при плъзгане</string>
<string name="revanced_swipe_overlay_progress_color_summary">Цветът на лентата за напредък за контролите за сила на звука и яркост</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Невалиден цвят на лентата за напредък</string>
<string name="revanced_swipe_text_overlay_size_title">Размер на текста на наслагването при плъзгане</string>
<string name="revanced_swipe_text_overlay_size_summary">Размерът на текста за наслагването при плъзгане между 1-30</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">Размерът на текста трябва да е между 1-30</string>
<string name="revanced_swipe_threshold_title">Праг на величината на плъзгане</string>
<string name="revanced_swipe_threshold_summary">Праг преди да се осъществи плъзгането</string>
<string name="revanced_swipe_volume_sensitivity_title">Чувствителност при плъзгане за сила на звука</string>
<string name="revanced_swipe_volume_sensitivity_summary">Колко се променя силата на звука при всяко плъзгане</string>
<string name="revanced_swipe_show_circular_overlay_title">Показване на кръгъл овърлей</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Показва се кръгъл овърлей</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Показва се хоризонтален овърлей</string>
<string name="revanced_swipe_overlay_minimal_style_title">Активиране на минимален стил</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Минималният стил на наслагване е активиран</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">Минималният стил на овърлея е деактивиран</string>
<string name="revanced_swipe_overlay_style_title">Стил на наслагване при плъзгане</string>
<string name="revanced_swipe_overlay_style_entry_1">Хоризонтално наслагване</string>
<string name="revanced_swipe_overlay_style_entry_2">Хоризонтално наслагване (минимално отгоре)</string>
<string name="revanced_swipe_overlay_style_entry_3">Хоризонтално наслагване (минимално център)</string>
<string name="revanced_swipe_overlay_style_entry_4">Кръгово наслагване</string>
<string name="revanced_swipe_overlay_style_entry_5">Кръгово наслагване (минимално)</string>
<string name="revanced_swipe_overlay_style_entry_6">Вертикално наслагване</string>
<string name="revanced_swipe_overlay_style_entry_7">Вертикално наслагване (минимално)</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>
@@ -513,6 +522,11 @@ Second \"item\" text"</string>
<string name="revanced_hide_thanks_button_title">Бутон за благодарност</string>
<string name="revanced_hide_thanks_button_summary_on">Бутона за благодарност е скрит</string>
<string name="revanced_hide_thanks_button_summary_off">Бутона за благодарност се показва</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">Скриване на \"Попитай\"</string>
<string name="revanced_hide_ask_button_summary_on">Бутонът \"Попитай\" е скрит</string>
<string name="revanced_hide_ask_button_summary_off">Бутонът \"Попитай\" е показан</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Бутон за създаване на клип</string>
<string name="revanced_hide_clip_button_summary_on">Бутона за клип е скрит</string>
@@ -1107,7 +1121,7 @@ Second \"item\" text"</string>
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Възстановяване на стари икони за навигация</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Задай начална страница</string>
<string name="revanced_change_start_page_title">Промяна на началната страница</string>
<string name="revanced_change_start_page_entry_default">По подразбиране</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Всички абонаменти</string>
<string name="revanced_change_start_page_entry_browse">Разглеждане на канала</string>
@@ -1132,6 +1146,11 @@ Second \"item\" text"</string>
<string name="revanced_change_start_page_entry_virtual_reality">Виртуална реалност</string>
<string name="revanced_change_start_page_entry_watch_later">Гледай по-късно</string>
<string name="revanced_change_start_page_entry_your_clips">Вашите клипове</string>
<string name="revanced_change_start_page_always_title">Винаги променяйте началната страница</string>
<string name="revanced_change_start_page_always_summary_on">"Началната страница винаги се променя
Ограничение: Използването на бутона за връщане назад в лентата с инструменти може да не работи"</string>
<string name="revanced_change_start_page_always_summary_off">Началната страница се променя само при стартиране на приложението</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Скриване на Shorts плейъра при стартиране</string>
@@ -1292,6 +1311,9 @@ Second \"item\" text"</string>
<string name="microg_settings_title">GmsCore Настройки</string>
<string name="microg_settings_summary">Настройки на GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Ако наскоро сте променили данните си за вход в профила, деинсталирайте и инсталирайте отново MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Заобикаляне на URL пренасочване</string>
<string name="revanced_bypass_url_redirects_summary_on">URL пренасочванията се заобикалят</string>

View File

@@ -459,21 +459,30 @@ MicroG-এর জন্য ব্যাটারি অপ্টিমাইজ
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">স্বয়ংক্রিয়-উজ্জ্বলতার অঙ্গভঙ্গি সক্রিয় করুন</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">উজ্জ্বলতার অঙ্গভঙ্গির সর্বনিম্ন মানে সোয়াইপ ডাউন করলে অটো-উজ্জ্বলতা সক্ষম হয়</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">সর্বনিম্ন মানে সোয়াইপ ডাউন করলে অটো-উজ্জ্বলতা সক্ষম হয় না</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">স্বতস্ফূর্তভাবে</string>
<string name="revanced_swipe_overlay_timeout_title">ওভার-লে টাইম আউট</string>
<string name="revanced_swipe_overlay_timeout_summary">কত মিলিসেকেন্ডের জন্য ওভারলে দৃশ্যমান হবে</string>
<string name="revanced_swipe_overlay_background_opacity_title">অস্বচ্ছতা</string>
<string name="revanced_swipe_overlay_background_opacity_summary">0-100 এর মধ্যে অস্বচ্ছতার মান</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">সোয়াইপের অস্বচ্ছতা অবশ্যই 0-100 এর মধ্যে হতে হবে</string>
<string name="revanced_swipe_overlay_progress_color_title">সোয়াইপ ওভারলে প্রগ্রেস বার এর রং</string>
<string name="revanced_swipe_overlay_progress_color_summary">ভলিউম এবং উজ্জ্বলতা নিয়ন্ত্রণের জন্য প্রগ্রেস বার এর রং</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">অবৈধ প্রগ্রেস বার রং</string>
<string name="revanced_swipe_text_overlay_size_title">সোয়াইপ ওভারলে টেক্সট সাইজ</string>
<string name="revanced_swipe_text_overlay_size_summary">সোয়াইপ ওভারলে-এর জন্য টেক্সট সাইজ ১-৩০ এর মধ্যে</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">টেক্সট সাইজ অবশ্যই ১-৩০ এর মধ্যে হতে হবে</string>
<string name="revanced_swipe_threshold_title">সোয়াইপ থ্রেশহোল্ড এর মাত্রা</string>
<string name="revanced_swipe_threshold_summary">সোয়াইপ করার থ্রেশহোল্ডের পরিমাণ</string>
<string name="revanced_swipe_volume_sensitivity_title">ভলিউম সোয়াইপ সংবেদনশীলতা</string>
<string name="revanced_swipe_volume_sensitivity_summary">প্রতি সোয়াইপে ভলিউম কতটা পরিবর্তিত হয়</string>
<string name="revanced_swipe_show_circular_overlay_title">বৃত্তাকার ওভারলে দেখান</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">বৃত্তাকার ওভারলে দেখানো হয়েছ</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">অনুভূমিক ওভারলে দেখানো হয়েছে</string>
<string name="revanced_swipe_overlay_minimal_style_title">ন্যূনতম শৈলী সক্ষম করুন</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimal overlay style enabled করা হয়েছ</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">ন্যূনতম ওভারলে শৈলী নিষ্ক্রিয় করা হয়েছে</string>
<string name="revanced_swipe_overlay_style_title">সোয়াইপ ওভারলে শৈলী</string>
<string name="revanced_swipe_overlay_style_entry_1">অনুভূমিক ওভারল</string>
<string name="revanced_swipe_overlay_style_entry_2">অনুভূমিক ওভারলে (ন্যূনতম - উপরে)</string>
<string name="revanced_swipe_overlay_style_entry_3">অনুভূমিক ওভারলে (ন্যূনতম - কেন্দ্র)</string>
<string name="revanced_swipe_overlay_style_entry_4">বৃত্তাকার ওভারল</string>
<string name="revanced_swipe_overlay_style_entry_5">বৃত্তাকার ওভারলে (ন্যূনতম)</string>
<string name="revanced_swipe_overlay_style_entry_6">উল্লম্ব ওভারলে</string>
<string name="revanced_swipe_overlay_style_entry_7">উল্লম্ব ওভারলে (ন্যূনতম)</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>
@@ -513,6 +522,11 @@ MicroG-এর জন্য ব্যাটারি অপ্টিমাইজ
<string name="revanced_hide_thanks_button_title">ধন্যবাদ লুকান</string>
<string name="revanced_hide_thanks_button_summary_on">ধন্যবাদ বোতাম লুকানো আছে</string>
<string name="revanced_hide_thanks_button_summary_off">ধন্যবাদ বোতাম দেখানো হয়</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">জিজ্ঞাসা লুকান</string>
<string name="revanced_hide_ask_button_summary_on">জিজ্ঞাসা বোতাম লুকানো আছে</string>
<string name="revanced_hide_ask_button_summary_off">জিজ্ঞাসা বোতাম দেখানো হয়েছে</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">ক্লিপ লুকান</string>
<string name="revanced_hide_clip_button_summary_on">ক্লিপ বোতাম লুকিয়ে রয়েছে</string>
@@ -1107,7 +1121,7 @@ YouTube সেটিংসে অটো প্লে পরিবর্তন
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - পুরনো নেভিগেশন আইকন পুনরুদ্ধার করুন</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">শুরুর পৃষ্ঠা সেট করুন</string>
<string name="revanced_change_start_page_title">শুরুর পৃষ্ঠা পরিবর্তন করুন</string>
<string name="revanced_change_start_page_entry_default">পূর্ব-নির্ধারিত</string>
<string name="revanced_change_start_page_entry_all_subscriptions">অন্য়াসব রভর্বাশার চিয়ুট</string>
<string name="revanced_change_start_page_entry_browse">চ্যানেল ব্রাউজ করুন</string>
@@ -1132,6 +1146,11 @@ YouTube সেটিংসে অটো প্লে পরিবর্তন
<string name="revanced_change_start_page_entry_virtual_reality">বির্চুভ্য়াল টিএলিটি</string>
<string name="revanced_change_start_page_entry_watch_later">পরে দেখুন</string>
<string name="revanced_change_start_page_entry_your_clips">আপ্নার কলিপ</string>
<string name="revanced_change_start_page_always_title">সর্বদা শুরুর পৃষ্ঠা পরিবর্তন করুন</string>
<string name="revanced_change_start_page_always_summary_on">"শুরুর পৃষ্ঠা সবসময় পরিবর্তিত হয়
সীমাবদ্ধতা: টুলবারে পিছনের বোতাম ব্যবহার করলে কাজ নাও করতে পারে"</string>
<string name="revanced_change_start_page_always_summary_off">অ্যাপ শুরু করার সময় শুধুমাত্র শুরুর পৃষ্ঠা পরিবর্তন করা হয়</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Shorts প্লেয়ার আবার চালানো নিষ্ক্রিয় করুন</string>
@@ -1292,6 +1311,9 @@ DeArrow সম্পর্কে আরও জানতে এখানে ট
<string name="microg_settings_title">GmsCore সেটিং</string>
<string name="microg_settings_summary">GmsCore এর জন্য সেটিং</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">আপনি যদি সম্প্রতি আপনার অ্যাকাউন্ট লগইন বিশদ পরিবর্তন করে থাকেন, তবে MicroG আনইনস্টল করুন এবং পুনরায় ইনস্টল করুন।</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL পুনঃনির্দেশ বাইপাস করুন</string>
<string name="revanced_bypass_url_redirects_summary_on">URL পুনঃনির্দেশ বাইপাস করছে</string>

View File

@@ -84,6 +84,8 @@ Second \"item\" text"</string>
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch>
@@ -195,6 +197,8 @@ Second \"item\" text"</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
</patch>
<patch id="misc.links.openLinksExternallyPatch">

View File

@@ -459,21 +459,30 @@ Ajusteu el volum lliscant verticalment a la part dreta de la pantalla"</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Activa el gest de la brillantor automàtica</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Deslizar hacia abajo hasta el valor más bajo del gesto de brillo activa el brillo automático</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Deslizar hacia abajo hasta el valor más bajo no activa el brillo automático</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automàtic</string>
<string name="revanced_swipe_overlay_timeout_title">Temps límite de superposició de lliscament</string>
<string name="revanced_swipe_overlay_timeout_summary">La quantitat de mil·lisegons que la superposició és visible</string>
<string name="revanced_swipe_overlay_background_opacity_title">Opacitat del fons de la superposició de lliscament</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Valor d\'opacitat entre 0 i 100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">L\'opacitat de lliscament ha d\'estar entre 0 i 100</string>
<string name="revanced_swipe_overlay_progress_color_title">Color de la barra de progrés de la superposició lliscant</string>
<string name="revanced_swipe_overlay_progress_color_summary">El color de la barra de progrés per als controls de volum i brillantor</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Color de barra de progrés no vàlid</string>
<string name="revanced_swipe_text_overlay_size_title">Mida del text de la superposició lliscant</string>
<string name="revanced_swipe_text_overlay_size_summary">La mida del text per a la superposició lliscant entre 1 i 30</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">La mida del text ha d\'estar entre 1 i 30</string>
<string name="revanced_swipe_threshold_title">Llindar de magnitud de lliscament</string>
<string name="revanced_swipe_threshold_summary">La quantitat de llindar per a què es produeixi el desplaçament</string>
<string name="revanced_swipe_volume_sensitivity_title">Sensibilitat del lliscament de volum</string>
<string name="revanced_swipe_volume_sensitivity_summary">Quant canvia el volum per lliscament</string>
<string name="revanced_swipe_show_circular_overlay_title">Mostra la superposició circular</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Es mostra la superposició circular</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Es mostra la superposició horitzontal</string>
<string name="revanced_swipe_overlay_minimal_style_title">Activa l\'estil minimalista</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">S\'ha activat l\'estil de superposició minimalista</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">L\'estil de superposició mínima està desactivat</string>
<string name="revanced_swipe_overlay_style_title">Estil de superposició de lliscament</string>
<string name="revanced_swipe_overlay_style_entry_1">Superposició horitzontal</string>
<string name="revanced_swipe_overlay_style_entry_2">Superposició horitzontal (mínima - part superior)</string>
<string name="revanced_swipe_overlay_style_entry_3">Superposició horitzontal (mínima - centre)</string>
<string name="revanced_swipe_overlay_style_entry_4">Superposició circular</string>
<string name="revanced_swipe_overlay_style_entry_5">Superposició circular (mínima)</string>
<string name="revanced_swipe_overlay_style_entry_6">Superposició vertical</string>
<string name="revanced_swipe_overlay_style_entry_7">Superposició vertical (mínima)</string>
<string name="revanced_swipe_change_video_title">Activa la funció de lliscament per canviar vídeos</string>
<string name="revanced_swipe_change_video_summary_on">Lliscar en mode de pantalla completa canviarà al vídeo següent/anterior</string>
<string name="revanced_swipe_change_video_summary_off">Lliscar en mode de pantalla completa no canviarà al vídeo següent/anterior</string>
@@ -513,6 +522,11 @@ Ajusteu el volum lliscant verticalment a la part dreta de la pantalla"</string>
<string name="revanced_hide_thanks_button_title">Amaga Gràcies</string>
<string name="revanced_hide_thanks_button_summary_on">S\'ha amagat el botó de gràcies</string>
<string name="revanced_hide_thanks_button_summary_off">El botó Gràcies es mostra</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">Amaga la sol·licitud</string>
<string name="revanced_hide_ask_button_summary_on">El botó de sol·licitud està amagat</string>
<string name="revanced_hide_ask_button_summary_off">El botó de sol·licitud es mostra</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Amaga Clip</string>
<string name="revanced_hide_clip_button_summary_on">El botó Clip s\'amaga</string>
@@ -1106,7 +1120,7 @@ Si després es desactiva, es recomana esborrar les dades de l'aplicació per evi
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Restaura les icones de navegació antigues</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Defineix la pàgina d\'inici</string>
<string name="revanced_change_start_page_title">Canvia la pàgina d\'inici</string>
<string name="revanced_change_start_page_entry_default">Per defecte</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Totes les subscripcions</string>
<string name="revanced_change_start_page_entry_browse">Explora canals</string>
@@ -1131,6 +1145,11 @@ Si després es desactiva, es recomana esborrar les dades de l'aplicació per evi
<string name="revanced_change_start_page_entry_virtual_reality">Realitat virtual</string>
<string name="revanced_change_start_page_entry_watch_later">Veure més tard</string>
<string name="revanced_change_start_page_entry_your_clips">Els teus clips</string>
<string name="revanced_change_start_page_always_title">Canvia sempre la pàgina d\'inici</string>
<string name="revanced_change_start_page_always_summary_on">"La pàgina d'inici sempre es canvia
Limitació: és possible que el botó Enrere de la barra d'eines no funcioni"</string>
<string name="revanced_change_start_page_always_summary_off">La pàgina d\'inici només es canvia en iniciar l\'aplicació</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Desactiva la represa del reproductor de Shorts</string>
@@ -1291,6 +1310,9 @@ Si actives aquesta opció, es poden desbloquejar qualitats de vídeo més altes"
<string name="microg_settings_title">Configuració de GmsCore</string>
<string name="microg_settings_summary">Configuració de GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Si recentment heu canviat les dades d\'inici de sessió del vostre compte, desinstal·leu i torneu a instal·lar MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Omet les redireccions d\'URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Les redireccions d\'URL s\'ometent</string>

View File

@@ -459,21 +459,30 @@ Hlasitost se upravuje svislým přejetím po pravé straně obrazovky"</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Povolit gesto pro automatický jas</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Přejetím dolů na nejnižší hodnotu gesta pro jas aktivujete automatický jas</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Přejetím dolů na nejnižší hodnotu neaktivujete automatický jas</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automaticky</string>
<string name="revanced_swipe_overlay_timeout_title">Timeout překrytí gesta</string>
<string name="revanced_swipe_overlay_timeout_summary">Doba v milisekundách, po kterou je překrytí viditelné</string>
<string name="revanced_swipe_overlay_background_opacity_title">Průsvitnost pozadí překrytí tažením</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Hodnota průsvitnosti mezi 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Průsvitnost tažení musí být mezi 0-100</string>
<string name="revanced_swipe_overlay_progress_color_title">Barva ukazatele průběhu překrytí přejetím</string>
<string name="revanced_swipe_overlay_progress_color_summary">Barva ukazatele průběhu pro ovládání hlasitosti a jasu</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Neplatná barva ukazatele průběhu</string>
<string name="revanced_swipe_text_overlay_size_title">Velikost textu překrytí přejetím</string>
<string name="revanced_swipe_text_overlay_size_summary">Velikost textu překrytí přejetím mezi 130</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">Velikost textu musí být mezi 130</string>
<string name="revanced_swipe_threshold_title">Práh vynucení gesta</string>
<string name="revanced_swipe_threshold_summary">Velikost prahu pro provedení gesta</string>
<string name="revanced_swipe_volume_sensitivity_title">Citlivost přejetí hlasitosti</string>
<string name="revanced_swipe_volume_sensitivity_summary">O kolik se změní hlasitost na jedno přejetí</string>
<string name="revanced_swipe_show_circular_overlay_title">Zobrazit kruhovou překryvnou vrstvu</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Zobrazuje se kruhová překryvná vrstva</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Zobrazuje se vodorovná překryvná vrstva</string>
<string name="revanced_swipe_overlay_minimal_style_title">Povolit minimální styl</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimální styl překrytí je povolen</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">Minimální styl překryvné vrstvy je zakázán</string>
<string name="revanced_swipe_overlay_style_title">Styl překrytí přejetím</string>
<string name="revanced_swipe_overlay_style_entry_1">Vodorovné překrytí</string>
<string name="revanced_swipe_overlay_style_entry_2">Vodorovné překrytí (minimalistické nahoře)</string>
<string name="revanced_swipe_overlay_style_entry_3">Vodorovné překrytí (minimalistické uprostřed)</string>
<string name="revanced_swipe_overlay_style_entry_4">Kruhové překrytí</string>
<string name="revanced_swipe_overlay_style_entry_5">Kruhové překrytí (minimalistické)</string>
<string name="revanced_swipe_overlay_style_entry_6">Svislé překrytí</string>
<string name="revanced_swipe_overlay_style_entry_7">Svislé překrytí (minimalistické)</string>
<string name="revanced_swipe_change_video_title">Povolit přejetí prstem pro změnu videa</string>
<string name="revanced_swipe_change_video_summary_on">Přejetí prstem v režimu celé obrazovky změní video na další/předchozí</string>
<string name="revanced_swipe_change_video_summary_off">Přejetí prstem v režimu celé obrazovky nebude video měnit na další/předchozí</string>
@@ -513,6 +522,11 @@ Hlasitost se upravuje svislým přejetím po pravé straně obrazovky"</string>
<string name="revanced_hide_thanks_button_title">Skrýt Poděkování</string>
<string name="revanced_hide_thanks_button_summary_on">Tlačítko Poděkování je skryto</string>
<string name="revanced_hide_thanks_button_summary_off">Tlačítko Poděkování je zobrazeno</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">Skrýt Zeptat se</string>
<string name="revanced_hide_ask_button_summary_on">Tlačítko Zeptat se je skryté</string>
<string name="revanced_hide_ask_button_summary_off">Tlačítko Zeptat se je zobrazeno</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Skrýt Klip</string>
<string name="revanced_hide_clip_button_summary_on">Tlačítko Klip je skryto</string>
@@ -1106,7 +1120,7 @@ Pokud bude později vypnuta, doporučujeme vymazat data aplikace, aby se zabrán
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 Obnovit staré ikony navigace</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Nastavit úvodní stránku</string>
<string name="revanced_change_start_page_title">Změnit úvodní stránku</string>
<string name="revanced_change_start_page_entry_default">Výchozí</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Všechna předplatná</string>
<string name="revanced_change_start_page_entry_browse">Procházet kanály</string>
@@ -1131,6 +1145,11 @@ Pokud bude později vypnuta, doporučujeme vymazat data aplikace, aby se zabrán
<string name="revanced_change_start_page_entry_virtual_reality">Virtuální realita</string>
<string name="revanced_change_start_page_entry_watch_later">Sledovat později</string>
<string name="revanced_change_start_page_entry_your_clips">Vaše klipy</string>
<string name="revanced_change_start_page_always_title">Vždy změnit úvodní stránku</string>
<string name="revanced_change_start_page_always_summary_on">"Úvodní stránka je vždy změněna
Omezení: Použití tlačítka zpět na panelu nástrojů nemusí fungovat"</string>
<string name="revanced_change_start_page_always_summary_off">Úvodní stránka se změní pouze při spuštění aplikace</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Zakázat obnovení přehrávače Shorts</string>
@@ -1291,6 +1310,9 @@ Povolením této funkce lze odemknout vyšší kvality videa"</string>
<string name="microg_settings_title">Nastavení GmsCore</string>
<string name="microg_settings_summary">Nastavení pro GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Pokud jste nedávno změnili přihlašovací údaje svého účtu, odinstalujte a znovu nainstalujte MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Obcházet přesměrování URL</string>
<string name="revanced_bypass_url_redirects_summary_on">Přesměrování URL jsou obcházena</string>

View File

@@ -423,21 +423,30 @@ Juster lydstyrken ved at swipe lodret i højre side af skærmen"</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Aktivér auto-lysstyrke-bevægelse</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Stryg ned til den laveste værdi af lysstyrke-bevægelsen aktiverer auto-lysstyrke</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Stryg ned til den laveste værdi aktiverer ikke auto-lysstyrke</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Automatisk</string>
<string name="revanced_swipe_overlay_timeout_title">Stryg overlay timeout</string>
<string name="revanced_swipe_overlay_timeout_summary">Mængden af millisekunder, overlayet er synlig</string>
<string name="revanced_swipe_overlay_background_opacity_title">Baggrundsgennemsigtighed for swipe-overlay</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Gennemsigtighedsværdi mellem 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Gennemsigtighed for swipe skal være mellem 0-100</string>
<string name="revanced_swipe_overlay_progress_color_title">Farve på statuslinje for strygeoverlejring</string>
<string name="revanced_swipe_overlay_progress_color_summary">Farven på statuslinjen for lydstyrke- og lysstyrkeknapper</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Ugyldig farve til statuslinjen</string>
<string name="revanced_swipe_text_overlay_size_title">Tekststørrelse for strygeoverlejring</string>
<string name="revanced_swipe_text_overlay_size_summary">Tekststørrelsen for strygeoverlejring mellem 1-30</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">Tekststørrelsen skal være mellem 1-30</string>
<string name="revanced_swipe_threshold_title">Stryg størrelse tærskel</string>
<string name="revanced_swipe_threshold_summary">Beløbet for tærskelværdi for stryg der skal ske</string>
<string name="revanced_swipe_volume_sensitivity_title">Volumen strygefølsomhed</string>
<string name="revanced_swipe_volume_sensitivity_summary">Hvor meget lydstyrken ændres pr. strygning</string>
<string name="revanced_swipe_show_circular_overlay_title">Vis cirkulært overlejring</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Cirkulært overlejring vises</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Horisontalt overlejring vises</string>
<string name="revanced_swipe_overlay_minimal_style_title">Aktiver minimal stil</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimal overlay-stil er aktiveret</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">Minimal overlejringsstil er deaktiveret</string>
<string name="revanced_swipe_overlay_style_title">Stil for stryge-overlay</string>
<string name="revanced_swipe_overlay_style_entry_1">Horisontalt overlay</string>
<string name="revanced_swipe_overlay_style_entry_2">Horisontalt overlay (minimal - top)</string>
<string name="revanced_swipe_overlay_style_entry_3">Horisontalt overlay (minimal - center)</string>
<string name="revanced_swipe_overlay_style_entry_4">Cirkulært overlay</string>
<string name="revanced_swipe_overlay_style_entry_5">Cirkulært overlay (minimal)</string>
<string name="revanced_swipe_overlay_style_entry_6">Vertikalt overlay</string>
<string name="revanced_swipe_overlay_style_entry_7">Vertikalt overlay (minimal)</string>
<string name="revanced_swipe_change_video_title">Aktivér swipe for at skifte videoer</string>
<string name="revanced_swipe_change_video_summary_on">Strygning i fuldskærmstilstand vil ændre til den næste/forrige video</string>
<string name="revanced_swipe_change_video_summary_off">Strygning i fuldskærmstilstand vil ikke ændre til den næste/forrige video</string>
@@ -476,6 +485,11 @@ Juster lydstyrken ved at swipe lodret i højre side af skærmen"</string>
<string name="revanced_hide_thanks_button_title">Skjul Tak</string>
<string name="revanced_hide_thanks_button_summary_on">Tak knappen er skjult</string>
<string name="revanced_hide_thanks_button_summary_off">Tak knappen er vist</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">Skjul Spørg</string>
<string name="revanced_hide_ask_button_summary_on">Spørg-knappen er skjult</string>
<string name="revanced_hide_ask_button_summary_off">Spørg-knappen vises</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Skjul klip</string>
<string name="revanced_hide_clip_button_summary_on">Klip knappen er skjult</string>
@@ -1052,7 +1066,7 @@ Hvis det senere slås fra, anbefales det at rydde app-dataene for at forhindre U
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - Gendan gamle Shorts player ikoner</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Indstil startside</string>
<string name="revanced_change_start_page_title">Skift startside</string>
<string name="revanced_change_start_page_entry_default">Standard</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Alle abonnementer</string>
<string name="revanced_change_start_page_entry_browse">Gennemse kanaler</string>
@@ -1077,6 +1091,11 @@ Hvis det senere slås fra, anbefales det at rydde app-dataene for at forhindre U
<string name="revanced_change_start_page_entry_virtual_reality">Virtual Reality</string>
<string name="revanced_change_start_page_entry_watch_later">Se senere</string>
<string name="revanced_change_start_page_entry_your_clips">Dine klip</string>
<string name="revanced_change_start_page_always_title">Skift altid startside</string>
<string name="revanced_change_start_page_always_summary_on">"Startside ændres altid
Begrænsning: Brug af tilbage-knappen på værktøjslinjen fungerer muligvis ikke"</string>
<string name="revanced_change_start_page_always_summary_off">Startsiden ændres kun ved appstart</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Deaktivér genoptagelse af Shorts spiller</string>
@@ -1233,6 +1252,9 @@ Aktivering af dette kan låse op for højere videokvalitet"</string>
<string name="microg_settings_title">GmsCore Indstillinger</string>
<string name="microg_settings_summary">Indstillinger for GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Hvis du for nylig har ændret dine kontooplysninger, skal du afinstallere og geninstallere MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Bypass URL omdirigeringer</string>
<string name="revanced_bypass_url_redirects_summary_on">URL omdirigeringer er omgået</string>

View File

@@ -452,21 +452,30 @@ Passen Sie die Helligkeit an, indem Sie auf der linken Seite des Bildschirms ver
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Auto-Helligkeit Geste aktivieren</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Wische runter auf den niedrigsten Wert der Helligkeitsgeste, um die automatische Helligkeit zu aktivieren</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Wenn man zum niedrigsten Wert wischt, aktiviert man die automatische Helligkeit nicht</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Autom</string>
<string name="revanced_swipe_overlay_timeout_title">Wischüberlagerungs-Timeout</string>
<string name="revanced_swipe_overlay_timeout_summary">Die Anzahl der Millisekunden, die das Overlay sichtbar ist</string>
<string name="revanced_swipe_overlay_background_opacity_title">Bildschirmüberlagerung Deckkraft Swipe</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Deckkraftwert zwischen 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Die Wischdeckkraft muss zwischen 0 und 100 liegen</string>
<string name="revanced_swipe_overlay_progress_color_title">Farbe des Fortschrittsbalkens für die Wischgesten-Überlagerung</string>
<string name="revanced_swipe_overlay_progress_color_summary">Die Farbe des Fortschrittsbalkens für Lautstärke- und Helligkeitsregler</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Ungültige Farbe für den Fortschrittsbalken</string>
<string name="revanced_swipe_text_overlay_size_title">Textgröße der Wischgesten-Überlagerung</string>
<string name="revanced_swipe_text_overlay_size_summary">Die Textgröße für die Wischgesten-Überlagerung zwischen 1 und 30</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">Die Textgröße muss zwischen 1 und 30 liegen</string>
<string name="revanced_swipe_threshold_title">Wischgrößenschwelle</string>
<string name="revanced_swipe_threshold_summary">Der Schwellenwert für Wischen</string>
<string name="revanced_swipe_volume_sensitivity_title">Lautstärke-Wischgestenempfindlichkeit</string>
<string name="revanced_swipe_volume_sensitivity_summary">Wie stark sich die Lautstärke pro Wisch ändert</string>
<string name="revanced_swipe_show_circular_overlay_title">Kreisförmiges Overlay anzeigen</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Kreisförmiges Overlay wird angezeigt</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Horizontales Overlay wird angezeigt</string>
<string name="revanced_swipe_overlay_minimal_style_title">Minimalen Stil aktivieren</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimaler Overlay-Stil ist aktiviert</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">Minimaler Overlay-Stil ist deaktiviert</string>
<string name="revanced_swipe_overlay_style_title">Design des Wisch-Overlays</string>
<string name="revanced_swipe_overlay_style_entry_1">Horizontales Overlay</string>
<string name="revanced_swipe_overlay_style_entry_2">Horizontales Overlay (minimal - oben)</string>
<string name="revanced_swipe_overlay_style_entry_3">Horizontales Overlay (minimal - Mitte)</string>
<string name="revanced_swipe_overlay_style_entry_4">Kreisförmiges Overlay</string>
<string name="revanced_swipe_overlay_style_entry_5">Kreisförmiges Overlay (minimal)</string>
<string name="revanced_swipe_overlay_style_entry_6">Vertikales Overlay</string>
<string name="revanced_swipe_overlay_style_entry_7">Vertikales Overlay (minimal)</string>
<string name="revanced_swipe_change_video_title">Mit Wischen wechseln Sie zu den Videos</string>
<string name="revanced_swipe_change_video_summary_on">Im Vollbildmodus wischen, um zum nächsten/vorherigen Video zu wechseln</string>
<string name="revanced_swipe_change_video_summary_off">Im Vollbildmodus wischen, um nicht zum nächsten/vorherigen Video zu wechseln</string>
@@ -506,6 +515,11 @@ Passen Sie die Helligkeit an, indem Sie auf der linken Seite des Bildschirms ver
<string name="revanced_hide_thanks_button_title">Dank ausblenden</string>
<string name="revanced_hide_thanks_button_summary_on">Dankeschön-Taste ist ausgeblendet</string>
<string name="revanced_hide_thanks_button_summary_off">Dankeschön Button wird angezeigt</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">Ask ausblenden</string>
<string name="revanced_hide_ask_button_summary_on">Ask-Button ist ausgeblendet</string>
<string name="revanced_hide_ask_button_summary_off">Ask-Button wird angezeigt</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Clip ausblenden</string>
<string name="revanced_hide_clip_button_summary_on">Clip-Button ist ausgeblendet</string>
@@ -1100,7 +1114,7 @@ Wenn Sie die Funktion später deaktivieren, wird empfohlen, die App-Daten zu lö
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Alte Navigations-Symbole wiederherstellen</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Startseite festlegen</string>
<string name="revanced_change_start_page_title">Startseite ändern</string>
<string name="revanced_change_start_page_entry_default">Standard</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Alle Abonnements</string>
<string name="revanced_change_start_page_entry_browse">Kanäle durchsuchen</string>
@@ -1125,6 +1139,11 @@ Wenn Sie die Funktion später deaktivieren, wird empfohlen, die App-Daten zu lö
<string name="revanced_change_start_page_entry_virtual_reality">Virtuelle Realität</string>
<string name="revanced_change_start_page_entry_watch_later">Später ansehen</string>
<string name="revanced_change_start_page_entry_your_clips">Deine Clips</string>
<string name="revanced_change_start_page_always_title">Startseite immer ändern</string>
<string name="revanced_change_start_page_always_summary_on">"Die Startseite wird immer geändert.
Einschränkung: Die Verwendung der Zurück-Taste auf der Symbolleiste funktioniert möglicherweise nicht."</string>
<string name="revanced_change_start_page_always_summary_off">Die Startseite wird nur beim Start der App geändert</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Fortsetzen des Shorts Players deaktivieren</string>
@@ -1285,6 +1304,9 @@ Durch Aktivieren dieser Option können höhere Videoqualitäten freigeschaltet w
<string name="microg_settings_title">GmsCore Einstellungen</string>
<string name="microg_settings_summary">Einstellungen für GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Wenn Sie kürzlich Ihre Kontoanmeldedaten geändert haben, deinstallieren Sie MicroG und installieren Sie es erneut.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL-Weiterleitungen umgehen</string>
<string name="revanced_bypass_url_redirects_summary_on">URL-Umleitungen werden umgangen</string>

View File

@@ -86,7 +86,7 @@ Second \"item\" text"</string>
<string name="revanced_settings_screen_03_feed_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_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>
@@ -461,21 +461,30 @@ Second \"item\" text"</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Ενεργοποίηση χειρονομίας αυτόματης φωτεινότητας</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Η σάρωση προς τα κάτω στη χαμηλότερη τιμή της χειρονομίας φωτεινότητας ενεργοποιεί την αυτόματη φωτεινότητα</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">Η σάρωση προς τα κάτω στη χαμηλότερη τιμή δεν ενεργοποιεί την αυτόματη φωτεινότητα</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Αυτόματη</string>
<string name="revanced_swipe_overlay_timeout_title">Χρονικό όριο εμφάνισης πλαισίου σάρωσης</string>
<string name="revanced_swipe_overlay_timeout_summary">Το χρονικό διάστημα χιλιοστών του δευτερολέπτου που είναι ορατό το πλαίσιο σάρωσης</string>
<string name="revanced_swipe_overlay_background_opacity_title">Αδιαφάνεια φόντου σάρωσης</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Τιμή αδιαφάνειας μεταξύ 0-100</string>
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">Η αδιαφάνεια σάρωσης πρέπει να είναι μεταξύ 0-100</string>
<string name="revanced_swipe_overlay_progress_color_title">Χρώμα γραμμής προόδου σάρωσης</string>
<string name="revanced_swipe_overlay_progress_color_summary">Το χρώμα της γραμμής προόδου για τα στοιχεία ελέγχου έντασης ήχου και φωτεινότητας</string>
<string name="revanced_swipe_overlay_progress_color_invalid_toast">Μη έγκυρο χρώμα γραμμής προόδου</string>
<string name="revanced_swipe_text_overlay_size_title">Μέγεθος κειμένου σάρωσης</string>
<string name="revanced_swipe_text_overlay_size_summary">Το μέγεθος κειμένου για τα στοιχεία ελέγχου σάρωσης μεταξύ 1-30</string>
<string name="revanced_swipe_text_overlay_size_invalid_toast">Το μέγεθος κειμένου πρέπει να είναι μεταξύ 1-30</string>
<string name="revanced_swipe_threshold_title">Κατώτατο όριο μεγέθους σάρωσης</string>
<string name="revanced_swipe_threshold_summary">Η ελάχιστη απόσταση που θα διανύσετε με το δάκτυλο σας για να είναι αναγνωρίσιμη η χειρονομία σάρωσης</string>
<string name="revanced_swipe_volume_sensitivity_title">Ευαισθησία σάρωσης έντασης ήχου</string>
<string name="revanced_swipe_volume_sensitivity_summary">Πόσο αλλάζει η ένταση ήχου ανά σάρωση</string>
<string name="revanced_swipe_show_circular_overlay_title">Εμφάνιση κυκλικής διάταξης</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Η διάταξη των ελέγχων σάρωσης είναι κυκλική</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Η διάταξη των ελέγχων σάρωσης είναι οριζόντια</string>
<string name="revanced_swipe_overlay_minimal_style_title">Διάταξη μινιμαλιστικού στυλ</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Το μινιμαλιστικό στυλ διάταξης των ελέγχων σάρωσης είναι ενεργοποιημένο</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">Το μινιμαλιστικό στυλ διάταξης των ελέγχων σάρωσης είναι απενεργοποιημένο</string>
<string name="revanced_swipe_overlay_style_title">Στυλ διάταξης σάρωσης</string>
<string name="revanced_swipe_overlay_style_entry_1">Οριζόντια διάταξη</string>
<string name="revanced_swipe_overlay_style_entry_2">Οριζόντια διάταξη (ελάχιστη - κορυφή)</string>
<string name="revanced_swipe_overlay_style_entry_3">Οριζόντια διάταξη (ελάχιστη - κέντρο)</string>
<string name="revanced_swipe_overlay_style_entry_4">Κυκλική διάταξη</string>
<string name="revanced_swipe_overlay_style_entry_5">Κυκλική διάταξη (ελάχιστη)</string>
<string name="revanced_swipe_overlay_style_entry_6">Κάθετη διάταξη</string>
<string name="revanced_swipe_overlay_style_entry_7">Κάθετη διάταξη (ελάχιστη)</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>
@@ -515,6 +524,11 @@ Second \"item\" text"</string>
<string name="revanced_hide_thanks_button_title">Κουμπί «Σας ευχαριστούμε»</string>
<string name="revanced_hide_thanks_button_summary_on">Κρυμμένο</string>
<string name="revanced_hide_thanks_button_summary_off">Εμφανίζεται</string>
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
Button only shows if the user ip is from specific region such as the USA or EU. -->
<string name="revanced_hide_ask_button_title">Κουμπί «Ερώτηση»</string>
<string name="revanced_hide_ask_button_summary_on">Κρυμμένο</string>
<string name="revanced_hide_ask_button_summary_off">Εμφανίζεται</string>
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_clip_button_title">Κουμπί «Κλιπ»</string>
<string name="revanced_hide_clip_button_summary_on">Κρυμμένο</string>
@@ -1108,7 +1122,7 @@ Second \"item\" text"</string>
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Επαναφορά παλιών εικονιδίων γραμμής πλοήγησης</string>
</patch>
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Αλλαγή της αρχικής σελίδας</string>
<string name="revanced_change_start_page_title">Αλλαγή αρχικής σελίδας</string>
<string name="revanced_change_start_page_entry_default">Προεπιλογή</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Όλες οι εγγραφές</string>
<string name="revanced_change_start_page_entry_browse">Περιήγηση καναλιών</string>
@@ -1133,6 +1147,11 @@ Second \"item\" text"</string>
<string name="revanced_change_start_page_entry_virtual_reality">Εικονική Πραγματικότητα</string>
<string name="revanced_change_start_page_entry_watch_later">Παρακολούθηση αργότερα</string>
<string name="revanced_change_start_page_entry_your_clips">Τα κλιπ σας</string>
<string name="revanced_change_start_page_always_title">Να αλλάζει πάντα η αρχική σελίδα</string>
<string name="revanced_change_start_page_always_summary_on">"Η αρχική σελίδα αλλάζει πάντα
Περιορισμός: Η χρήση του κουμπιού επιστροφής στη γραμμή εργαλείων ενδέχεται να μην λειτουργεί"</string>
<string name="revanced_change_start_page_always_summary_off">Η αρχική σελίδα αλλάζει μόνο κατά την εκκίνηση της εφαρμογής</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Απενεργοποίηση συνέχισης των Shorts</string>
@@ -1291,6 +1310,9 @@ Second \"item\" text"</string>
<string name="microg_settings_title">Ρυθμίσεις GmsCore</string>
<string name="microg_settings_summary">Ρυθμίσεις για το MicroG GmsCore</string>
</patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">Εάν αλλάξατε πρόσφατα τα στοιχεία σύνδεσης του λογαριασμού σας, απεγκαταστήστε και εγκαταστήστε ξανά το MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">Παράκαμψη ανακατευθύνσεων συνδέσμων</string>
<string name="revanced_bypass_url_redirects_summary_on">Οι ανακατευθύνσεις συνδέσμων URL παρακάμπτονται</string>

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