Compare commits

...

57 Commits

Author SHA1 Message Date
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
168 changed files with 4212 additions and 1627 deletions

View File

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

@@ -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.23.0-dev.7

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

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

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

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,8 @@ import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.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 ı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şlaıc səhifəsini təyin et</string> <string name="revanced_change_start_page_title">Başlatma səhifəsini dəyişdir</string>
<string name="revanced_change_start_page_entry_default">İlkin</string> <string name="revanced_change_start_page_entry_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">

View File

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

View File

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

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