Compare commits

...

63 Commits

Author SHA1 Message Date
semantic-release-bot
48d5fdf7e1 chore: Release v5.24.0-dev.1 [skip ci]
# [5.24.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.23.0...v5.24.0-dev.1) (2025-05-12)

### Features

* **NU.nl:** Support version `11.3.0` ([#4925](https://github.com/ReVanced/revanced-patches/issues/4925)) ([887c9f0](887c9f0d75))
2025-05-12 22:40:55 +00:00
Jasper Abbink
887c9f0d75 feat(NU.nl): Support version 11.3.0 (#4925) 2025-05-13 02:37:41 +04:00
github-actions[bot]
7de4c9d41d chore: Sync translations (#4946) 2025-05-13 02:36:53 +04:00
semantic-release-bot
7d3b8d9c42 chore: Release v5.23.0 [skip ci]
# [5.23.0](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0) (2025-05-10)

### Bug Fixes

* Correct incorrect fingerprint ([5f05414](5f0541407c))
* Fix incorrect fingerprints ([#4917](https://github.com/ReVanced/revanced-patches/issues/4917)) ([796c118](796c118fe1))
* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([5028c1a](5028c1acb3))
* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([60fdf4c](60fdf4c44c))
* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([23fd720](23fd720fa7))

### Features

* **Lightroom:** Constrain patches to last working version ([858c59d](858c59d728))
* **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))
* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([777957e](777957e2d0))
2025-05-10 09:02:41 +00:00
LisoUseInAIKyrios
25e1a965d6 chore: Merge branch dev to main (#4899) 2025-05-10 12:58:55 +04:00
github-actions[bot]
b29c01cee1 chore: Sync translations (#4933) 2025-05-10 12:39:30 +04:00
semantic-release-bot
639850471b chore: Release v5.23.0-dev.7 [skip ci]
# [5.23.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.6...v5.23.0-dev.7) (2025-05-06)

### Bug Fixes

* Fix incorrect fingerprints ([#4917](https://github.com/ReVanced/revanced-patches/issues/4917)) ([796c118](796c118fe1))
2025-05-06 12:13:29 +00:00
Nuckyz
796c118fe1 fix: Fix incorrect fingerprints (#4917) 2025-05-06 16:09:54 +04:00
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
semantic-release-bot
e2a7e25c66 chore: Release v5.21.0-dev.6 [skip ci]
# [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)

### Bug Fixes

* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([77ea5c4](77ea5c4033))
2025-04-19 16:36:29 +00:00
LisoUseInAIKyrios
77ea5c4033 fix(YouTube - Wide search bar): Do not force phone layout for tablet devices (#4827) 2025-04-19 18:33:12 +02:00
github-actions[bot]
6eea2354f5 chore: Sync translations (#4828) 2025-04-19 12:00:28 +02:00
172 changed files with 4319 additions and 1714 deletions

View File

@@ -1,3 +1,207 @@
# [5.24.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.23.0...v5.24.0-dev.1) (2025-05-12)
### Features
* **NU.nl:** Support version `11.3.0` ([#4925](https://github.com/ReVanced/revanced-patches/issues/4925)) ([bedde60](https://github.com/ReVanced/revanced-patches/commit/bedde60fc1a52b0fd491174b3b5b887435eb621a))
# [5.23.0](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0) (2025-05-10)
### Bug Fixes
* Correct incorrect fingerprint ([c3bab89](https://github.com/ReVanced/revanced-patches/commit/c3bab89fc4189e38c10eee0caa36289de7e29dfa))
* Fix incorrect fingerprints ([#4917](https://github.com/ReVanced/revanced-patches/issues/4917)) ([49ca329](https://github.com/ReVanced/revanced-patches/commit/49ca3290a726cdba7bc9b62ffcd8d46e6f04778e))
* **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))
* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([7b43986](https://github.com/ReVanced/revanced-patches/commit/7b43986871a68e5cb43331d2fb2fdb9ef67438ad))
* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([bd53955](https://github.com/ReVanced/revanced-patches/commit/bd53955df738bb7b819eb91a3e776e9d2ca5c74a))
### Features
* **Lightroom:** Constrain patches to last working version ([efef03b](https://github.com/ReVanced/revanced-patches/commit/efef03b80da21552d0d8be6913faba64e4fb5ed1))
* **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))
* **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.7](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.6...v5.23.0-dev.7) (2025-05-06)
### Bug Fixes
* Fix incorrect fingerprints ([#4917](https://github.com/ReVanced/revanced-patches/issues/4917)) ([49ca329](https://github.com/ReVanced/revanced-patches/commit/49ca3290a726cdba7bc9b62ffcd8d46e6f04778e))
# [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)
### Bug Fixes
* **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))
# [5.21.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.4...v5.21.0-dev.5) (2025-04-18) # [5.21.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.4...v5.21.0-dev.5) (2025-04-18)

View File

@@ -82,7 +82,7 @@ public class HideAdsPatch {
// Filter HeaderBlock with known ads until next HeaderBlock. // Filter HeaderBlock with known ads until next HeaderBlock.
if (currentBlock instanceof HeaderBlock headerBlock) { if (currentBlock instanceof HeaderBlock headerBlock) {
StyledText headerText = headerBlock.component20(); StyledText headerText = headerBlock.getTitle();
if (headerText != null) { if (headerText != null) {
skipFullHeader = false; skipFullHeader = false;
for (String blockedHeaderBlock : blockedHeaderBlocks) { for (String blockedHeaderBlock : blockedHeaderBlocks) {

View File

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

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.MalformedURLException;
import java.net.URL; import java.net.URL;
/** @SuppressWarnings("unused")
* @noinspection unused
*/
public class GmsCoreSupport { public class GmsCoreSupport {
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube"; 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"; 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}. * Identical to calling {@link #save(Object)} using {@link #defaultValue}.
*
* @return The newly saved default value.
*/ */
public void resetToDefault() { public T resetToDefault() {
save(defaultValue); 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.Aweme;
import com.ss.android.ugc.aweme.feed.model.FeedItemList; 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.Iterator;
import java.util.List; import java.util.List;
@@ -13,22 +14,41 @@ public final class FeedItemsFilter {
new StoryFilter(), new StoryFilter(),
new ImageVideoFilter(), new ImageVideoFilter(),
new ViewCountFilter(), new ViewCountFilter(),
new LikeCountFilter() new LikeCountFilter(),
new ShopFilter()
); );
public static void filter(FeedItemList feedItemList) { public static void filter(FeedItemList feedItemList) {
Iterator<Aweme> feedItemListIterator = feedItemList.items.iterator(); filterFeedList(feedItemList.items, item -> item);
while (feedItemListIterator.hasNext()) { }
Aweme item = feedItemListIterator.next();
if (item == null) continue;
for (IFilter filter : FILTERS) { public static void filter(FollowFeedList followFeedList) {
boolean enabled = filter.getEnabled(); filterFeedList(followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null);
if (enabled && filter.getFiltered(item)) { }
feedItemListIterator.remove();
break; 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 class Settings extends BaseSettings {
public static final BooleanSetting REMOVE_ADS = new BooleanSetting("remove_ads", TRUE, true); 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_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_STORY = new BooleanSetting("hide_story", FALSE, true);
public static final BooleanSetting HIDE_IMAGE = new BooleanSetting("hide_image", 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); 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.", "Remove feed ads", "Remove ads from feed.",
Settings.REMOVE_ADS Settings.REMOVE_ADS
)); ));
addPreference(new TogglePreference(
context,
"Hide TikTok Shop", "Hide TikTok shop from feed.",
Settings.HIDE_SHOP
));
addPreference(new TogglePreference( addPreference(new TogglePreference(
context, context,
"Hide livestreams", "Hide livestreams from feed.", "Hide livestreams", "Hide livestreams from feed.",

View File

@@ -33,4 +33,8 @@ public class Aweme {
public AwemeStatistics getStatistics() { public AwemeStatistics getStatistics() {
throw new UnsupportedOperationException("Stub"); 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.app.Activity;
import android.graphics.Color; import android.graphics.Color;
import android.os.Build;
import android.view.Window;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -102,4 +104,21 @@ public class ThemeHelper {
return Utils.getColorFromString(colorName); 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 androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @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. * Intent action when YouTube is cold started from the launcher.
* <p> * <p>
@@ -93,6 +101,8 @@ public final class ChangeStartPagePatch {
private static final StartPage START_PAGE = Settings.CHANGE_START_PAGE.get(); 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. * 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. * 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; return original;
} }
if (appLaunched) { if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
Logger.printDebug(() -> "Ignore override browseId as the app already launched"); Logger.printDebug(() -> "Ignore override browseId as the app already launched");
return original; return original;
} }
appLaunched = true; appLaunched = true;
Logger.printDebug(() -> "Changing browseId to " + START_PAGE.id); Logger.printDebug(() -> "Changing browseId to: " + START_PAGE.id);
return START_PAGE.id; return START_PAGE.id;
} }
@@ -125,14 +135,14 @@ public final class ChangeStartPagePatch {
return; return;
} }
if (appLaunched) { if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
Logger.printDebug(() -> "Ignore override intent action as the app already launched"); Logger.printDebug(() -> "Ignore override intent action as the app already launched");
return; return;
} }
appLaunched = true; appLaunched = true;
String intentAction = START_PAGE.id; String intentAction = START_PAGE.id;
Logger.printDebug(() -> "Changing intent action to " + intentAction); Logger.printDebug(() -> "Changing intent action to: " + intentAction);
intent.setAction(intentAction); intent.setAction(intentAction);
} }
} }

View File

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

View File

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

View File

@@ -2,6 +2,8 @@ package app.revanced.extension.youtube.patches;
import android.app.Activity; import android.app.Activity;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Objects; import java.util.Objects;
@@ -76,7 +78,7 @@ public class ShortsAutoplayPatch {
/** /**
* Injection point. * Injection point.
*/ */
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) { public static Enum<?> changeShortsRepeatBehavior(@Nullable Enum<?> original) {
try { try {
final boolean autoplay; final boolean autoplay;
@@ -98,17 +100,35 @@ public class ShortsAutoplayPatch {
: ShortsLoopBehavior.REPEAT; : ShortsLoopBehavior.REPEAT;
if (behavior.ytEnumValue != null) { if (behavior.ytEnumValue != null) {
Logger.printDebug(() -> behavior.ytEnumValue == original Logger.printDebug(() -> {
? "Changing Shorts repeat behavior from: " + original.name() + " to: " + behavior.ytEnumValue String name = (original == null ? "unknown (null)" : original.name());
: "Behavior setting is same as original. Using original: " + 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; 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) { } catch (Exception ex) {
Logger.printException(() -> "changeShortsRepeatState failure", ex); Logger.printException(() -> "changeShortsRepeatBehavior failure", ex);
} }
return original; return original;
} }
/**
* Injection point.
*/
public static boolean isAutoPlay(Enum<?> original) {
return ShortsLoopBehavior.SINGLE_PLAY.ytEnumValue == original;
}
} }

View File

@@ -1,11 +1,48 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import android.content.res.Resources;
import android.util.TypedValue;
import android.view.View;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class WideSearchbarPatch { public final class WideSearchbarPatch {
private static final Boolean WIDE_SEARCHBAR_ENABLED = Settings.WIDE_SEARCHBAR.get();
/**
* Injection point.
*/
public static boolean enableWideSearchbar(boolean original) { public static boolean enableWideSearchbar(boolean original) {
return Settings.WIDE_SEARCHBAR.get() || original; return WIDE_SEARCHBAR_ENABLED || original;
}
/**
* Injection point.
*/
public static void setActionBar(View view) {
try {
if (!WIDE_SEARCHBAR_ENABLED) return;
View searchBarView = Utils.getChildViewByResourceName(view, "search_bar");
final int paddingLeft = searchBarView.getPaddingLeft();
final int paddingRight = searchBarView.getPaddingRight();
final int paddingTop = searchBarView.getPaddingTop();
final int paddingBottom = searchBarView.getPaddingBottom();
final int paddingStart = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
8, Resources.getSystem().getDisplayMetrics());
if (Utils.isRightToLeftTextLayout()) {
searchBarView.setPadding(paddingLeft, paddingTop, paddingStart, paddingBottom);
} else {
searchBarView.setPadding(paddingStart, paddingTop, paddingRight, paddingBottom);
}
} catch (Exception ex) {
Logger.printException(() -> "setActionBar failure", ex);
}
} }
} }

View File

@@ -31,7 +31,7 @@ final class ButtonsFilter extends Filter {
bufferFilterPathGroup = new StringFilterGroup( bufferFilterPathGroup = new StringFilterGroup(
null, null,
"|ContainerType|button.eml|" "|ContainerType|button.eml"
); );
addPathCallbacks( addPathCallbacks(
@@ -43,7 +43,7 @@ final class ButtonsFilter extends Filter {
), ),
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_DOWNLOAD_BUTTON, Settings.HIDE_DOWNLOAD_BUTTON,
"|download_button.eml|" "|download_button.eml"
), ),
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_PLAYLIST_BUTTON, Settings.HIDE_PLAYLIST_BUTTON,
@@ -51,7 +51,7 @@ final class ButtonsFilter extends Filter {
), ),
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_CLIP_BUTTON, Settings.HIDE_CLIP_BUTTON,
"|clip_button.eml|" "|clip_button.eml"
) )
); );
@@ -68,15 +68,19 @@ final class ButtonsFilter extends Filter {
Settings.HIDE_REMIX_BUTTON, Settings.HIDE_REMIX_BUTTON,
"yt_outline_youtube_shorts_plus" "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, // 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' // as there's a chance the path is a generic action button and won't contain 'clip_button'
new ByteArrayFilterGroup( new ByteArrayFilterGroup(
Settings.HIDE_CLIP_BUTTON, Settings.HIDE_CLIP_BUTTON,
"yt_outline_scissors" "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", "post_base_wrapper_slim.eml",
"poll_post_root.eml", "poll_post_root.eml",
"videos_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( final var communityGuidelines = new StringFilterGroup(
@@ -211,7 +214,7 @@ public final class LayoutComponentsFilter extends Filter {
compactChannelBarInnerButton = new StringFilterGroup( compactChannelBarInnerButton = new StringFilterGroup(
null, null,
"|button.eml|" "|button.eml"
); );
joinMembershipButton = new ByteArrayFilterGroup( 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. * 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<>(); private static final ThreadLocal<ByteBuffer> bufferThreadLocal = new ThreadLocal<>();
/**
* Results of calling {@link #filter(String, StringBuilder)}.
*/
private static final ThreadLocal<Boolean> filterResult = new ThreadLocal<>();
static { static {
for (Filter filter : filters) { 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. * Injection point. Called off the main thread, and commonly called by multiple threads at the same time.
*/ */
@SuppressWarnings("unused") public static void filter(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
public static boolean filter(@Nullable String lithoIdentifier, @NonNull StringBuilder pathBuilder) { filterResult.set(handleFiltering(lithoIdentifier, pathBuilder));
}
private static boolean handleFiltering(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
try { try {
if (pathBuilder.length() == 0) { if (pathBuilder.length() == 0) {
return false; return false;

View File

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

View File

@@ -1,6 +1,5 @@
package app.revanced.extension.youtube.patches.components; 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 static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.view.View; import android.view.View;
@@ -52,6 +51,7 @@ public final class ShortsFilter extends Filter {
private final StringFilterGroup suggestedAction; private final StringFilterGroup suggestedAction;
private final ByteArrayFilterGroupList suggestedActionsGroupList = new ByteArrayFilterGroupList(); private final ByteArrayFilterGroupList suggestedActionsGroupList = new ByteArrayFilterGroupList();
private final StringFilterGroup shortsActionBar;
private final StringFilterGroup actionButton; private final StringFilterGroup actionButton;
private final ByteArrayFilterGroupList videoActionButtonGroupList = new ByteArrayFilterGroupList(); private final ByteArrayFilterGroupList videoActionButtonGroupList = new ByteArrayFilterGroupList();
@@ -141,6 +141,16 @@ public final class ShortsFilter extends Filter {
"like_fountain.eml" "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( joinButton = new StringFilterGroup(
Settings.HIDE_SHORTS_JOIN_BUTTON, Settings.HIDE_SHORTS_JOIN_BUTTON,
"sponsor_button" "sponsor_button"
@@ -156,9 +166,15 @@ public final class ShortsFilter extends Filter {
"reel_player_disclosure.eml" "reel_player_disclosure.eml"
); );
shortsActionBar = new StringFilterGroup(
null,
"shorts_action_bar.eml"
);
actionButton = new StringFilterGroup( actionButton = new StringFilterGroup(
null, null,
"shorts_video_action_button.eml" // Can be simply 'button.eml' or 'shorts_video_action_button.eml'
"button.eml"
); );
suggestedAction = new StringFilterGroup( suggestedAction = new StringFilterGroup(
@@ -167,27 +183,16 @@ public final class ShortsFilter extends Filter {
); );
addPathCallbacks( addPathCallbacks(
shortsCompactFeedVideoPath, suggestedAction, actionButton, joinButton, subscribeButton, shortsCompactFeedVideoPath, joinButton, subscribeButton, paidPromotionButton,
paidPromotionButton, pausedOverlayButtons, channelBar, fullVideoLinkLabel, videoTitle, shortsActionBar, suggestedAction, pausedOverlayButtons, channelBar,
reelSoundMetadata, soundButton, infoPanel, stickers, likeFountain fullVideoLinkLabel, videoTitle, reelSoundMetadata, soundButton, infoPanel,
stickers, likeFountain, likeButton, dislikeButton
); );
// //
// Action buttons // All other action buttons.
// //
videoActionButtonGroupList.addAll( 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( new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_COMMENTS_BUTTON, Settings.HIDE_SHORTS_COMMENTS_BUTTON,
"reel_comment_button" "reel_comment_button"
@@ -286,9 +291,11 @@ public final class ShortsFilter extends Filter {
return false; return false;
} }
// Video action buttons (like, dislike, comment, share, remix) have the same path. // Video action buttons (comment, share, remix) have the same path.
if (matchedGroup == actionButton) { // Like and dislike are separate path filters and don't require buffer searching.
if (videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) { if (matchedGroup == shortsActionBar) {
if (actionButton.check(path).isFiltered()
&& videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex); return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
} }
return false; return false;
@@ -392,37 +399,6 @@ public final class ShortsFilter extends Filter {
return original; 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) { public static void setNavigationBar(PivotBar view) {
pivotBarRef = new WeakReference<>(view); pivotBarRef = new WeakReference<>(view);
} }

View File

@@ -54,12 +54,12 @@ public class CustomPlaybackSpeedPatch {
static { static {
final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get(); final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get();
if (holdSpeed > 0 && holdSpeed <= PLAYBACK_SPEED_MAXIMUM) { if (holdSpeed > 0 && holdSpeed <= PLAYBACK_SPEED_MAXIMUM) {
TAP_AND_HOLD_SPEED = holdSpeed; TAP_AND_HOLD_SPEED = holdSpeed;
} else { } else {
showInvalidCustomSpeedToast(); showInvalidCustomSpeedToast();
Settings.SPEED_TAP_AND_HOLD.resetToDefault(); TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.resetToDefault();
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.get();
} }
loadCustomSpeeds(); loadCustomSpeeds();

View File

@@ -84,6 +84,7 @@ public class LicenseActivityHook {
public static void initialize(Activity licenseActivity) { public static void initialize(Activity licenseActivity) {
try { try {
ThemeHelper.setActivityTheme(licenseActivity); ThemeHelper.setActivityTheme(licenseActivity);
ThemeHelper.setNavigationBarColor(licenseActivity.getWindow());
licenseActivity.setContentView(getResourceIdentifier( licenseActivity.setContentView(getResourceIdentifier(
"revanced_settings_with_toolbar", "layout")); "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+ // This is required to fix submenu title alignment issue with Android ASOP 15+
ViewGroup toolBarParent = activity.findViewById( ViewGroup toolBarParent = activity.findViewById(
getResourceIdentifier("revanced_toolbar_parent", "id")); getResourceIdentifier("revanced_toolbar_parent", "id"));
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar"); ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent, "revanced_toolbar");
toolbarLayoutParams = dummyToolbar.getLayoutParams(); toolbarLayoutParams = dummyToolbar.getLayoutParams();
toolBarParent.removeView(dummyToolbar); toolBarParent.removeView(dummyToolbar);
@@ -149,5 +150,4 @@ public class LicenseActivityHook {
toolBarParent.addView(toolbar, 0); 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.parent;
import static app.revanced.extension.shared.settings.Setting.parentsAny; 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.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.ChangeStartPagePatch.StartPage;
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode; import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability; import static app.revanced.extension.youtube.patches.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.MANUAL_SKIP;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
import android.graphics.Color; 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_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_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_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 // 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_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); 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 SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true); public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true); public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
public static final 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)); public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
// Custom filter // Custom filter
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE); public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
@@ -320,12 +325,14 @@ public class Settings extends BaseSettings {
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true, public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(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)); 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)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true, public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final 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, public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS)); 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) .findViewById(android.R.id.content)
.getParent(); .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+ // 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 // https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {

View File

@@ -1,95 +1,98 @@
package app.revanced.extension.youtube.swipecontrols package app.revanced.extension.youtube.swipecontrols
import android.annotation.SuppressLint
import android.graphics.Color import android.graphics.Color
import app.revanced.extension.shared.Logger
import app.revanced.extension.shared.StringRef.str import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.settings.Settings import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerType import app.revanced.extension.youtube.shared.PlayerType
/** /**
* 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 { 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 val enableSwipeControls: Boolean
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo 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() 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() 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 private val isFullscreenVideo: Boolean
get() = PlayerType.current == PlayerType.WATCH_WHILE_FULLSCREEN 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 val overwriteVolumeKeyControls: Boolean
get() = enableVolumeControls && isFullscreenVideo 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 val shouldEnablePressToSwipe: Boolean
get() = Settings.SWIPE_PRESS_TO_ENGAGE.get() get() = Settings.SWIPE_PRESS_TO_ENGAGE.get()
/** /**
* threshold for swipe detection * The threshold for detecting swipe gestures, in pixels.
* this may be called rapidly in onScroll, so we have to load it once and then leave it constant * Loaded once to ensure consistent behavior during rapid scroll events.
*/ */
val swipeMagnitudeThreshold: Int val swipeMagnitudeThreshold: Int
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get() get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
/** /**
* How much volume will change by single swipe. * The sensitivity of volume swipe gestures, determining how much volume changes per swipe.
* If it is set to 0, it will reset to the default value because 0 would disable swiping. * Resets to default if set to 0, as it would disable swiping.
* */ */
val volumeSwipeSensitivity: Int val volumeSwipeSensitivity: Int
get() { get() {
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get() val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
if (sensitivity < 1) { if (sensitivity < 1) {
Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault() return Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
return Settings.SWIPE_VOLUME_SENSITIVITY.get()
} }
return sensitivity 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 val shouldEnableHapticFeedback: Boolean
get() = Settings.SWIPE_HAPTIC_FEEDBACK.get() 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 val overlayShowTimeoutMillis: Long
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get() get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
/** /**
* Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency. * The background opacity of the overlay, converted from a percentage (0-100) to an alpha value (0-255).
* If the opacity value is out of range, it resets to the default and displays a warning message. * Resets to default and shows a toast if the value is out of range.
*/ */
val overlayBackgroundOpacity: Int val overlayBackgroundOpacity: Int
get() { get() {
@@ -97,8 +100,7 @@ class SwipeControlsConfigurationProvider {
if (opacity < 0 || opacity > 100) { if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast")) Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
Settings.SWIPE_OVERLAY_OPACITY.resetToDefault() opacity = Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
} }
opacity = opacity * 255 / 100 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 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 val overlayFillBackgroundPaint: Int
get() = 0x80D3D3D3.toInt() 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 val overlayTextColor: Int
get() = Color.WHITE 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 val overlayTextSize: Int
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get() 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 @Suppress("unused")
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get() enum class SwipeOverlayStyle(
//endregion 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 val shouldSaveAndRestoreBrightness: Boolean
get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get() 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 val shouldLowestValueEnableAutoBrightness: Boolean
get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get() 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 var savedScreenBrightnessValue: Float
get() = Settings.SWIPE_BRIGHTNESS_VALUE.get() get() = Settings.SWIPE_BRIGHTNESS_VALUE.get()
set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value) 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. * The main controller for volume and brightness swipe controls.
* note that the superclass is overwritten to the superclass of the MainActivity at patch time * note that the superclass is overwritten to the superclass of the MainActivity at patch time.
*
* @smali Lapp/revanced/extension/swipecontrols/SwipeControlsHostActivity;
*/ */
class SwipeControlsHostActivity : Activity() { class SwipeControlsHostActivity : Activity() {
/** /**

View File

@@ -1,8 +1,11 @@
package app.revanced.extension.youtube.swipecontrols.views package app.revanced.extension.youtube.swipecontrols.views
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Paint import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF import android.graphics.RectF
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Handler import android.os.Handler
@@ -11,14 +14,23 @@ import android.util.AttributeSet
import android.view.HapticFeedbackConstants import android.view.HapticFeedbackConstants
import android.view.View import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
import kotlin.math.min import kotlin.math.min
import kotlin.math.max
import kotlin.math.round 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( class SwipeControlsOverlayLayout(
context: Context, context: Context,
@@ -51,18 +63,21 @@ class SwipeControlsOverlayLayout(
// Initialize progress bars // Initialize progress bars
private val circularProgressView: CircularProgressView private val circularProgressView: CircularProgressView
private val horizontalProgressView: HorizontalProgressView private val horizontalProgressView: HorizontalProgressView
private val verticalBrightnessProgressView: VerticalProgressView
private val verticalVolumeProgressView: VerticalProgressView
init { init {
// Initialize circular progress bar // Initialize circular progress bar
circularProgressView = CircularProgressView( circularProgressView = CircularProgressView(
context, context,
config.overlayBackgroundOpacity, config.overlayBackgroundOpacity,
config.overlayShowOverlayMinimalStyle, config.overlayStyle.isMinimal,
config.overlayProgressColor, config.overlayProgressColor,
config.overlayFillBackgroundPaint, config.overlayFillBackgroundPaint,
config.overlayTextColor config.overlayTextColor,
config.overlayTextSize
).apply { ).apply {
layoutParams = LayoutParams(300, 300).apply { layoutParams = LayoutParams(100f.toDisplayPixels().toInt(), 100f.toDisplayPixels().toInt()).apply {
addRule(CENTER_IN_PARENT, TRUE) addRule(CENTER_IN_PARENT, TRUE)
} }
visibility = GONE // Initially hidden visibility = GONE // Initially hidden
@@ -71,22 +86,65 @@ class SwipeControlsOverlayLayout(
// Initialize horizontal progress bar // Initialize horizontal progress bar
val screenWidth = resources.displayMetrics.widthPixels 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( horizontalProgressView = HorizontalProgressView(
context, context,
config.overlayBackgroundOpacity, config.overlayBackgroundOpacity,
config.overlayShowOverlayMinimalStyle, config.overlayStyle.isMinimal,
config.overlayProgressColor, config.overlayProgressColor,
config.overlayFillBackgroundPaint, config.overlayFillBackgroundPaint,
config.overlayTextColor config.overlayTextColor,
config.overlayTextSize
).apply { ).apply {
layoutParams = LayoutParams(layoutWidth, 100).apply { layoutParams = LayoutParams(layoutWidth, 32f.toDisplayPixels().toInt()).apply {
addRule(CENTER_HORIZONTAL) addRule(CENTER_HORIZONTAL)
topMargin = 40 // Top margin if (config.overlayStyle.isHorizontalMinimalCenter) {
addRule(CENTER_VERTICAL)
} else {
topMargin = 20f.toDisplayPixels().toInt()
}
} }
visibility = GONE // Initially hidden visibility = GONE // Initially hidden
} }
addView(horizontalProgressView) 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 // Handler and callback for hiding progress bars
@@ -94,6 +152,8 @@ class SwipeControlsOverlayLayout(
private val feedbackHideCallback = Runnable { private val feedbackHideCallback = Runnable {
circularProgressView.visibility = GONE circularProgressView.visibility = GONE
horizontalProgressView.visibility = GONE horizontalProgressView.visibility = GONE
verticalBrightnessProgressView.visibility = GONE
verticalVolumeProgressView.visibility = GONE
} }
/** /**
@@ -103,7 +163,11 @@ class SwipeControlsOverlayLayout(
feedbackHideHandler.removeCallbacks(feedbackHideCallback) feedbackHideHandler.removeCallbacks(feedbackHideCallback)
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis) 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 { viewToShow.apply {
setProgress(progress, max, value, isBrightness) setProgress(progress, max, value, isBrightness)
this.icon = icon this.icon = icon
@@ -126,7 +190,9 @@ class SwipeControlsOverlayLayout(
// Handle brightness change // Handle brightness change
override fun onBrightnessChanged(brightness: Double) { override fun onBrightnessChanged(brightness: Double) {
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) { 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 { } else {
val brightnessValue = round(brightness).toInt() val brightnessValue = round(brightness).toInt()
val icon = when { val icon = when {
@@ -135,7 +201,8 @@ class SwipeControlsOverlayLayout(
brightnessValue < 75 -> highBrightnessIcon brightnessValue < 75 -> highBrightnessIcon
else -> fullBrightnessIcon 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( abstract class AbstractProgressView(
context: Context, context: Context,
protected val overlayBackgroundOpacity: Int, overlayBackgroundOpacity: Int,
protected val overlayShowOverlayMinimalStyle: Boolean, protected val isMinimalStyle: Boolean,
protected val overlayProgressColor: Int, overlayProgressColor: Int,
protected val overlayFillBackgroundPaint: Int, overlayFillBackgroundPaint: Int,
protected val overlayTextColor: Int, private val overlayTextColor: Int,
protected val overlayTextSize: Int,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = 0 defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) { ) : View(context, attrs, defStyleAttr) {
@@ -174,26 +242,25 @@ abstract class AbstractProgressView(
} }
// Initialize paints // Initialize paints
public val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL) val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
public val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f) val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 6f.toDisplayPixels())
public val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL) val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = overlayTextColor color = overlayTextColor
textAlign = Paint.Align.CENTER 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 progress = 0
protected var maxProgress = 100 protected var maxProgress = 100
protected var displayText: String = "0" protected var displayText: String = "0"
protected var isBrightness = true protected var isBrightness = true
public var icon: Drawable? = null var icon: Drawable? = null
init { open fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
// Stroke widths are now set in createPaint for progressPaint and fillBackgroundPaint
}
fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
progress = value progress = value
maxProgress = max maxProgress = max
displayText = text displayText = text
@@ -201,6 +268,11 @@ abstract class AbstractProgressView(
invalidate() invalidate()
} }
protected fun measureTextWidth(text: String, paint: Paint): Int {
paint.getTextBounds(text, 0, text.length, textBounds)
return textBounds.width()
}
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
// Base class implementation can be empty // 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. * Custom view for rendering a circular progress indicator with icons and text.
*/ */
@SuppressLint("ViewConstructor")
class CircularProgressView( class CircularProgressView(
context: Context, context: Context,
overlayBackgroundOpacity: Int, overlayBackgroundOpacity: Int,
overlayShowOverlayMinimalStyle: Boolean, isMinimalStyle: Boolean,
overlayProgressColor: Int, overlayProgressColor: Int,
overlayFillBackgroundPaint: Int, overlayFillBackgroundPaint: Int,
overlayTextColor: Int, overlayTextColor: Int,
overlayTextSize: Int,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = 0 defStyleAttr: Int = 0
) : AbstractProgressView( ) : AbstractProgressView(
context, context,
overlayBackgroundOpacity, overlayBackgroundOpacity,
overlayShowOverlayMinimalStyle, isMinimalStyle,
overlayProgressColor, overlayProgressColor,
overlayFillBackgroundPaint, overlayFillBackgroundPaint,
overlayTextColor, overlayTextColor,
overlayTextSize,
attrs, attrs,
defStyleAttr defStyleAttr
) { ) {
private val rectF = RectF() private val rectF = RectF()
init { init {
textPaint.textSize = 40f // Override default text size for circular view progressPaint.strokeWidth = 6f.toDisplayPixels()
progressPaint.strokeWidth = 20f fillBackgroundPaint.strokeWidth = 6f.toDisplayPixels()
fillBackgroundPaint.strokeWidth = 20f progressPaint.strokeCap = Paint.Cap.ROUND
progressPaint.strokeCap = Paint.Cap.ROUND
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.STROKE progressPaint.style = Paint.Style.STROKE
fillBackgroundPaint.style = Paint.Style.STROKE fillBackgroundPaint.style = Paint.Style.STROKE
} }
@@ -244,7 +318,8 @@ class CircularProgressView(
super.onDraw(canvas) super.onDraw(canvas)
val size = min(width, height).toFloat() 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.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle. 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. // Draw the icon in the center.
icon?.let { icon?.let {
val iconSize = if (overlayShowOverlayMinimalStyle) 100 else 80 val iconSize = (if (isMinimalStyle) 36f else 24f).toDisplayPixels().toInt()
val iconX = (width - iconSize) / 2 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.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
it.draw(canvas) it.draw(canvas)
} }
// If not a minimal style mode, draw the text inside the ring. // If not a minimal style mode, draw the text inside the ring.
if (!overlayShowOverlayMinimalStyle) { if (!isMinimalStyle) {
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint) 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. * Custom view for rendering a rectangular progress bar with icons and text.
*/ */
@SuppressLint("ViewConstructor")
class HorizontalProgressView( class HorizontalProgressView(
context: Context, context: Context,
overlayBackgroundOpacity: Int, overlayBackgroundOpacity: Int,
overlayShowOverlayMinimalStyle: Boolean, isMinimalStyle: Boolean,
overlayProgressColor: Int, overlayProgressColor: Int,
overlayFillBackgroundPaint: Int, overlayFillBackgroundPaint: Int,
overlayTextColor: Int, overlayTextColor: Int,
overlayTextSize: Int,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = 0 defStyleAttr: Int = 0
) : AbstractProgressView( ) : AbstractProgressView(
context, context,
overlayBackgroundOpacity, overlayBackgroundOpacity,
overlayShowOverlayMinimalStyle, isMinimalStyle,
overlayProgressColor, overlayProgressColor,
overlayFillBackgroundPaint, overlayFillBackgroundPaint,
overlayTextColor, overlayTextColor,
overlayTextSize,
attrs, attrs,
defStyleAttr defStyleAttr
) { ) {
private val iconSize = 60f private val iconSize = 20f.toDisplayPixels()
private val padding = 40f private val padding = 12f.toDisplayPixels()
private var textWidth = 0f
private val progressBarHeight = 3f.toDisplayPixels()
private val progressBarWidth: Float = resources.displayMetrics.widthPixels / 4f
init { init {
textPaint.textSize = 36f // Override default text size for horizontal view
progressPaint.strokeWidth = 0f progressPaint.strokeWidth = 0f
progressPaint.strokeCap = Paint.Cap.BUTT progressPaint.strokeCap = Paint.Cap.BUTT
progressPaint.style = Paint.Style.FILL progressPaint.style = Paint.Style.FILL
fillBackgroundPaint.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) { override fun onDraw(canvas: Canvas) {
super.onDraw(canvas) super.onDraw(canvas)
val width = width.toFloat() val viewWidth = width.toFloat()
val height = height.toFloat() val viewHeight = height.toFloat()
val viewHeightHalf = viewHeight / 2
// Radius for rounded corners textWidth = measureTextWidth(displayText, textPaint).toFloat()
val cornerRadius = min(width, height) / 2
// Calculate the total width for the elements val cornerRadius = viewHeightHalf
val minimalElementWidth = 5 * padding + iconSize
// Calculate the starting point (X) to center the elements val startX = padding
val minimalStartX = (width - minimalElementWidth) / 2 val iconEndX = startX + iconSize
// Draw the background val textStartX = (viewWidth - 1.5f * padding - textWidth)
if (!overlayShowOverlayMinimalStyle) {
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
} else {
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
}
if (!overlayShowOverlayMinimalStyle) { canvas.drawRoundRect(
// Draw the fill background 0f, 0f, viewWidth, viewHeight,
val startX = 2 * padding + iconSize cornerRadius, cornerRadius, backgroundPaint
val endX = width - 4 * padding )
val fillWidth = endX - startX
canvas.drawRoundRect(
startX,
height / 2 - 5f,
endX,
height / 2 + 5f,
10f, 10f,
fillBackgroundPaint
)
// Draw the progress
val progressWidth = (progress.toFloat() / maxProgress) * fillWidth
canvas.drawRoundRect(
startX,
height / 2 - 5f,
startX + progressWidth,
height / 2 + 5f,
10f, 10f,
progressPaint
)
}
// Draw the icon
icon?.let { icon?.let {
val iconX = if (!overlayShowOverlayMinimalStyle) { val iconY = viewHeightHalf - iconSize / 2
padding it.setBounds(
} else { startX.toInt(),
padding + minimalStartX iconY.toInt(),
} (startX + iconSize).toInt(),
val iconY = height / 2 - iconSize / 2 (iconY + iconSize).toInt()
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt()) )
it.draw(canvas) it.draw(canvas)
} }
// Draw the text on the right val textY = viewHeightHalf + textPaint.textSize / 3
val textX = if (!overlayShowOverlayMinimalStyle) { textPaint.textAlign = Paint.Align.LEFT
width - 2 * padding
} else {
minimalStartX + minimalElementWidth - 2 * padding
}
val textY = height / 2 + textPaint.textSize / 3
// Draw the text if (isMinimalStyle) {
canvas.drawText(displayText, textX, textY, textPaint) 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 org.gradle.parallel = true
android.useAndroidX = true android.useAndroidX = true
kotlin.code.style = official kotlin.code.style = official
version = 5.21.0-dev.5 version = 5.24.0-dev.1

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 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 final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatchKt {
public static final fun getGetDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; 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 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 final class app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatchKt {
public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/ResourcePatch; 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 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 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 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 getIcon ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String; public final fun getKey ()Ljava/lang/String;
public final fun getLayout ()Ljava/lang/String; public final fun getLayout ()Ljava/lang/String;
public final fun getSummaryKey ()Ljava/lang/String; public final fun getSummaryKey ()Ljava/lang/String;
public final fun getTag ()Ljava/lang/String; public final fun getTag ()Ljava/lang/String;
public final fun getTitleKey ()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; 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 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 final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt {
public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch; 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 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;)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;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;F)Z
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)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( val disableMandatoryLoginPatch = bytecodePatch(
name = "Disable mandatory login", name = "Disable mandatory login",
) { ) {
compatibleWith("com.adobe.lrmobile") compatibleWith("com.adobe.lrmobile"("10.0.2"))
execute { execute {
isLoggedInFingerprint.method.apply { isLoggedInFingerprint.method.apply {

View File

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

View File

@@ -4,10 +4,10 @@ import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
internal val jwUtilCreateAdvertisementFingerprint = fingerprint { internal val jwPlayerConfigFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC) accessFlags(AccessFlags.PUBLIC)
custom { methodDef, classDef -> custom { methodDef, classDef ->
classDef.type == "Lnl/sanomamedia/android/nu/video/util/JWUtil;" && methodDef.name == "createAdvertising" classDef.type == "Lcom/jwplayer/pub/api/configuration/PlayerConfig${'$'}Builder;" && methodDef.name == "advertisingConfig"
} }
} }

View File

@@ -2,8 +2,11 @@ package app.revanced.patches.nunl.ads
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused") @Suppress("unused")
@@ -11,23 +14,15 @@ val hideAdsPatch = bytecodePatch(
name = "Hide ads", name = "Hide ads",
description = "Hide ads and sponsored articles in list pages and remove pre-roll ads on videos.", description = "Hide ads and sponsored articles in list pages and remove pre-roll ads on videos.",
) { ) {
compatibleWith("nl.sanomamedia.android.nu"("11.0.0", "11.0.1", "11.1.0")) compatibleWith("nl.sanomamedia.android.nu"("11.3.0"))
dependsOn(sharedExtensionPatch("nunl", mainActivityOnCreateHook)) dependsOn(sharedExtensionPatch("nunl", mainActivityOnCreateHook))
execute { execute {
// Disable video pre-roll ads. // Disable video pre-roll ads.
// Whenever the app tries to create an ad via JWUtils.createAdvertising, don't actually tell the underlying JWPlayer library to do so => JWPlayer will not display ads. // Whenever the app tries to define the advertising config for JWPlayer, don't set the advertising config and directly return.
jwUtilCreateAdvertisementFingerprint.method.addInstructions( val iputInstructionIndex = jwPlayerConfigFingerprint.method.indexOfFirstInstructionOrThrow(Opcode.IPUT_OBJECT)
0, jwPlayerConfigFingerprint.method.removeInstructions(iputInstructionIndex, 1)
"""
new-instance v0, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;
invoke-direct { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;-><init>()V
invoke-virtual { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;->build()Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig;
move-result-object v0
return-object v0
""",
)
// Filter injected content from API calls out of lists. // Filter injected content from API calls out of lists.
arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach { arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach {

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

View File

@@ -51,26 +51,6 @@ abstract class BasePreference(
layout?.let { setAttribute("android:layout", layout) } 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 { companion object {
fun Element.addSummary(summaryKey: String, summaryType: SummaryType = SummaryType.DEFAULT) = fun Element.addSummary(summaryKey: String, summaryType: SummaryType = SummaryType.DEFAULT) =
setAttribute("android:${summaryType.type}", "@string/$summaryKey") 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.patcher.fingerprint
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET 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.AccessFlags
import com.android.tools.smali.dexlib2.Opcode 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 { internal val accountAttributeFingerprint = fingerprint {
custom { _, classDef -> custom { _, classDef ->
@@ -15,7 +19,7 @@ internal val accountAttributeFingerprint = fingerprint {
} }
} }
internal val productStateProtoFingerprint = fingerprint { internal val productStateProtoGetMapFingerprint = fingerprint {
returns("Ljava/util/Map;") returns("Ljava/util/Map;")
custom { _, classDef -> custom { _, classDef ->
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) { 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 { internal val protobufListsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
custom { method, _ -> method.name == "emptyProtobufList" } custom { method, _ -> method.name == "emptyProtobufList" }
} }
internal val homeStructureFingerprint = fingerprint { internal val protobufListRemoveFingerprint = fingerprint {
opcodes(Opcode.IGET_OBJECT, Opcode.RETURN_OBJECT) custom { method, _ -> method.name == "remove" }
custom { _, classDef -> classDef.endsWith("homeapi/proto/HomeStructure;") }
} }
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

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

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

View File

@@ -1,9 +1,22 @@
package app.revanced.patches.tiktok.feedfilter package app.revanced.patches.tiktok.feedfilter
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val feedApiServiceLIZFingerprint = fingerprint { internal val feedApiServiceLIZFingerprint = fingerprint {
custom { method, classDef -> custom { method, classDef ->
classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList" 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.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch 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.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.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
@@ -42,9 +43,13 @@ private val swipeControlsResourcePatch = resourcePatch {
SwitchPreference("revanced_swipe_haptic_feedback"), SwitchPreference("revanced_swipe_haptic_feedback"),
SwitchPreference("revanced_swipe_save_and_restore_brightness"), SwitchPreference("revanced_swipe_save_and_restore_brightness"),
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"), SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
SwitchPreference("revanced_swipe_show_circular_overlay"), ListPreference(
SwitchPreference("revanced_swipe_overlay_minimal_style"), "revanced_swipe_overlay_style",
summaryKey = null,
),
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER), 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_overlay_timeout", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER), TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_volume_sensitivity", 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_remix_button"),
SwitchPreference("revanced_hide_download_button"), SwitchPreference("revanced_hide_download_button"),
SwitchPreference("revanced_hide_thanks_button"), SwitchPreference("revanced_hide_thanks_button"),
SwitchPreference("revanced_hide_ask_button"),
SwitchPreference("revanced_hide_clip_button"), SwitchPreference("revanced_hide_clip_button"),
SwitchPreference("revanced_hide_playlist_button"), SwitchPreference("revanced_hide_playlist_button"),
), )
), )
) )
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;") addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;")

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.layout.hide.general package app.revanced.patches.youtube.layout.hide.general
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutFingerprint
import app.revanced.util.literal import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@@ -67,6 +68,9 @@ internal val showWatermarkFingerprint = fingerprint {
parameters("L", "L") parameters("L", "L")
} }
/**
* Matches same method as [wideSearchbarLayoutFingerprint].
*/
internal val yoodlesImageViewFingerprint = fingerprint { internal val yoodlesImageViewFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;") returns("Landroid/view/View;")

View File

@@ -44,14 +44,12 @@ var crowdfundingBoxId = -1L
private set private set
var youTubeLogo = -1L var youTubeLogo = -1L
private set private set
var filterBarHeightId = -1L var filterBarHeightId = -1L
private set private set
var relatedChipCloudMarginId = -1L var relatedChipCloudMarginId = -1L
private set private set
var barContainerHeightId = -1L var barContainerHeightId = -1L
private set private set
var fabButtonId = -1L var fabButtonId = -1L
private set private set

View File

@@ -25,11 +25,6 @@ internal val shortsBottomBarContainerFingerprint = fingerprint {
literal { bottomBarContainer } literal { bottomBarContainer }
} }
internal val createShortsButtonsFingerprint = fingerprint {
returns("V")
literal { reelPlayerRightCellButtonHeight }
}
internal val renderBottomNavigationBarFingerprint = fingerprint { internal val renderBottomNavigationBarFingerprint = fingerprint {
returns("V") returns("V")
parameters("Ljava/lang/String;") 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.booleanOption
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch 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.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get 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.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.* import app.revanced.util.findElementByAttributeValueOrThrow
import com.android.tools.smali.dexlib2.Opcode import app.revanced.util.forEachLiteralValueInstruction
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction 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.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal var reelPlayerRightCellButtonHeight = -1L
private set
internal var bottomBarContainer = -1L internal var bottomBarContainer = -1L
private set private set
internal var reelPlayerRightPivotV2Size = -1L internal var reelPlayerRightPivotV2Size = -1L
@@ -137,11 +136,6 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
} }
} }
reelPlayerRightCellButtonHeight = resourceMappings[
"dimen",
"reel_player_right_cell_button_height",
]
bottomBarContainer = resourceMappings[ bottomBarContainer = resourceMappings[
"id", "id",
"bottom_bar_container", "bottom_bar_container",
@@ -186,15 +180,6 @@ val hideShortsComponentsPatch = bytecodePatch(
hideShortsWidgetOption() hideShortsWidgetOption()
execute { 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) addLithoFilter(FILTER_CLASS_DESCRIPTOR)
forEachLiteralValueInstruction( forEachLiteralValueInstruction(
@@ -211,7 +196,7 @@ val hideShortsComponentsPatch = bytecodePatch(
""" """
invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I invoke-static { v$sizeRegister }, $FILTER_CLASS_DESCRIPTOR->getSoundButtonSize(I)I
move-result v$sizeRegister move-result v$sizeRegister
""", """
) )
} }
@@ -261,31 +246,10 @@ val hideShortsComponentsPatch = bytecodePatch(
""" """
invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I invoke-static { v$heightRegister }, $FILTER_CLASS_DESCRIPTOR->getNavigationBarHeight(I)I
move-result v$heightRegister move-result v$heightRegister
""", """
) )
} }
// endregion // 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.AccessFlags
import com.android.tools.smali.dexlib2.Opcode 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 { internal val dislikeFingerprint = fingerprint {
returns("V") returns("V")
strings("like/dislike") 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.addSettingPreference
import app.revanced.patches.youtube.misc.settings.newIntent import app.revanced.patches.youtube.misc.settings.newIntent
import app.revanced.patches.youtube.misc.settings.settingsPatch 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.shared.rollingNumberTextViewAnimationUpdateFingerprint
import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
import app.revanced.patches.youtube.video.videoid.hookVideoId 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. // 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. // Find the field name of the conversion context.
val conversionContextField = textComponentConstructorFingerprint.originalClassDef.fields.find { val conversionContextField = textComponentConstructorFingerprint.originalClassDef.fields.find {
it.type == conversionContextFingerprint.originalClassDef.type it.type == conversionContextFingerprintToString.originalClassDef.type
} ?: throw PatchException("Could not find conversion context field") } ?: throw PatchException("Could not find conversion context field")
textComponentLookupFingerprint.match(textComponentConstructorFingerprint.originalClassDef) textComponentLookupFingerprint.match(textComponentConstructorFingerprint.originalClassDef)
textComponentLookupFingerprint.method.apply { .method.apply {
// Find the instruction for creating the text data object. // Find the instruction for creating the text data object.
val textDataClassType = textComponentDataFingerprint.originalClassDef.type val textDataClassType = textComponentDataFingerprint.originalClassDef.type
@@ -160,12 +161,12 @@ val returnYouTubeDislikePatch = bytecodePatch(
addInstructionsAtControlFlowLabel( addInstructionsAtControlFlowLabel(
insertIndex, insertIndex,
""" """
# Copy conversion context # Copy conversion context
move-object/from16 v$tempRegister, p0 move-object/from16 v$tempRegister, p0
iget-object v$tempRegister, v$tempRegister, $conversionContextField 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; invoke-static { v$tempRegister, v$charSequenceRegister }, $EXTENSION_CLASS_DESCRIPTOR->onLithoTextLoaded(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$charSequenceRegister move-result-object v$charSequenceRegister
""", """
) )
} }
@@ -201,11 +202,9 @@ val returnYouTubeDislikePatch = bytecodePatch(
val charSequenceFieldReference = val charSequenceFieldReference =
getInstruction<ReferenceInstruction>(dislikesIndex).reference 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 = findFreeRegister(insertIndex, charSequenceInstanceRegister, conversionContextRegister)
val freeRegister = registerCount - 1
val conversionContextRegister = registerCount - parameters.size + 1
addInstructions( addInstructions(
insertIndex, insertIndex,

View File

@@ -1,31 +1,27 @@
package app.revanced.patches.youtube.layout.searchbar package app.revanced.patches.youtube.layout.searchbar
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import app.revanced.patches.youtube.layout.hide.general.yoodlesImageViewFingerprint
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val createSearchSuggestionsFingerprint = fingerprint {
opcodes(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
)
strings("ss_rds")
}
internal val setWordmarkHeaderFingerprint = fingerprint { internal val setWordmarkHeaderFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V") returns("V")
parameters("Landroid/widget/ImageView;") parameters("Landroid/widget/ImageView;")
opcodes( custom { methodDef, _ ->
Opcode.IGET_OBJECT, methodDef.containsLiteralInstruction(ytPremiumWordmarkHeaderId) &&
Opcode.INVOKE_STATIC, methodDef.containsLiteralInstruction(ytWordmarkHeaderId)
Opcode.MOVE_RESULT, }
Opcode.IF_NEZ, }
Opcode.IGET_BOOLEAN,
Opcode.IF_EQZ, /**
Opcode.IGET_OBJECT, * Matches the same method as [yoodlesImageViewFingerprint].
Opcode.CONST, */
null, // invoke-static or invoke-virtual. internal val wideSearchbarLayoutFingerprint = fingerprint {
) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Landroid/view/View;")
parameters("L", "L")
literal { actionBarRingoId }
} }

View File

@@ -1,30 +1,67 @@
package app.revanced.patches.youtube.layout.searchbar package app.revanced.patches.youtube.layout.searchbar
import app.revanced.patcher.Fingerprint import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.all.misc.resources.addResourcesPatch
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.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/WideSearchbarPatch;" "Lapp/revanced/extension/youtube/patches/WideSearchbarPatch;"
internal var ytWordmarkHeaderId = -1L
private set
internal var ytPremiumWordmarkHeaderId = -1L
private set
internal var actionBarRingoId = -1L
private set
private val wideSearchbarResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
execute {
ytWordmarkHeaderId = resourceMappings[
"attr",
"ytWordmarkHeader",
]
ytPremiumWordmarkHeaderId = resourceMappings[
"attr",
"ytPremiumWordmarkHeader",
]
actionBarRingoId = resourceMappings[
"layout",
"action_bar_ringo",
]
}
}
val wideSearchbarPatch = bytecodePatch( val wideSearchbarPatch = bytecodePatch(
name = "Wide search bar", name = "Wide search bar",
description = "Adds an option to replace the search icon with a wide search bar. This will hide the YouTube logo when active.", description = "Adds an option to replace the search icon with a wide search bar. " +
"This will hide the YouTube logo when active.",
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
settingsPatch, settingsPatch,
addResourcesPatch, addResourcesPatch,
wideSearchbarResourcePatch,
) )
compatibleWith( compatibleWith(
@@ -46,37 +83,45 @@ val wideSearchbarPatch = bytecodePatch(
SwitchPreference("revanced_wide_searchbar"), SwitchPreference("revanced_wide_searchbar"),
) )
/** setWordmarkHeaderFingerprint.let {
* Navigate a fingerprints method at a given index mutably. // Navigate to the method that checks if the YT logo is shown beside the search bar.
* val shouldShowLogoMethod = with(it.originalMethod) {
* @param index The index to navigate to. val invokeStaticIndex = indexOfFirstInstructionOrThrow {
* @param from The fingerprint to navigate the method on. opcode == Opcode.INVOKE_STATIC &&
* @return The [MutableMethod] which was navigated on. getReference<MethodReference>()?.returnType == "Z"
*/ }
fun BytecodePatchContext.walkMutable(index: Int, from: Fingerprint) = navigate(this).to(invokeStaticIndex).stop()
navigate(from.originalMethod).to(index).stop() }
/** shouldShowLogoMethod.apply {
* Injects instructions required for certain methods. findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index ->
*/ val register = getInstruction<OneRegisterInstruction>(index).registerA
fun MutableMethod.injectSearchBarHook() {
val insertIndex = implementation!!.instructions.size - 1
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions( addInstructionsAtControlFlowLabel(
insertIndex, index,
""" """
invoke-static {v$insertRegister}, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z
move-result v$insertRegister move-result v$register
""", """
) )
}
}
} }
mapOf( // Fix missing left padding when using wide searchbar.
setWordmarkHeaderFingerprint to 1, wideSearchbarLayoutFingerprint.method.apply {
createSearchSuggestionsFingerprint to createSearchSuggestionsFingerprint.patternMatch!!.startIndex, findInstructionIndicesReversedOrThrow {
).forEach { (fingerprint, callIndex) -> val reference = getReference<MethodReference>()
walkMutable(callIndex, fingerprint).injectSearchBarHook() reference?.definingClass == "Landroid/view/LayoutInflater;"
&& reference.name == "inflate"
}.forEach { inflateIndex ->
val register = getInstruction<OneRegisterInstruction>(inflateIndex + 1).registerA
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 package app.revanced.patches.youtube.layout.shortsautoplay
import app.revanced.patcher.fingerprint 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.AccessFlags
import com.android.tools.smali.dexlib2.Opcode 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 { internal val reelEnumConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
@@ -20,3 +25,27 @@ internal val reelPlaybackRepeatFingerprint = fingerprint {
parameters("L") parameters("L")
strings("YoutubePlayerState is in throwing an Error.") 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.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions 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.getInstruction
import app.revanced.patcher.patch.bytecodePatch 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.all.misc.resources.addResources
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch 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_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.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference 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.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.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;" 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( PreferenceScreen.GENERAL_LAYOUT.addPreferences(
// Group the switch and list preference together, since General menu is sorted by name // 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( PreferenceCategory(
titleKey = null, titleKey = null,
sorting = Sorting.UNSORTED, sorting = Sorting.UNSORTED,
@@ -122,16 +122,17 @@ val spoofAppVersionPatch = bytecodePatch(
) )
} }
val insertIndex = spoofAppVersionFingerprint.patternMatch!!.startIndex + 1 spoofAppVersionFingerprint.apply {
val buildOverrideNameRegister = val startIndex = patternMatch!!.startIndex
spoofAppVersionFingerprint.method.getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA val buildOverrideNameRegister = method.getInstruction<OneRegisterInstruction>(startIndex).registerA
spoofAppVersionFingerprint.method.addInstructions( method.addInstructions(
insertIndex, startIndex + 1,
""" """
invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String; invoke-static {v$buildOverrideNameRegister}, $EXTENSION_CLASS_DESCRIPTOR->getYouTubeVersionOverride(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$buildOverrideNameRegister 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.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch 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.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.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.misc.settings.settingsPatch
@@ -43,10 +46,18 @@ val changeStartPagePatch = bytecodePatch(
addResources("youtube", "layout.startpage.changeStartPagePatch") addResources("youtube", "layout.startpage.changeStartPagePatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences( PreferenceScreen.GENERAL_LAYOUT.addPreferences(
ListPreference( PreferenceCategory(
key = "revanced_change_start_page", titleKey = null,
summaryKey = 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. // 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.AccessFlags
import com.android.tools.smali.dexlib2.Opcode 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 { internal val componentContextParserFingerprint = fingerprint {
strings( strings(
"TreeNode result must be set.", "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 { internal val lithoFilterFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
returns("V") returns("V")
custom { _, classDef -> 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 { internal val emptyComponentFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR) accessFlags(AccessFlags.PRIVATE, AccessFlags.CONSTRUCTOR)
parameters() 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.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions 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.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch 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.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_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.is_20_05_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.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.shared.conversionContextFingerprintToString
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findFreeRegister import app.revanced.util.findFreeRegister
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.AccessFlags 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.instruction.OneRegisterInstruction 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.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference 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. * The buffer is a large byte array that represents the component tree.
* This byte array is searched for strings that indicate the current component. * 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 { * class SomeOtherClass {
* // Called before ComponentContextParser.parseBytesToComponentContext method. * // Called before ComponentContextParser.parseComponent() method.
* public void someOtherMethod(ByteBuffer byteBuffer) { * public void someOtherMethod(ByteBuffer byteBuffer) {
* ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch. * ExtensionClass.setProtoBuffer(byteBuffer); // Inserted by this patch.
* ... * ...
* } * }
* } * }
* *
* When patching 19.17 and earlier:
*
* class ComponentContextParser { * 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; * return emptyComponent;
* ... * }
* } * return originalUnpatchedComponent; // Original code.
* }
*
* 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;
* ...
* } * }
* } * }
*/ */
@@ -103,7 +94,7 @@ val lithoFilterPatch = bytecodePatch(
2, 2,
""" """
new-instance v1, $classDescriptor new-instance v1, $classDescriptor
invoke-direct {v1}, $classDescriptor-><init>()V invoke-direct { v1 }, $classDescriptor-><init>()V
const/16 v2, ${filterCount++} const/16 v2, ${filterCount++}
aput-object v1, v0, v2 aput-object v1, v0, v2
""", """,
@@ -115,110 +106,105 @@ val lithoFilterPatch = bytecodePatch(
protobufBufferReferenceFingerprint.method.addInstruction( protobufBufferReferenceFingerprint.method.addInstruction(
0, 0,
" invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V", "invoke-static { p2 }, $EXTENSION_CLASS_DESCRIPTOR->setProtoBuffer(Ljava/nio/ByteBuffer;)V",
) )
// endregion // endregion
// region Hook the method that parses bytes into a ComponentContext. // region Hook the method that parses bytes into a ComponentContext.
val readComponentMethod = readComponentIdentifierFingerprint.originalMethod // Allow the method to run to completion, and override the
// Get the only static method in the class. // return value with an empty component if it should be filtered.
val builderMethodDescriptor = emptyComponentFingerprint.classDef.methods.first { method -> // It is important to allow the original code to always run to completion,
AccessFlags.STATIC.isSet(method.accessFlags) // otherwise memory leaks and poor app performance can occur.
} //
// Only one field. // The extension filtering result needs to be saved off somewhere, but cannot
val emptyComponentField = classBy { classDef -> // save to a class field since the target class is called by multiple threads.
builderMethodDescriptor.returnType == classDef.type // It would be great if there was a way to change the register count of the
}!!.immutableClass.fields.single() // 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.
// Returns an empty component instead of the original component. // Instead save the extension filter result to a thread local and check the
fun createReturnEmptyComponentInstructions(register: Int): String = // filtering result at each method return index.
""" // String field for the litho identifier.
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
"""
componentContextParserFingerprint.method.apply { componentContextParserFingerprint.method.apply {
// 19.18 and later require patching 2 methods instead of one. val conversionContextClass = conversionContextFingerprintToString.originalClassDef
// Otherwise the modifications done here are the same for all targets.
if (is_19_18_or_greater) { val conversionContextIdentifierField = componentContextSubParserFingerprint.match(
// Get the method name of the ReadComponentIdentifierFingerprint call. componentContextParserFingerprint.originalClassDef
val readComponentMethodCallIndex = indexOfFirstInstructionOrThrow { ).let {
val reference = getReference<MethodReference>() // Identifier field is loaded just before the string declaration.
reference?.definingClass == readComponentMethod.definingClass && val index = it.method.indexOfFirstInstructionReversedOrThrow(
reference.name == readComponentMethod.name it.stringMatches!!.first().index
) {
val reference = getReference<FieldReference>()
reference?.definingClass == conversionContextClass.type
&& reference.type == "Ljava/lang/String;"
} }
it.method.getInstruction<ReferenceInstruction>(index).getReference<FieldReference>()
// 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)),
)
} }
}
// 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 conversionContextResultRegister = getInstruction<OneRegisterInstruction>(
val insertHookIndex = indexOfFirstInstructionOrThrow { conversionContextResultIndex
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;"
},
).registerA ).registerA
val freeRegister = findFreeRegister(insertHookIndex, identifierRegister, stringBuilderRegister) val identifierRegister = findFreeRegister(
val invokeFilterInstructions = """ conversionContextResultIndex, conversionContextResultRegister
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 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 // 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.AccessFlags
import com.android.tools.smali.dexlib2.Opcode 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 { internal val autoRepeatFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V") returns("V")

View File

@@ -11,12 +11,14 @@ import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod 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.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.util.InstructionUtils.Companion.branchOpcodes import app.revanced.util.InstructionUtils.Companion.branchOpcodes
import app.revanced.util.InstructionUtils.Companion.returnOpcodes import app.revanced.util.InstructionUtils.Companion.returnOpcodes
import app.revanced.util.InstructionUtils.Companion.writeOpcodes 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.Opcode.* import com.android.tools.smali.dexlib2.Opcode.*
import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.Method
@@ -168,6 +170,15 @@ internal val Instruction.isBranchInstruction: Boolean
internal val Instruction.isReturnInstruction: Boolean internal val Instruction.isReturnInstruction: Boolean
get() = this.opcode in returnOpcodes 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]. * 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", "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. * 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. * Inserted instructions can have it's own control flow labels as well.
@@ -223,13 +254,14 @@ fun MutableMethod.injectHideViewCall(
fun MutableMethod.addInstructionsAtControlFlowLabel( fun MutableMethod.addInstructionsAtControlFlowLabel(
insertIndex: Int, insertIndex: Int,
instructions: String, instructions: String,
vararg externalLabels: ExternalLabel
) { ) {
// Duplicate original instruction and add to +1 index. // Duplicate original instruction and add to +1 index.
addInstruction(insertIndex + 1, getInstruction(insertIndex)) addInstruction(insertIndex + 1, getInstruction(insertIndex))
// Add patch code at same index as duplicated instruction, // Add patch code at same index as duplicated instruction,
// so it uses the original instruction control flow label. // so it uses the original instruction control flow label.
addInstructionsWithLabels(insertIndex + 1, instructions) addInstructionsWithLabels(insertIndex + 1, instructions, *externalLabels)
// Remove original non duplicated instruction. // Remove original non duplicated instruction.
removeInstruction(insertIndex) removeInstruction(insertIndex)
@@ -472,7 +504,7 @@ fun Method.indexOfFirstInstruction(startIndex: Int = 0, targetOpcode: Opcode): I
* @see indexOfFirstInstructionOrThrow * @see indexOfFirstInstructionOrThrow
*/ */
fun Method.indexOfFirstInstruction(startIndex: Int = 0, filter: Instruction.() -> Boolean): Int { fun Method.indexOfFirstInstruction(startIndex: Int = 0, filter: Instruction.() -> Boolean): Int {
var instructions = this.implementation!!.instructions var instructions = this.implementation?.instructions ?: return -1
if (startIndex != 0) { if (startIndex != 0) {
instructions = instructions.drop(startIndex) instructions = instructions.drop(startIndex)
} }
@@ -538,7 +570,7 @@ fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, targetOpcode
* @see indexOfFirstInstructionReversedOrThrow * @see indexOfFirstInstructionReversedOrThrow
*/ */
fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, filter: Instruction.() -> Boolean): Int { fun Method.indexOfFirstInstructionReversed(startIndex: Int? = null, filter: Instruction.() -> Boolean): Int {
var instructions = this.implementation!!.instructions var instructions = this.implementation?.instructions ?: return -1
if (startIndex != null) { if (startIndex != null) {
instructions = instructions.take(startIndex + 1) instructions = instructions.take(startIndex + 1)
} }

View File

@@ -84,6 +84,8 @@ Second \"item\" text"</string>
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. --> <!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' 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. --> <!-- '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. --> <!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. --> <!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch> </patch>
@@ -195,6 +197,8 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch"> <patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch> </patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch"> <patch id="misc.links.bypassURLRedirectsPatch">
</patch> </patch>
<patch id="misc.links.openLinksExternallyPatch"> <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. --> <!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' 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. --> <!-- '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. --> <!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. --> <!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch> </patch>
@@ -195,6 +197,8 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch"> <patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch> </patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch"> <patch id="misc.links.bypassURLRedirectsPatch">
</patch> </patch>
<patch id="misc.links.openLinksExternallyPatch"> <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_title">ØĒŲ…ŲƒŲŠŲ† ØĨŲŠŲ…Ø§ØĄØŠ Ø§Ų„ØŗØˇŲˆØš Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">Ø§Ų„ØĒŲ…ØąŲŠØą Ų„ØŖØŗŲŲ„ ØĨŲ„Ų‰ ØŖØ¯Ų†Ų‰ Ų‚ŲŠŲ…ØŠ Ų„Ų„ØŗØˇŲˆØš ŲŠŲ…ŲƒŲ‘Ų† Ø§Ų„ØŗØˇŲˆØš Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊</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_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_title">Ų…Ų‡Ų„ØŠ ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„ØĒŲ…ØąŲŠØą</string>
<string name="revanced_swipe_overlay_timeout_summary">Ų…Ų‚Ø¯Ø§Øą Ø§Ų„ŲˆŲ‚ØĒ Ø§Ų„Ø°ŲŠ ØĒØ¸Ų‡Øą ŲŲŠŲ‡ ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„ØĒŲ…ØąŲŠØą بؚد Ø§Ų„ØĒØēŲŠŲŠØą بØŦØ˛ØĄ Ø§Ų„ØĢØ§Ų†ŲŠØŠ</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_title">ØĒØšØĒŲŠŲ… ØŽŲ„ŲŲŠØŠ ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„ØĒŲ…ØąŲŠØą Ø§Ų„ØŗØąŲŠØš</string>
<string name="revanced_swipe_overlay_background_opacity_summary">Ų‚ŲŠŲ…ØŠ Ø§Ų„ØĒØšØĒŲŠŲ… Ø¨ŲŠŲ† 0-100</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_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_title">Ų…Ų‚Ø¯Ø§Øą حد Ø§Ų„ØĒŲ…ØąŲŠØą</string>
<string name="revanced_swipe_threshold_summary">Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖØ¯Ų†Ų‰ Ų…Ų† Ø§Ų„ØĒŲ…ØąŲŠØą Ų‚Ø¨Ų„ Ø§ŲƒØĒØ´Ø§Ų Ø§Ų„ØĨŲŠŲ…Ø§ØĄØŠ</string> <string name="revanced_swipe_threshold_summary">Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖØ¯Ų†Ų‰ Ų…Ų† Ø§Ų„ØĒŲ…ØąŲŠØą Ų‚Ø¨Ų„ Ø§ŲƒØĒØ´Ø§Ų Ø§Ų„ØĨŲŠŲ…Ø§ØĄØŠ</string>
<string name="revanced_swipe_volume_sensitivity_title">Ø­ØŗØ§ØŗŲŠØŠ ØĨŲŠŲ…Ø§ØĄØŠ ØĒŲ…ØąŲŠØą Ų…ØŗØĒŲˆŲ‰ Ø§Ų„Øĩ؈ØĒ</string> <string name="revanced_swipe_volume_sensitivity_title">Ø­ØŗØ§ØŗŲŠØŠ ØĨŲŠŲ…Ø§ØĄØŠ ØĒŲ…ØąŲŠØą Ų…ØŗØĒŲˆŲ‰ Ø§Ų„Øĩ؈ØĒ</string>
<string name="revanced_swipe_volume_sensitivity_summary">Ų…Ų‚Ø¯Ø§Øą ØĒØēŲŠØą Ų…ØŗØĒŲˆŲ‰ Ø§Ų„Øĩ؈ØĒ Ų„ŲƒŲ„ ØĒŲ…ØąŲŠØąØŠ</string> <string name="revanced_swipe_volume_sensitivity_summary">Ų…Ų‚Ø¯Ø§Øą ØĒØēŲŠØą Ų…ØŗØĒŲˆŲ‰ Ø§Ų„Øĩ؈ØĒ Ų„ŲƒŲ„ ØĒŲ…ØąŲŠØąØŠ</string>
<string name="revanced_swipe_show_circular_overlay_title">ØšØąØļ Ø§Ų„ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„Ø¯Ø§ØĻØąŲŠØŠ</string> <string name="revanced_swipe_overlay_style_title">Ų†Ų…Øˇ ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„ØĒŲ…ØąŲŠØą</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">؊ØĒŲ… ØšØąØļ Ø§Ų„ØĒØąØ§ŲƒØ¨ Ø§Ų„Ø¯Ø§ØĻØąŲŠ</string> <string name="revanced_swipe_overlay_style_entry_1">ŲˆØ§ØŦŲ‡ØŠ ØŖŲŲ‚ŲŠØŠ</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ØĒØąØ§ŲƒØ¨ Ø§Ų„ØŖŲŲ‚ŲŠ</string> <string name="revanced_swipe_overlay_style_entry_2">ŲˆØ§ØŦŲ‡ØŠ ØŖŲŲ‚ŲŠØŠ (Ø§Ų„ØŖØ¯Ų†Ų‰ - Ø§Ų„ØŖØšŲ„Ų‰)</string>
<string name="revanced_swipe_overlay_minimal_style_title">ØĒŲ…ŲƒŲŠŲ† Ø§Ų„Ų†Ų…Øˇ Ø§Ų„ØŖØ¯Ų†Ų‰</string> <string name="revanced_swipe_overlay_style_entry_3">ŲˆØ§ØŦŲ‡ØŠ ØŖŲŲ‚ŲŠØŠ (Ø§Ų„ØŖØ¯Ų†Ų‰ - Ø§Ų„Ų…Ų†ØĒØĩ؁)</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">ØĒŲ… ØĒŲ…ŲƒŲŠŲ† Ø§Ų„Ų†Ų…Øˇ Ø§Ų„ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„ØŖØ¯Ų†Ų‰</string> <string name="revanced_swipe_overlay_style_entry_4">ŲˆØ§ØŦŲ‡ØŠ داØĻØąŲŠØŠ</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">ØĒŲ… ØĒØšØˇŲŠŲ„ Ų†Ų…Øˇ Ø§Ų„ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„ØŖØ¯Ų†Ų‰</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_title">ØĒŲ…ŲƒŲŠŲ† ØĨŲŠŲ…Ø§ØĄØŠ Ø§Ų„ØĒŲ…ØąŲŠØą Ų„ØĒØēŲŠŲŠØą Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_swipe_change_video_summary_on">ØŗŲŠØ¤Ø¯ŲŠ Ø§Ų„ØĒŲ…ØąŲŠØą ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ ØĨŲ„Ų‰ Ø§Ų„ØĒØēŲŠŲŠØą Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĒØ§Ų„ŲŠ/Ø§Ų„ØŗØ§Ø¨Ų‚</string> <string name="revanced_swipe_change_video_summary_on">ØŗŲŠØ¤Ø¯ŲŠ Ø§Ų„ØĒŲ…ØąŲŠØą ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ ØĨŲ„Ų‰ Ø§Ų„ØĒØēŲŠŲŠØą Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĒØ§Ų„ŲŠ/Ø§Ų„ØŗØ§Ø¨Ų‚</string>
<string name="revanced_swipe_change_video_summary_off">Ų„Ų† ŲŠØ¤Ø¯ŲŠ Ø§Ų„ØĒŲ…ØąŲŠØą ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ ØĨŲ„Ų‰ Ø§Ų„ØĒØēŲŠŲŠØą Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĒØ§Ų„ŲŠ/Ø§Ų„ØŗØ§Ø¨Ų‚</string> <string name="revanced_swipe_change_video_summary_off">Ų„Ų† ŲŠØ¤Ø¯ŲŠ Ø§Ų„ØĒŲ…ØąŲŠØą ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ ØĨŲ„Ų‰ Ø§Ų„ØĒØēŲŠŲŠØą Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĒØ§Ų„ŲŠ/Ø§Ų„ØŗØ§Ø¨Ų‚</string>
</patch> </patch>
<patch id="layout.autocaptions.autoCaptionsPatch"> <patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_disable_auto_captions_title">ØĒØšØˇŲŠŲ„ Ø§Ų„ØĒŲ‘ŲŽØąŲ’ØŦŲŽŲ…ŲŽØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ</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_on">ØĒŲ… ØĒØšØˇŲŠŲ„ Ø§Ų„ØĒØąØŦŲ…ØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ</string>
<string name="revanced_disable_auto_captions_summary_off">ØĒŲ… ØĒŲ…ŲƒŲŠŲ† Ø§Ų„ØĒŲ‘ŲŽØąŲ’ØŦŲŽŲ…ŲŽØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ</string> <string name="revanced_disable_auto_captions_summary_off">ØĒŲ… ØĒŲ…ŲƒŲŠŲ† Ø§Ų„ØĒØąØŦŲ…ØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ</string>
</patch> </patch>
<patch id="layout.buttons.action.hideButtonsPatch"> <patch id="layout.buttons.action.hideButtonsPatch">
<string name="revanced_hide_buttons_screen_title">ØŖØ˛ØąØ§Øą Ø§Ų„ØĨØŦØąØ§ØĄ</string> <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_title">ØĨØŽŲØ§ØĄ Ø´ŲƒØąŲ‹Ø§</string>
<string name="revanced_hide_thanks_button_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø˛Øą Ø´ŲƒØąŲ‹Ø§</string> <string name="revanced_hide_thanks_button_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø˛Øą Ø´ŲƒØąŲ‹Ø§</string>
<string name="revanced_hide_thanks_button_summary_off">؊ØĒŲ… ØšØąØļ Ø˛Øą Ø´ŲƒØąŲ‹Ø§</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. --> <!-- '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_title">ØĨØŽŲØ§ØĄ Ø§Ų„Ų…Ų‚ØˇØš</string>
<string name="revanced_hide_clip_button_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø˛Øą ØĨŲ†Ø´Ø§ØĄ Ų…Ų‚ØˇØš</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> <string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Ø§ØŗØĒؚاد؊ ØŖŲŠŲ‚ŲˆŲ†Ø§ØĒ Ø§Ų„ØĒŲ†Ų‚Ų„ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ</string>
</patch> </patch>
<patch id="layout.startpage.changeStartPagePatch"> <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_default">Ø§Ų„Ø§ŲØĒØąØ§Øļ؊</string>
<string name="revanced_change_start_page_entry_all_subscriptions">ŲƒŲ„Ų‘ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string> <string name="revanced_change_start_page_entry_all_subscriptions">ŲƒŲ„Ų‘ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_change_start_page_entry_browse">ØĒØĩŲØ­ Ø§Ų„Ų‚Ų†ŲˆØ§ØĒ</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_virtual_reality">Ø§Ų„ŲˆØ§Ų‚Øš Ø§Ų„Ø§ŲØĒØąØ§Øļ؊</string>
<string name="revanced_change_start_page_entry_watch_later">Ø´Ø§Ų‡Ø¯ Ų„Ø§Ø­Ų‚Ų‹Ø§</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_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>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch"> <patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">ØĒØšØˇŲŠŲ„ Ø§ØŗØĒØĻŲ†Ø§Ų Ų…Ø´ØēŲ„ Shorts</string> <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_title">ØĨؚداداØĒ GmsCore</string>
<string name="microg_settings_summary">ØĨؚداداØĒ Ų„Ų€ GmsCore</string> <string name="microg_settings_summary">ØĨؚداداØĒ Ų„Ų€ GmsCore</string>
</patch> </patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">ØĨذا Ų‚Ų…ØĒ Ų…Ø¤ØŽØąŲ‹Ø§ بØĒØēŲŠŲŠØą ØĒŲØ§ØĩŲŠŲ„ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ ØĨŲ„Ų‰ Ø­ØŗØ§Ø¨ŲƒØŒ ŲØŖØ˛Ų„ ØĒØĢØ¨ŲŠØĒ MicroG ØĢŲ… ØŖØšØ¯ ØĒØĢØ¨ŲŠØĒŲ‡.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch"> <patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">ØĒØŦØ§ŲˆØ˛ ØĨؚاد؊ ØĒ؈ØŦŲŠŲ‡ URL</string> <string name="revanced_bypass_url_redirects_title">ØĒØŦØ§ŲˆØ˛ ØĨؚاد؊ ØĒ؈ØŦŲŠŲ‡ URL</string>
<string name="revanced_bypass_url_redirects_summary_on">ØĒŲ… ØĒØŦØ§ŲˆØ˛ ØĨؚاد؊ ØĒ؈ØŦŲŠŲ‡ ØšŲ†ŲˆØ§Ų† 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. --> <!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' 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. --> <!-- '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. --> <!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. --> <!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch> </patch>
@@ -197,6 +199,8 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch"> <patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch> </patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch"> <patch id="misc.links.bypassURLRedirectsPatch">
</patch> </patch>
<patch id="misc.links.openLinksExternallyPatch"> <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 â€ĸ Son xəbərlər
â€ĸ İzləməyə davam et â€ĸ İzləməyə davam et
â€ĸ Daha çox kanallar kəşf et â€ĸ Daha çox kanallar kəşf et
â€ĸ AlÄąÅŸ-veriş â€ĸ Mağaza
â€ĸ Təkrar izlə"</string> â€ĸ Təkrar izlə"</string>
<string name="revanced_hide_horizontal_shelves_summary_off">Hissələr gÃļstərilir</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. <!-- '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_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_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_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_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_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> <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_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_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_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_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_player_store_shelf_summary_off">AlÄąÅŸ-veriş rəfi gÃļstərilir</string>
<string name="revanced_hide_shopping_links_title">Video aÃ§Äą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_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> <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. --> <!-- '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_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_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> <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_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_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_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_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_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_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_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_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_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_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_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_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_overlay_style_title">SÃŧrÃŧşmə ÃļrtÃŧyÃŧ Ãŧslubu</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">Dairəvi ÃļrtÃŧk gÃļstərilir</string> <string name="revanced_swipe_overlay_style_entry_1">ÜfÃŧqi ÃļrtÃŧk</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">DÃŧzÃŧnə ÃļrtÃŧk gÃļstərilir</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_minimal_style_title">Ən kiçik Ãŧslubu aktivləşdir</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_minimal_style_summary_on">Ən kiçik ÃļrtÃŧk Ãŧslubu aktivləşdirilib</string> <string name="revanced_swipe_overlay_style_entry_4">Dairəvi ÃļrtÃŧk</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_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_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_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> <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>
<patch id="layout.autocaptions.autoCaptionsPatch"> <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>
<patch id="layout.buttons.action.hideButtonsPatch"> <patch id="layout.buttons.action.hideButtonsPatch">
<string name="revanced_hide_buttons_screen_title">Fəaliyyət dÃŧymələri</string> <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_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_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> <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. --> <!-- '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_title">Kəsmə/ gizlət</string>
<string name="revanced_hide_clip_button_summary_on">Kəsmə dÃŧyməsi gizlidir</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_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_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_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_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_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> <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> <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>
<patch id="layout.startpage.changeStartPagePatch"> <patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Başlanğı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_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_all_subscriptions">BÃŧtÃŧn abunəliklər</string>
<string name="revanced_change_start_page_entry_browse">Kanallara nəzər yetir</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_notifications">Bildirişlər</string>
<string name="revanced_change_start_page_entry_playlists">Pleylistlə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_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_sports">İdman</string>
<string name="revanced_change_start_page_entry_subscriptions">Abunəliklər</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_trending">Trendlər</string>
<string name="revanced_change_start_page_entry_virtual_reality">Faktiki Həyat</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_watch_later">Sonra izlə</string>
<string name="revanced_change_start_page_entry_your_clips">Klipləriniz</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>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch"> <patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Shorts oynadÄącÄą başladÄącÄąnÄą qapat</string> <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_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_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_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. 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_title">GmsCore Tənzimləmələri</string>
<string name="microg_settings_summary">GmsCore ÃŧçÃŧn Tənzimləmələr</string> <string name="microg_settings_summary">GmsCore ÃŧçÃŧn Tənzimləmələr</string>
</patch> </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"> <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_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> <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_title">ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐļŅŅŅ‚ Đ°ŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊаК ŅŅ€ĐēĐ°ŅŅ†Ņ–</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">ĐŸŅ€Đ°Đ˛ŅĐ´ĐˇŅ–Ņ†Đĩ ĐŋаĐģŅŒŅ†Đ°Đŧ ҃ĐŊŅ–Đˇ да ŅĐ°ĐŧĐ°ĐŗĐ° ĐŊŅ–ĐˇĐēĐ°ĐŗĐ° СĐŊĐ°Ņ‡ŅĐŊĐŊŅ ŅŅ€ĐēĐ°ŅŅ†Ņ–, Đēай ҃ĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Đ°ŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊŅƒŅŽ ŅŅ€ĐēĐ°ŅŅ†ŅŒ</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_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_title">ĐĸаКĐŧ-Đ°ŅžŅ‚ ĐŊаĐēĐģадаĐŊĐŊŅ ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_swipe_overlay_timeout_summary">ĐŸŅ€Đ°Ņ†ŅĐŗĐģĐ°ŅŅ†ŅŒ ĐąĐ°Ņ‡ĐŊĐ°ĐŗĐ° ĐŊаĐēĐģадаĐŊĐŊŅ Ņž ĐŧŅ–ĐģҖҁĐĩĐē҃ĐŊĐ´Đ°Ņ…</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_title">НĐĩĐŋŅ€Đ°ĐˇŅ€Ņ‹ŅŅ‚Đ°ŅŅ†ŅŒ Ņ„ĐžĐŊ҃ ĐŊаĐēĐģадĐēŅ– ĐŋŅ€Đ°ĐēŅ€ŅƒŅ‚ĐēŅ–</string>
<string name="revanced_swipe_overlay_background_opacity_summary">ЗĐŊĐ°Ņ‡ŅĐŊĐŊĐĩ ĐŊĐĩĐŋŅ€Đ°ĐˇŅ€Ņ‹ŅŅ‚Đ°ŅŅ†Ņ– ĐŋаĐŧŅ–Đļ 0-100</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_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_title">ĐŸĐ°Ņ€ĐžĐŗ вĐĩĐģҖ҇ҋĐŊŅ– ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_swipe_threshold_summary">ВĐĩĐģҖ҇ҋĐŊŅ ĐŋĐ°Ņ€ĐžĐŗĐ°Đ˛Đ°ĐŗĐ° СĐŊĐ°Ņ‡ŅĐŊĐŊŅ Đ´ĐģŅ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ ĐŋаĐģŅŒŅ†Đ°Đŧ</string> <string name="revanced_swipe_threshold_summary">ВĐĩĐģҖ҇ҋĐŊŅ ĐŋĐ°Ņ€ĐžĐŗĐ°Đ˛Đ°ĐŗĐ° СĐŊĐ°Ņ‡ŅĐŊĐŊŅ Đ´ĐģŅ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_swipe_volume_sensitivity_title">ĐĐ´Ņ‡ŅƒĐ˛Đ°ĐģҌĐŊĐ°ŅŅ†ŅŒ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ Đ´ĐģŅ ĐŗŅƒŅ‡ĐŊĐ°ŅŅ†Ņ–</string> <string name="revanced_swipe_volume_sensitivity_title">ĐĐ´Ņ‡ŅƒĐ˛Đ°ĐģҌĐŊĐ°ŅŅ†ŅŒ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ Đ´ĐģŅ ĐŗŅƒŅ‡ĐŊĐ°ŅŅ†Ņ–</string>
<string name="revanced_swipe_volume_sensitivity_summary">НаĐēĐžĐģҌĐēŅ– СĐŧŅĐŊŅĐĩŅ†Ņ†Đ° ĐŗŅƒŅ‡ĐŊĐ°ŅŅ†ŅŒ ĐŋҀҋ ĐēĐžĐļĐŊŅ‹Đŧ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ–</string> <string name="revanced_swipe_volume_sensitivity_summary">НаĐēĐžĐģҌĐēŅ– СĐŧŅĐŊŅĐĩŅ†Ņ†Đ° ĐŗŅƒŅ‡ĐŊĐ°ŅŅ†ŅŒ ĐŋҀҋ ĐēĐžĐļĐŊŅ‹Đŧ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ–</string>
<string name="revanced_swipe_show_circular_overlay_title">ПаĐēĐ°ĐˇĐ˛Đ°Ņ†ŅŒ ĐēŅ€ŅƒĐŗĐ°Đ˛ĐžĐĩ ĐŊаĐēĐģадаĐŊĐŊĐĩ</string> <string name="revanced_swipe_overlay_style_title">ĐĄŅ‚Ņ‹ĐģҌ ĐŊаĐēĐģадĐēŅ– ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">ĐšŅ€ŅƒĐŗĐ°Đ˛ĐžĐĩ ĐŊаĐēĐģадаĐŊĐŊĐĩ ĐŋаĐēаСваĐĩŅ†Ņ†Đ°</string> <string name="revanced_swipe_overlay_style_entry_1">Đ“Đ°Ņ€Ņ‹ĐˇĐ°ĐŊŅ‚Đ°ĐģҌĐŊĐ°Ņ ĐŊаĐēĐģадĐēа</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">Đ“Đ°Ņ€Ņ‹ĐˇĐ°ĐŊŅ‚Đ°ĐģҌĐŊаĐĩ ĐŊаĐēĐģадаĐŊĐŊĐĩ ĐŋаĐēаСваĐĩŅ†Ņ†Đ°</string> <string name="revanced_swipe_overlay_style_entry_2">Đ“Đ°Ņ€Ņ‹ĐˇĐ°ĐŊŅ‚Đ°ĐģҌĐŊĐ°Ņ ĐŊаĐēĐģадĐēа (ĐŧŅ–ĐŊŅ–ĐŧаĐģҌĐŊĐ°ŅÂ â€” СвĐĩŅ€Ņ…Ņƒ)</string>
<string name="revanced_swipe_overlay_minimal_style_title">ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐŧŅ–ĐŊŅ–ĐŧаĐģҌĐŊŅ‹ ҁ҂ҋĐģҌ</string> <string name="revanced_swipe_overlay_style_entry_3">Đ“Đ°Ņ€Ņ‹ĐˇĐ°ĐŊŅ‚Đ°ĐģҌĐŊĐ°Ņ ĐŊаĐēĐģадĐēа (ĐŧŅ–ĐŊŅ–ĐŧаĐģҌĐŊĐ°ŅÂ â€” Đŋа Ņ†ŅĐŊ҂Ҁҋ)</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">ĐŖĐēĐģŅŽŅ‡Đ°ĐŊŅ‹ ĐŧŅ–ĐŊŅ–ĐŧаĐģҌĐŊŅ‹ ҁ҂ҋĐģҌ ĐŊаĐēĐģадаĐŊĐŊŅ</string> <string name="revanced_swipe_overlay_style_entry_4">ĐšŅ€ŅƒĐŗĐ°Đ˛Đ°Ņ ĐŊаĐēĐģадĐēа</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">ĐœŅ–ĐŊŅ–ĐŧаĐģҌĐŊŅ‹ ҁ҂ҋĐģҌ ĐŊаĐēĐģадаĐŊĐŊŅ Đ˛Ņ‹ĐēĐģŅŽŅ‡Đ°ĐŊŅ‹</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_title">ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐˇŅŒĐŧĐĩĐŊ҃ Đ˛Ņ–Đ´ŅĐ° ĐŋŅ€Đ°Đˇ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_swipe_change_video_summary_on">ĐŸŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐŋаĐģŅŒŅ†Đ°Đŧ ҃ Ņ€ŅĐļŅ‹ĐŧĐĩ ĐŋĐžŅžĐŊĐ°ĐŗĐ° ŅĐēŅ€Đ°ĐŊа ĐˇŅŒĐŧĐĩĐŊŅ–Ņ†ŅŒ Đ˛Ņ–Đ´ŅĐ° ĐŊа ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊаĐĩ/ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐĩ</string> <string name="revanced_swipe_change_video_summary_on">ĐŸŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐŋаĐģŅŒŅ†Đ°Đŧ ҃ Ņ€ŅĐļŅ‹ĐŧĐĩ ĐŋĐžŅžĐŊĐ°ĐŗĐ° ŅĐēŅ€Đ°ĐŊа ĐˇŅŒĐŧĐĩĐŊŅ–Ņ†ŅŒ Đ˛Ņ–Đ´ŅĐ° ĐŊа ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊаĐĩ/ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐĩ</string>
<string name="revanced_swipe_change_video_summary_off">ĐŸŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐŋаĐģŅŒŅ†Đ°Đŧ ҃ Ņ€ŅĐļŅ‹ĐŧĐĩ ĐŋĐžŅžĐŊĐ°ĐŗĐ° ŅĐēŅ€Đ°ĐŊа ĐŊĐĩ ĐˇŅŒĐŧĐĩĐŊŅ–Ņ†ŅŒ Đ˛Ņ–Đ´ŅĐ° ĐŊа ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊаĐĩ/ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐĩ</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_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ Đ”ĐˇŅĐēŅƒĐš</string>
<string name="revanced_hide_thanks_button_summary_on">КĐŊĐžĐŋĐēа ĐŋĐ°Đ´ĐˇŅĐēŅ– ŅŅ…Đ°Đ˛Đ°ĐŊа</string> <string name="revanced_hide_thanks_button_summary_on">КĐŊĐžĐŋĐēа ĐŋĐ°Đ´ĐˇŅĐēŅ– ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
<string name="revanced_hide_thanks_button_summary_off">ПаĐēаСаĐŊа ĐēĐŊĐžĐŋĐēа ĐŋĐ°Đ´ĐˇŅĐēŅ–</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. --> <!-- '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_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐēĐģŅ–Đŋ</string>
<string name="revanced_hide_clip_button_summary_on">КĐŊĐžĐŋĐēа ĐēĐģŅ–Đŋа ŅŅ…Đ°Đ˛Đ°ĐŊа</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> <string name="revanced_spoof_app_version_target_entry_2">19.01.34 - АдĐŊĐ°ŅžĐģĐĩĐŊĐŊĐĩ ŅŅ‚Đ°Ņ€Ņ‹Ņ… СĐŊĐ°Ņ‡ĐēĐžŅž ĐŊĐ°Đ˛Ņ–ĐŗĐ°Ņ†Ņ‹Ņ–</string>
</patch> </patch>
<patch id="layout.startpage.changeStartPagePatch"> <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_default">Па СĐŧĐ°ŅžŅ‡Đ°ĐŊĐŊŅ–</string>
<string name="revanced_change_start_page_entry_all_subscriptions">ĐŖŅĐĩ ĐŋадĐŋҖҁĐēŅ–</string> <string name="revanced_change_start_page_entry_all_subscriptions">ĐŖŅĐĩ ĐŋадĐŋҖҁĐēŅ–</string>
<string name="revanced_change_start_page_entry_browse">ĐžĐąĐˇĐžŅ€ ĐēаĐŊаĐģОв</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_virtual_reality">Đ’Ņ–Ņ€Ņ‚ŅƒĐ°ĐģҌĐŊĐ°Ņ Ņ€ŅĐ°ĐģҌĐŊĐ°ŅŅ†ŅŒ</string>
<string name="revanced_change_start_page_entry_watch_later">ĐŸĐžŅĐŧĐžŅ‚Ņ€ĐĩŅ‚ŅŒ ĐŋОСĐļĐĩ</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_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>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch"> <patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">АдĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ адĐŊĐ°ŅžĐģĐĩĐŊĐŊĐĩ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊŅ Shorts</string> <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_title">НаĐģĐ°Đ´Ņ‹ GmsCore</string>
<string name="microg_settings_summary">НаĐģĐ°Đ´Ņ‹ Đ´ĐģŅ GmsCore</string> <string name="microg_settings_summary">НаĐģĐ°Đ´Ņ‹ Đ´ĐģŅ GmsCore</string>
</patch> </patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">КаĐģŅ– Đ˛Ņ‹ ĐŊŅĐ´Đ°ŅžĐŊа СĐŧŅĐŊŅ–ĐģŅ– даĐŊŅ‹Ņ Đ´ĐģŅ ŅžĐ˛Đ°Ņ…ĐžĐ´Ņƒ Ņž ŅĐ˛ĐžĐš ҃ĐģŅ–ĐēĐžĐ˛Ņ‹ СаĐŋҖҁ, Đ˛Ņ‹Đ´Đ°ĐģҖ҆Đĩ Ņ– ĐŋĐĩŅ€Đ°ŅžŅŅ‚Đ°ĐģŅŽĐšŅ†Đĩ MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch"> <patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">ĐĐąŅ‹Ņ…ĐžĐ´ URL-ĐŋĐĩŅ€Đ°ĐŊаĐēŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊŅŅž</string> <string name="revanced_bypass_url_redirects_title">ĐĐąŅ‹Ņ…ĐžĐ´ URL-ĐŋĐĩŅ€Đ°ĐŊаĐēŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊŅŅž</string>
<string name="revanced_bypass_url_redirects_summary_on">ПĐĩŅ€Đ°ĐŊаĐēŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊĐĩ 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_title">ЗадаваĐŊĐĩ ĐŊа ŅŅ€ĐēĐžŅŅ‚ ҇ҀĐĩС ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">ПĐģŅŠĐˇĐŗĐ°ĐŊĐĩŅ‚Đž ĐŊадОĐģ҃ Đ´Đž ĐŊаК-ĐŊĐ¸ŅĐēĐ°Ņ‚Đ° ŅŅ‚ĐžĐšĐŊĐžŅŅ‚ ĐŊа ĐļĐĩŅŅ‚Đ° Са ŅŅ€ĐēĐžŅŅ‚, Са да ҁĐĩ аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ° Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐ°Ņ‚Đ° ŅŅ€ĐēĐžŅŅ‚</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_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_title">Đ—Đ°Đ´Ņ€ŅŠĐļĐēа ĐŊа ĐŋĐģŅŠĐˇĐŗĐ°Ņ‰Đ°Ņ‚Đ° ĐēĐžĐŊŅ‚Ņ€ĐžĐģа Са ĐŋĐžĐēаСваĐŊĐĩ</string>
<string name="revanced_swipe_overlay_timeout_summary">Đ’Ņ€ĐĩĐŧĐĩ Са ĐēĐžĐĩŅ‚Đž ĐŋĐģŅŠĐˇĐŗĐ°Ņ‰Đ°Ņ‚Đ° ĐēĐžĐŊŅ‚Ņ€ĐžĐģа Đĩ видиĐŧа.</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_title">ПĐģŅŠĐˇĐŗĐ°ĐŊĐĩ ĐŊа Ņ„ĐžĐŊа ĐŊа ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩŅ‚Đž ĐŊĐĩĐŋŅ€ĐžĐˇŅ€Đ°Ņ‡ĐŊĐžŅŅ‚</string>
<string name="revanced_swipe_overlay_background_opacity_summary">ĐĄŅ‚ĐžĐšĐŊĐžŅŅ‚ ĐŊа ĐŊĐĩĐŋŅ€ĐžĐˇŅ€Đ°Ņ‡ĐŊĐžŅŅ‚Ņ‚Đ° ĐŧĐĩĐļĐ´Ņƒ 0-100</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_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_title">ĐŸŅ€Đ°Đŗ ĐŊа вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐ°Ņ‚Đ° ĐŊа ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ</string>
<string name="revanced_swipe_threshold_summary">ĐŸŅ€Đ°Đŗ ĐŋŅ€Đĩди да ҁĐĩ ĐžŅŅŠŅ‰ĐĩŅŅ‚Đ˛Đ¸ ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩŅ‚Đž</string> <string name="revanced_swipe_threshold_summary">ĐŸŅ€Đ°Đŗ ĐŋŅ€Đĩди да ҁĐĩ ĐžŅŅŠŅ‰ĐĩŅŅ‚Đ˛Đ¸ ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩŅ‚Đž</string>
<string name="revanced_swipe_volume_sensitivity_title">Đ§ŅƒĐ˛ŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģĐŊĐžŅŅ‚ ĐŋŅ€Đ¸ ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ Са ŅĐ¸Đģа ĐŊа ĐˇĐ˛ŅƒĐēа</string> <string name="revanced_swipe_volume_sensitivity_title">Đ§ŅƒĐ˛ŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģĐŊĐžŅŅ‚ ĐŋŅ€Đ¸ ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ Са ŅĐ¸Đģа ĐŊа ĐˇĐ˛ŅƒĐēа</string>
<string name="revanced_swipe_volume_sensitivity_summary">КоĐģĐēĐž ҁĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊŅ ŅĐ¸ĐģĐ°Ņ‚Đ° ĐŊа ĐˇĐ˛ŅƒĐēа ĐŋŅ€Đ¸ Đ˛ŅŅĐēĐž ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ</string> <string name="revanced_swipe_volume_sensitivity_summary">КоĐģĐēĐž ҁĐĩ ĐŋŅ€ĐžĐŧĐĩĐŊŅ ŅĐ¸ĐģĐ°Ņ‚Đ° ĐŊа ĐˇĐ˛ŅƒĐēа ĐŋŅ€Đ¸ Đ˛ŅŅĐēĐž ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ</string>
<string name="revanced_swipe_show_circular_overlay_title">ПоĐēаСваĐŊĐĩ ĐŊа ĐēŅ€ŅŠĐŗŅŠĐģ ĐžĐ˛ŅŠŅ€ĐģĐĩĐš</string> <string name="revanced_swipe_overlay_style_title">ĐĄŅ‚Đ¸Đģ ĐŊа ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩ ĐŋŅ€Đ¸ ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">ПоĐēаСва ҁĐĩ ĐēŅ€ŅŠĐŗŅŠĐģ ĐžĐ˛ŅŠŅ€ĐģĐĩĐš</string> <string name="revanced_swipe_overlay_style_entry_1">ĐĨĐžŅ€Đ¸ĐˇĐžĐŊŅ‚Đ°ĐģĐŊĐž ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩ</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">ПоĐēаСва ҁĐĩ Ņ…ĐžŅ€Đ¸ĐˇĐžĐŊŅ‚Đ°ĐģĐĩĐŊ ĐžĐ˛ŅŠŅ€ĐģĐĩĐš</string> <string name="revanced_swipe_overlay_style_entry_2">ĐĨĐžŅ€Đ¸ĐˇĐžĐŊŅ‚Đ°ĐģĐŊĐž ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩ (ĐŧиĐŊиĐŧаĐģĐŊĐž – ĐžŅ‚ĐŗĐžŅ€Đĩ)</string>
<string name="revanced_swipe_overlay_minimal_style_title">АĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŧиĐŊиĐŧаĐģĐĩĐŊ ŅŅ‚Đ¸Đģ</string> <string name="revanced_swipe_overlay_style_entry_3">ĐĨĐžŅ€Đ¸ĐˇĐžĐŊŅ‚Đ°ĐģĐŊĐž ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩ (ĐŧиĐŊиĐŧаĐģĐŊĐž – ҆ĐĩĐŊŅ‚ŅŠŅ€)</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">МиĐŊиĐŧаĐģĐŊĐ¸ŅŅ‚ ŅŅ‚Đ¸Đģ ĐŊа ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩ Đĩ аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ</string> <string name="revanced_swipe_overlay_style_entry_4">ĐšŅ€ŅŠĐŗĐžĐ˛Đž ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩ</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">МиĐŊиĐŧаĐģĐŊĐ¸ŅŅ‚ ŅŅ‚Đ¸Đģ ĐŊа ĐžĐ˛ŅŠŅ€ĐģĐĩŅ Đĩ Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ</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_title">ВĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа видĐĩĐžŅ‚Đž ҇ҀĐĩС ĐŋĐģŅŠĐˇĐ˛Đ°ĐŊĐĩ</string>
<string name="revanced_swipe_change_video_summary_on">ПĐģŅŠĐˇĐ˛Đ°ĐŊĐĩŅ‚Đž в Ņ€ĐĩĐļиĐŧ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ ҉Đĩ ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ¸ ĐēҊĐŧ ҁĐģĐĩĐ´Đ˛Đ°Ņ‰ĐžŅ‚Đž/ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊĐž видĐĩĐž</string> <string name="revanced_swipe_change_video_summary_on">ПĐģŅŠĐˇĐ˛Đ°ĐŊĐĩŅ‚Đž в Ņ€ĐĩĐļиĐŧ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ ҉Đĩ ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ¸ ĐēҊĐŧ ҁĐģĐĩĐ´Đ˛Đ°Ņ‰ĐžŅ‚Đž/ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊĐž видĐĩĐž</string>
<string name="revanced_swipe_change_video_summary_off">ПĐģŅŠĐˇĐ˛Đ°ĐŊĐĩŅ‚Đž в Ņ€ĐĩĐļиĐŧ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ ĐŊŅĐŧа да ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ¸ ĐēҊĐŧ ҁĐģĐĩĐ´Đ˛Đ°Ņ‰ĐžŅ‚Đž/ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊĐž видĐĩĐž</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_title">Đ‘ŅƒŅ‚ĐžĐŊ Са ĐąĐģĐ°ĐŗĐžĐ´Đ°Ņ€ĐŊĐžŅŅ‚</string>
<string name="revanced_hide_thanks_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊа Са ĐąĐģĐ°ĐŗĐžĐ´Đ°Ņ€ĐŊĐžŅŅ‚ Đĩ ҁĐēŅ€Đ¸Ņ‚</string> <string name="revanced_hide_thanks_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊа Са ĐąĐģĐ°ĐŗĐžĐ´Đ°Ņ€ĐŊĐžŅŅ‚ Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_thanks_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊа Са ĐąĐģĐ°ĐŗĐžĐ´Đ°Ņ€ĐŊĐžŅŅ‚ ҁĐĩ ĐŋĐžĐēаСва</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. --> <!-- '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_title">Đ‘ŅƒŅ‚ĐžĐŊ Са ŅŅŠĐˇĐ´Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐēĐģиĐŋ</string>
<string name="revanced_hide_clip_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊа Са ĐēĐģиĐŋ Đĩ ҁĐēŅ€Đ¸Ņ‚</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> <string name="revanced_spoof_app_version_target_entry_2">19.01.34 - Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ŅŅ‚Đ°Ņ€Đ¸ иĐēĐžĐŊи Са ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸Ņ</string>
</patch> </patch>
<patch id="layout.startpage.changeStartPagePatch"> <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_default">По ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ</string>
<string name="revanced_change_start_page_entry_all_subscriptions">Đ’ŅĐ¸Ņ‡Đēи айОĐŊаĐŧĐĩĐŊŅ‚Đ¸</string> <string name="revanced_change_start_page_entry_all_subscriptions">Đ’ŅĐ¸Ņ‡Đēи айОĐŊаĐŧĐĩĐŊŅ‚Đ¸</string>
<string name="revanced_change_start_page_entry_browse">Đ Đ°ĐˇĐŗĐģĐĩĐļдаĐŊĐĩ ĐŊа ĐēаĐŊаĐģа</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_virtual_reality">Đ’Đ¸Ņ€Ņ‚ŅƒĐ°ĐģĐŊа Ņ€ĐĩаĐģĐŊĐžŅŅ‚</string>
<string name="revanced_change_start_page_entry_watch_later">ГĐģĐĩдаК ĐŋĐž-ĐēҊҁĐŊĐž</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_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>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch"> <patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Shorts ĐŋĐģĐĩĐšŅŠŅ€Đ° ĐŋŅ€Đ¸ ŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ</string> <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_title">GmsCore ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи</string>
<string name="microg_settings_summary">ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа GmsCore</string> <string name="microg_settings_summary">ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊа GmsCore</string>
</patch> </patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">АĐēĐž ĐŊĐ°ŅĐēĐžŅ€Đž ҁ҂Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊиĐģи даĐŊĐŊĐ¸Ņ‚Đĩ ŅĐ¸ Са Đ˛Ņ…ĐžĐ´ в ĐŋŅ€ĐžŅ„Đ¸Đģа, Đ´ĐĩиĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐšŅ‚Đĩ и иĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐšŅ‚Đĩ ĐžŅ‚ĐŊОвО MicroG.</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch"> <patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">ЗаобиĐēаĐģŅĐŊĐĩ ĐŊа URL ĐŋŅ€ĐĩĐŊĐ°ŅĐžŅ‡Đ˛Đ°ĐŊĐĩ</string> <string name="revanced_bypass_url_redirects_title">ЗаобиĐēаĐģŅĐŊĐĩ ĐŊа URL ĐŋŅ€ĐĩĐŊĐ°ŅĐžŅ‡Đ˛Đ°ĐŊĐĩ</string>
<string name="revanced_bypass_url_redirects_summary_on">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_title">āĻ¸ā§āĻŦ⧟āĻ‚āĻ•ā§āϰāĻŋ⧟-āωāĻœā§āĻœā§āĻŦāϞāϤāĻžāϰ āĻ…āĻ™ā§āĻ—āĻ­āĻ™ā§āĻ—āĻŋ āϏāĻ•ā§āϰāĻŋ⧟ āĻ•āϰ⧁āύ</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">āωāĻœā§āĻœā§āĻŦāϞāϤāĻžāϰ āĻ…āĻ™ā§āĻ—āĻ­āĻ™ā§āĻ—āĻŋāϰ āϏāĻ°ā§āĻŦāύāĻŋāĻŽā§āύ āĻŽāĻžāύ⧇ āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻĄāĻžāωāύ āĻ•āϰāϞ⧇ āĻ…āĻŸā§‹-āωāĻœā§āĻœā§āĻŦāϞāϤāĻž āϏāĻ•ā§āώāĻŽ āĻšāϝāĻŧ</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_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_title">āĻ“āĻ­āĻžāϰ-āϞ⧇ āϟāĻžāχāĻŽ āφāωāϟ</string>
<string name="revanced_swipe_overlay_timeout_summary">āĻ•āϤ āĻŽāĻŋāϞāĻŋāϏ⧇āϕ⧇āĻ¨ā§āĻĄā§‡āϰ āϜāĻ¨ā§āϝ āĻ“āĻ­āĻžāϰāϞ⧇ āĻĻ⧃āĻļā§āϝāĻŽāĻžāύ āĻšāĻŦ⧇</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_title">āĻ…āĻ¸ā§āĻŦāĻšā§āĻ›āϤāĻž</string>
<string name="revanced_swipe_overlay_background_opacity_summary">0-100 āĻāϰ āĻŽāĻ§ā§āϝ⧇ āĻ…āĻ¸ā§āĻŦāĻšā§āĻ›āϤāĻžāϰ āĻŽāĻžāύ</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_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_title">āĻ¸ā§‹ā§ŸāĻžāχāĻĒ āĻĨā§āϰ⧇āĻļāĻšā§‹āĻ˛ā§āĻĄ āĻāϰ āĻŽāĻžāĻ¤ā§āϰāĻž</string>
<string name="revanced_swipe_threshold_summary">āĻ¸ā§‹ā§ŸāĻžāχāĻĒ āĻ•āϰāĻžāϰ āĻĨā§āϰ⧇āĻļāĻšā§‹āĻ˛ā§āĻĄā§‡āϰ āĻĒāϰāĻŋāĻŽāĻžāĻŖ</string> <string name="revanced_swipe_threshold_summary">āĻ¸ā§‹ā§ŸāĻžāχāĻĒ āĻ•āϰāĻžāϰ āĻĨā§āϰ⧇āĻļāĻšā§‹āĻ˛ā§āĻĄā§‡āϰ āĻĒāϰāĻŋāĻŽāĻžāĻŖ</string>
<string name="revanced_swipe_volume_sensitivity_title">āĻ­āϞāĻŋāωāĻŽ āϏ⧋āϝāĻŧāĻžāχāĻĒ āϏāĻ‚āĻŦ⧇āĻĻāύāĻļā§€āϞāϤāĻž</string> <string name="revanced_swipe_volume_sensitivity_title">āĻ­āϞāĻŋāωāĻŽ āϏ⧋āϝāĻŧāĻžāχāĻĒ āϏāĻ‚āĻŦ⧇āĻĻāύāĻļā§€āϞāϤāĻž</string>
<string name="revanced_swipe_volume_sensitivity_summary">āĻĒā§āϰāϤāĻŋ āϏ⧋āϝāĻŧāĻžāχāĻĒ⧇ āĻ­āϞāĻŋāωāĻŽ āĻ•āϤāϟāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāĻŋāϤ āĻšāϝāĻŧ</string> <string name="revanced_swipe_volume_sensitivity_summary">āĻĒā§āϰāϤāĻŋ āϏ⧋āϝāĻŧāĻžāχāĻĒ⧇ āĻ­āϞāĻŋāωāĻŽ āĻ•āϤāϟāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāĻŋāϤ āĻšāϝāĻŧ</string>
<string name="revanced_swipe_show_circular_overlay_title">āĻŦ⧃āĻ¤ā§āϤāĻžāĻ•āĻžāϰ āĻ“āĻ­āĻžāϰāϞ⧇ āĻĻ⧇āĻ–āĻžāύ</string> <string name="revanced_swipe_overlay_style_title">āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ“āĻ­āĻžāϰāϞ⧇ āĻļ⧈āϞ⧀</string>
<string name="revanced_swipe_show_circular_overlay_summary_on">āĻŦ⧃āĻ¤ā§āϤāĻžāĻ•āĻžāϰ āĻ“āĻ­āĻžāϰāϞ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string> <string name="revanced_swipe_overlay_style_entry_1">āĻ…āύ⧁āĻ­ā§‚āĻŽāĻŋāĻ• āĻ“āĻ­āĻžāϰāϞ⧇</string>
<string name="revanced_swipe_show_circular_overlay_summary_off">āĻ…āύ⧁āĻ­ā§‚āĻŽāĻŋāĻ• āĻ“āĻ­āĻžāϰāϞ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string> <string name="revanced_swipe_overlay_style_entry_2">āĻ…āύ⧁āĻ­ā§‚āĻŽāĻŋāĻ• āĻ“āĻ­āĻžāϰāϞ⧇ (āĻ¨ā§āϝ⧂āύāϤāĻŽ - āωāĻĒāϰ⧇)</string>
<string name="revanced_swipe_overlay_minimal_style_title">āĻ¨ā§āϝ⧂āύāϤāĻŽ āĻļ⧈āϞ⧀ āϏāĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ</string> <string name="revanced_swipe_overlay_style_entry_3">āĻ…āύ⧁āĻ­ā§‚āĻŽāĻŋāĻ• āĻ“āĻ­āĻžāϰāϞ⧇ (āĻ¨ā§āϝ⧂āύāϤāĻŽ - āϕ⧇āĻ¨ā§āĻĻā§āϰ)</string>
<string name="revanced_swipe_overlay_minimal_style_summary_on">Minimal overlay style enabled āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇</string> <string name="revanced_swipe_overlay_style_entry_4">āĻŦ⧃āĻ¤ā§āϤāĻžāĻ•āĻžāϰ āĻ“āĻ­āĻžāϰāϞ⧇</string>
<string name="revanced_swipe_overlay_minimal_style_summary_off">āĻ¨ā§āϝ⧂āύāϤāĻŽ āĻ“āĻ­āĻžāϰāϞ⧇ āĻļ⧈āϞ⧀ āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋāϝāĻŧ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇</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_title">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāϤ⧇ āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ•āϰ⧇ āϏāĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ</string>
<string name="revanced_swipe_change_video_summary_on">āĻĢ⧁āϞāĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻŽā§‹āĻĄā§‡ āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ•āϰāϞ⧇ āĻĒāϰāĻŦāĻ°ā§āϤ⧀/āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻšāĻŦ⧇</string> <string name="revanced_swipe_change_video_summary_on">āĻĢ⧁āϞāĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻŽā§‹āĻĄā§‡ āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ•āϰāϞ⧇ āĻĒāϰāĻŦāĻ°ā§āϤ⧀/āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻšāĻŦ⧇</string>
<string name="revanced_swipe_change_video_summary_off">āĻĢ⧁āϞāĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻŽā§‹āĻĄā§‡ āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ•āϰāϞ⧇ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ /āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻšāĻŦ⧇ āύāĻž</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_title">āϧāĻ¨ā§āϝāĻŦāĻžāĻĻ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_thanks_button_summary_on">āϧāĻ¨ā§āϝāĻŦāĻžāĻĻ āĻŦā§‹āϤāĻžāĻŽ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string> <string name="revanced_hide_thanks_button_summary_on">āϧāĻ¨ā§āϝāĻŦāĻžāĻĻ āĻŦā§‹āϤāĻžāĻŽ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_thanks_button_summary_off">āϧāĻ¨ā§āϝāĻŦāĻžāĻĻ āĻŦā§‹āϤāĻžāĻŽ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ</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. --> <!-- '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_title">āĻ•ā§āϞāĻŋāĻĒ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_clip_button_summary_on">āĻ•ā§āϞāĻŋāĻĒ āĻŦā§‹āϤāĻžāĻŽ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</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> <string name="revanced_spoof_app_version_target_entry_2">19.01.34 - āĻĒ⧁āϰāύ⧋ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āφāχāĻ•āύ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧁āύ</string>
</patch> </patch>
<patch id="layout.startpage.changeStartPagePatch"> <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_default">āĻĒā§‚āĻ°ā§āĻŦ-āύāĻŋāĻ°ā§āϧāĻžāϰāĻŋāϤ</string>
<string name="revanced_change_start_page_entry_all_subscriptions">āĻ…āĻ¨ā§ā§ŸāĻžāϏāĻŦ āϰāĻ­āĻ°ā§āĻŦāĻžāĻļāĻžāϰ āϚāĻŋ⧟⧁āϟ</string> <string name="revanced_change_start_page_entry_all_subscriptions">āĻ…āĻ¨ā§ā§ŸāĻžāϏāĻŦ āϰāĻ­āĻ°ā§āĻŦāĻžāĻļāĻžāϰ āϚāĻŋ⧟⧁āϟ</string>
<string name="revanced_change_start_page_entry_browse">āĻšā§āϝāĻžāύ⧇āϞ āĻŦā§āϰāĻžāωāϜ āĻ•āϰ⧁āύ</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_virtual_reality">āĻŦāĻŋāĻ°ā§āϚ⧁āĻ­ā§āϝāĻŧāĻžāϞ āϟāĻŋāĻāϞāĻŋāϟāĻŋ</string>
<string name="revanced_change_start_page_entry_watch_later">āĻĒāϰ⧇ āĻĻ⧇āϖ⧁āύ</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_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>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch"> <patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Shorts āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ āφāĻŦāĻžāϰ āϚāĻžāϞāĻžāύ⧋ āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋ⧟ āĻ•āϰ⧁āύ</string> <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_title">GmsCore āϏ⧇āϟāĻŋāĻ‚</string>
<string name="microg_settings_summary">GmsCore āĻāϰ āϜāĻ¨ā§āϝ āϏ⧇āϟāĻŋāĻ‚</string> <string name="microg_settings_summary">GmsCore āĻāϰ āϜāĻ¨ā§āϝ āϏ⧇āϟāĻŋāĻ‚</string>
</patch> </patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
<string name="microg_offline_account_login_error">āφāĻĒāύāĻŋ āϝāĻĻāĻŋ āϏāĻŽā§āĻĒā§āϰāϤāĻŋ āφāĻĒāύāĻžāϰ āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ āϞāĻ—āχāύ āĻŦāĻŋāĻļāĻĻ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧇ āĻĨāĻžāϕ⧇āύ, āϤāĻŦ⧇ MicroG āφāύāχāύāĻ¸ā§āϟāϞ āĻ•āϰ⧁āύ āĻāĻŦāĻ‚ āĻĒ⧁āύāϰāĻžāϝāĻŧ āχāύāĻ¸ā§āϟāϞ āĻ•āϰ⧁āύāĨ¤</string>
</patch>
<patch id="misc.links.bypassURLRedirectsPatch"> <patch id="misc.links.bypassURLRedirectsPatch">
<string name="revanced_bypass_url_redirects_title">URL āĻĒ⧁āύāσāύāĻŋāĻ°ā§āĻĻ⧇āĻļ āĻŦāĻžāχāĻĒāĻžāϏ āĻ•āϰ⧁āύ</string> <string name="revanced_bypass_url_redirects_title">URL āĻĒ⧁āύāσāύāĻŋāĻ°ā§āĻĻ⧇āĻļ āĻŦāĻžāχāĻĒāĻžāϏ āĻ•āϰ⧁āύ</string>
<string name="revanced_bypass_url_redirects_summary_on">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. --> <!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Download' 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. --> <!-- '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. --> <!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
<!-- 'Save' should be translated with the same localized wording that YouTube displays. --> <!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
</patch> </patch>
@@ -195,6 +197,8 @@ Second \"item\" text"</string>
</patch> </patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch"> <patch id="misc.gms.gmsCoreSupportResourcePatch">
</patch> </patch>
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
</patch>
<patch id="misc.links.bypassURLRedirectsPatch"> <patch id="misc.links.bypassURLRedirectsPatch">
</patch> </patch>
<patch id="misc.links.openLinksExternallyPatch"> <patch id="misc.links.openLinksExternallyPatch">

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