mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-24 19:21:03 +00:00
Compare commits
63 Commits
v5.21.0-de
...
v5.24.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48d5fdf7e1 | ||
|
|
887c9f0d75 | ||
|
|
7de4c9d41d | ||
|
|
7d3b8d9c42 | ||
|
|
25e1a965d6 | ||
|
|
b29c01cee1 | ||
|
|
639850471b | ||
|
|
796c118fe1 | ||
|
|
edf20e397d | ||
|
|
5f0541407c | ||
|
|
56b7ba9ba7 | ||
|
|
f8bdf744ab | ||
|
|
f4f36ff273 | ||
|
|
5028c1acb3 | ||
|
|
555c9a5823 | ||
|
|
777957e2d0 | ||
|
|
b3316a5915 | ||
|
|
2ca2bb7692 | ||
|
|
23fd720fa7 | ||
|
|
1f08586ae8 | ||
|
|
60fdf4c44c | ||
|
|
63f3342815 | ||
|
|
858c59d728 | ||
|
|
5debf9936d | ||
|
|
f1b85d20a1 | ||
|
|
37d0de5e93 | ||
|
|
96d08d5eb7 | ||
|
|
9b1013e1c2 | ||
|
|
75d6cd7c7b | ||
|
|
5a17f5e1c1 | ||
|
|
1d16de6617 | ||
|
|
aee7cba46d | ||
|
|
ec3faf30a8 | ||
|
|
45b5a51da3 | ||
|
|
8abf176bc9 | ||
|
|
ef35ed7335 | ||
|
|
4fd666b667 | ||
|
|
72e0c01922 | ||
|
|
f69eab3e3b | ||
|
|
7c5c2d95bc | ||
|
|
b2453fecfc | ||
|
|
0d54f8bd80 | ||
|
|
fda16fad1a | ||
|
|
ddd43acd73 | ||
|
|
3451318d53 | ||
|
|
2d94ba9df6 | ||
|
|
aaf3437a5a | ||
|
|
ec8bf06047 | ||
|
|
96512de6c9 | ||
|
|
6114807c43 | ||
|
|
6d69f01421 | ||
|
|
fd4218154d | ||
|
|
8bed8a6622 | ||
|
|
3174047223 | ||
|
|
15053e2b68 | ||
|
|
e5b6aac018 | ||
|
|
d7c9dd0f77 | ||
|
|
a0eb6d5fdb | ||
|
|
55c5eb3d14 | ||
|
|
896de8910a | ||
|
|
e2a7e25c66 | ||
|
|
77ea5c4033 | ||
|
|
6eea2354f5 |
204
CHANGELOG.md
204
CHANGELOG.md
@@ -1,3 +1,207 @@
|
|||||||
|
# [5.24.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.23.0...v5.24.0-dev.1) (2025-05-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **NU.nl:** Support version `11.3.0` ([#4925](https://github.com/ReVanced/revanced-patches/issues/4925)) ([bedde60](https://github.com/ReVanced/revanced-patches/commit/bedde60fc1a52b0fd491174b3b5b887435eb621a))
|
||||||
|
|
||||||
|
# [5.23.0](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0) (2025-05-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Correct incorrect fingerprint ([c3bab89](https://github.com/ReVanced/revanced-patches/commit/c3bab89fc4189e38c10eee0caa36289de7e29dfa))
|
||||||
|
* Fix incorrect fingerprints ([#4917](https://github.com/ReVanced/revanced-patches/issues/4917)) ([49ca329](https://github.com/ReVanced/revanced-patches/commit/49ca3290a726cdba7bc9b62ffcd8d46e6f04778e))
|
||||||
|
* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([00aa200](https://github.com/ReVanced/revanced-patches/commit/00aa2000ba2eef15a0dd827c2bd84c2e85c412e0))
|
||||||
|
* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([7b43986](https://github.com/ReVanced/revanced-patches/commit/7b43986871a68e5cb43331d2fb2fdb9ef67438ad))
|
||||||
|
* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([bd53955](https://github.com/ReVanced/revanced-patches/commit/bd53955df738bb7b819eb91a3e776e9d2ca5c74a))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Lightroom:** Constrain patches to last working version ([efef03b](https://github.com/ReVanced/revanced-patches/commit/efef03b80da21552d0d8be6913faba64e4fb5ed1))
|
||||||
|
* **Pandora:** Add `Disable audio ads` and `Unlimited skips` patch ([#4841](https://github.com/ReVanced/revanced-patches/issues/4841)) ([0cf7a4c](https://github.com/ReVanced/revanced-patches/commit/0cf7a4c6be615ed0a52a6bacf87592f5f43ff575))
|
||||||
|
* **Prime Video:** Add `Skip ads` patch ([#4824](https://github.com/ReVanced/revanced-patches/issues/4824)) ([bb672c4](https://github.com/ReVanced/revanced-patches/commit/bb672c4674ddc201b8b2648c3906cfc31ef43f10))
|
||||||
|
* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([2e3511d](https://github.com/ReVanced/revanced-patches/commit/2e3511d03c8198bbdb9336888df038a33fb3ab8c))
|
||||||
|
|
||||||
|
# [5.23.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.6...v5.23.0-dev.7) (2025-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Fix incorrect fingerprints ([#4917](https://github.com/ReVanced/revanced-patches/issues/4917)) ([49ca329](https://github.com/ReVanced/revanced-patches/commit/49ca3290a726cdba7bc9b62ffcd8d46e6f04778e))
|
||||||
|
|
||||||
|
# [5.23.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.5...v5.23.0-dev.6) (2025-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Correct incorrect fingerprint ([c3bab89](https://github.com/ReVanced/revanced-patches/commit/c3bab89fc4189e38c10eee0caa36289de7e29dfa))
|
||||||
|
|
||||||
|
# [5.23.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.4...v5.23.0-dev.5) (2025-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Spotify - Unlock Spotify Premium:** Remove pop up premium ads ([#4842](https://github.com/ReVanced/revanced-patches/issues/4842)) ([00aa200](https://github.com/ReVanced/revanced-patches/commit/00aa2000ba2eef15a0dd827c2bd84c2e85c412e0))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Pandora:** Add `Disable audio ads` and `Unlimited skips` patch ([#4841](https://github.com/ReVanced/revanced-patches/issues/4841)) ([0cf7a4c](https://github.com/ReVanced/revanced-patches/commit/0cf7a4c6be615ed0a52a6bacf87592f5f43ff575))
|
||||||
|
* **Prime Video:** Add `Skip ads` patch ([#4824](https://github.com/ReVanced/revanced-patches/issues/4824)) ([bb672c4](https://github.com/ReVanced/revanced-patches/commit/bb672c4674ddc201b8b2648c3906cfc31ef43f10))
|
||||||
|
|
||||||
|
# [5.23.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.3...v5.23.0-dev.4) (2025-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Spotify:** Add `Sanitize sharing links` patch ([#4829](https://github.com/ReVanced/revanced-patches/issues/4829)) ([2e3511d](https://github.com/ReVanced/revanced-patches/commit/2e3511d03c8198bbdb9336888df038a33fb3ab8c))
|
||||||
|
|
||||||
|
# [5.23.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.2...v5.23.0-dev.3) (2025-05-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Simplify litho filtering patch ([#4910](https://github.com/ReVanced/revanced-patches/issues/4910)) ([bd53955](https://github.com/ReVanced/revanced-patches/commit/bd53955df738bb7b819eb91a3e776e9d2ca5c74a))
|
||||||
|
|
||||||
|
# [5.23.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.23.0-dev.1...v5.23.0-dev.2) (2025-05-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Improve litho filtering performance ([#4904](https://github.com/ReVanced/revanced-patches/issues/4904)) ([7b43986](https://github.com/ReVanced/revanced-patches/commit/7b43986871a68e5cb43331d2fb2fdb9ef67438ad))
|
||||||
|
|
||||||
|
# [5.23.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.22.0...v5.23.0-dev.1) (2025-05-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Lightroom:** Constrain patches to last working version ([efef03b](https://github.com/ReVanced/revanced-patches/commit/efef03b80da21552d0d8be6913faba64e4fb5ed1))
|
||||||
|
|
||||||
|
# [5.22.0](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0) (2025-05-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([c255ac1](https://github.com/ReVanced/revanced-patches/commit/c255ac18e0b2dcf917bd0559876be5a2a81023db))
|
||||||
|
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([f0c9c35](https://github.com/ReVanced/revanced-patches/commit/f0c9c35778ab43a99149ee5ad0ccfd8aeb09f638))
|
||||||
|
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([9dcd3d3](https://github.com/ReVanced/revanced-patches/commit/9dcd3d35dddf019547ab6ce431bac7a5a8a4c291))
|
||||||
|
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([06b35b2](https://github.com/ReVanced/revanced-patches/commit/06b35b2a7d7371915881e8f430c32ce15fa224de))
|
||||||
|
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f459c3c](https://github.com/ReVanced/revanced-patches/commit/f459c3c7fae3a1b8addf3354488dcef9f95255cc))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([f198bec](https://github.com/ReVanced/revanced-patches/commit/f198bece653e3e1adf083129dedb77c1d1a633d7))
|
||||||
|
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([ab4bdc8](https://github.com/ReVanced/revanced-patches/commit/ab4bdc8a2519cee15f79bf95d89e7ea56ea464ee))
|
||||||
|
|
||||||
|
# [5.22.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.3...v5.22.0-dev.4) (2025-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide layout components:** Hide new type of community posts ([#4888](https://github.com/ReVanced/revanced-patches/issues/4888)) ([f0c9c35](https://github.com/ReVanced/revanced-patches/commit/f0c9c35778ab43a99149ee5ad0ccfd8aeb09f638))
|
||||||
|
* **YouTube - Hide Shorts components:** Hide action buttons A/B button layout ([#4889](https://github.com/ReVanced/revanced-patches/issues/4889)) ([9dcd3d3](https://github.com/ReVanced/revanced-patches/commit/9dcd3d35dddf019547ab6ce431bac7a5a8a4c291))
|
||||||
|
|
||||||
|
# [5.22.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.2...v5.22.0-dev.3) (2025-04-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - GmsCore support:** Show troubleshooting in app text if the user recently changed their account details ([#4879](https://github.com/ReVanced/revanced-patches/issues/4879)) ([ab4bdc8](https://github.com/ReVanced/revanced-patches/commit/ab4bdc8a2519cee15f79bf95d89e7ea56ea464ee))
|
||||||
|
|
||||||
|
# [5.22.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.22.0-dev.1...v5.22.0-dev.2) (2025-04-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Shorts autoplay:** Fix autoplay with YT 20.12 ([06b35b2](https://github.com/ReVanced/revanced-patches/commit/06b35b2a7d7371915881e8f430c32ce15fa224de))
|
||||||
|
|
||||||
|
# [5.22.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.21.0...v5.22.0-dev.1) (2025-04-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok - Feed filter:** Hide ads in following feed ([#4844](https://github.com/ReVanced/revanced-patches/issues/4844)) ([c255ac1](https://github.com/ReVanced/revanced-patches/commit/c255ac18e0b2dcf917bd0559876be5a2a81023db))
|
||||||
|
* **YouTube - Spoof app version:** Do not hide spoof version in general settings menu ([#4861](https://github.com/ReVanced/revanced-patches/issues/4861)) ([f459c3c](https://github.com/ReVanced/revanced-patches/commit/f459c3c7fae3a1b8addf3354488dcef9f95255cc))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **TikTok - Feed Filter:** Remove TikTok Shop from feed. ([#4851](https://github.com/ReVanced/revanced-patches/issues/4851)) ([f198bec](https://github.com/ReVanced/revanced-patches/commit/f198bece653e3e1adf083129dedb77c1d1a633d7))
|
||||||
|
|
||||||
|
# [5.21.0](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0) (2025-04-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
|
||||||
|
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
|
||||||
|
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
|
||||||
|
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
|
||||||
|
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
|
||||||
|
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
|
||||||
|
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
|
||||||
|
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
|
||||||
|
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([655b390](https://github.com/ReVanced/revanced-patches/commit/655b39043ad77efcb4380de67c3f603666e7bc49))
|
||||||
|
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
|
||||||
|
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
|
||||||
|
|
||||||
|
# [5.21.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.11...v5.21.0-dev.12) (2025-04-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
|
||||||
|
|
||||||
|
# [5.21.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.10...v5.21.0-dev.11) (2025-04-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
|
||||||
|
|
||||||
|
# [5.21.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.9...v5.21.0-dev.10) (2025-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
|
||||||
|
|
||||||
|
# [5.21.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.8...v5.21.0-dev.9) (2025-04-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
|
||||||
|
|
||||||
|
# [5.21.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.7...v5.21.0-dev.8) (2025-04-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
|
||||||
|
|
||||||
|
# [5.21.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.6...v5.21.0-dev.7) (2025-04-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
|
||||||
|
|
||||||
|
# [5.21.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.5...v5.21.0-dev.6) (2025-04-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
|
||||||
|
|
||||||
# [5.21.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.4...v5.21.0-dev.5) (2025-04-18)
|
# [5.21.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.4...v5.21.0-dev.5) (2025-04-18)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class HideAdsPatch {
|
|||||||
|
|
||||||
// Filter HeaderBlock with known ads until next HeaderBlock.
|
// Filter HeaderBlock with known ads until next HeaderBlock.
|
||||||
if (currentBlock instanceof HeaderBlock headerBlock) {
|
if (currentBlock instanceof HeaderBlock headerBlock) {
|
||||||
StyledText headerText = headerBlock.component20();
|
StyledText headerText = headerBlock.getTitle();
|
||||||
if (headerText != null) {
|
if (headerText != null) {
|
||||||
skipFullHeader = false;
|
skipFullHeader = false;
|
||||||
for (String blockedHeaderBlock : blockedHeaderBlocks) {
|
for (String blockedHeaderBlock : blockedHeaderBlocks) {
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ package nl.nu.performance.api.client.objects;
|
|||||||
import nl.nu.performance.api.client.interfaces.Block;
|
import nl.nu.performance.api.client.interfaces.Block;
|
||||||
|
|
||||||
public class HeaderBlock extends Block {
|
public class HeaderBlock extends Block {
|
||||||
// returns title
|
public final StyledText getTitle() {
|
||||||
public final StyledText component20() {
|
|
||||||
throw new UnsupportedOperationException("Stub");
|
throw new UnsupportedOperationException("Stub");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
extensions/primevideo/build.gradle.kts
Normal file
4
extensions/primevideo/build.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
dependencies {
|
||||||
|
compileOnly(project(":extensions:shared:library"))
|
||||||
|
compileOnly(project(":extensions:primevideo:stub"))
|
||||||
|
}
|
||||||
1
extensions/primevideo/src/main/AndroidManifest.xml
Normal file
1
extensions/primevideo/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
extensions/primevideo/stub/build.gradle.kts
Normal file
17
extensions/primevideo/stub/build.gradle.kts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
1
extensions/primevideo/stub/src/main/AndroidManifest.xml
Normal file
1
extensions/primevideo/stub/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.amazon.avod.fsm;
|
||||||
|
|
||||||
|
public final class SimpleTrigger<T> implements Trigger<T> {
|
||||||
|
public SimpleTrigger(T triggerType) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.amazon.avod.fsm;
|
||||||
|
|
||||||
|
public interface Trigger<T> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.amazon.avod.media;
|
||||||
|
|
||||||
|
public final class TimeSpan {
|
||||||
|
public long getTotalMilliseconds() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.amazon.avod.media.ads;
|
||||||
|
|
||||||
|
import com.amazon.avod.media.TimeSpan;
|
||||||
|
|
||||||
|
public interface AdBreak {
|
||||||
|
TimeSpan getDurationExcludingAux();
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.amazon.avod.media.ads.internal.state;
|
||||||
|
|
||||||
|
public abstract class AdBreakState extends AdEnabledPlaybackState {
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.amazon.avod.media.ads.internal.state;
|
||||||
|
|
||||||
|
public enum AdEnabledPlayerTriggerType {
|
||||||
|
NO_MORE_ADS_SKIP_TRANSITION
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.amazon.avod.media.ads.internal.state;
|
||||||
|
|
||||||
|
public class ServerInsertedAdBreakState extends AdBreakState {
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.amazon.avod.media.playback;
|
||||||
|
|
||||||
|
public interface VideoPlayer {
|
||||||
|
long getCurrentPosition();
|
||||||
|
|
||||||
|
void seekTo(long positionMs);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.amazon.avod.media.playback.state;
|
||||||
|
|
||||||
|
public interface PlayerStateType {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.amazon.avod.media.playback.state.trigger;
|
||||||
|
|
||||||
|
public interface PlayerTriggerType {
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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));
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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() {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.21.0-dev.5
|
version = 5.24.0-dev.1
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import app.revanced.patcher.fingerprint
|
|||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal val jwUtilCreateAdvertisementFingerprint = fingerprint {
|
internal val jwPlayerConfigFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
accessFlags(AccessFlags.PUBLIC)
|
||||||
custom { methodDef, classDef ->
|
custom { methodDef, classDef ->
|
||||||
classDef.type == "Lnl/sanomamedia/android/nu/video/util/JWUtil;" && methodDef.name == "createAdvertising"
|
classDef.type == "Lcom/jwplayer/pub/api/configuration/PlayerConfig${'$'}Builder;" && methodDef.name == "advertisingConfig"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ package app.revanced.patches.nunl.ads
|
|||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@@ -11,23 +14,15 @@ val hideAdsPatch = bytecodePatch(
|
|||||||
name = "Hide ads",
|
name = "Hide ads",
|
||||||
description = "Hide ads and sponsored articles in list pages and remove pre-roll ads on videos.",
|
description = "Hide ads and sponsored articles in list pages and remove pre-roll ads on videos.",
|
||||||
) {
|
) {
|
||||||
compatibleWith("nl.sanomamedia.android.nu"("11.0.0", "11.0.1", "11.1.0"))
|
compatibleWith("nl.sanomamedia.android.nu"("11.3.0"))
|
||||||
|
|
||||||
dependsOn(sharedExtensionPatch("nunl", mainActivityOnCreateHook))
|
dependsOn(sharedExtensionPatch("nunl", mainActivityOnCreateHook))
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
// Disable video pre-roll ads.
|
// Disable video pre-roll ads.
|
||||||
// Whenever the app tries to create an ad via JWUtils.createAdvertising, don't actually tell the underlying JWPlayer library to do so => JWPlayer will not display ads.
|
// Whenever the app tries to define the advertising config for JWPlayer, don't set the advertising config and directly return.
|
||||||
jwUtilCreateAdvertisementFingerprint.method.addInstructions(
|
val iputInstructionIndex = jwPlayerConfigFingerprint.method.indexOfFirstInstructionOrThrow(Opcode.IPUT_OBJECT)
|
||||||
0,
|
jwPlayerConfigFingerprint.method.removeInstructions(iputInstructionIndex, 1)
|
||||||
"""
|
|
||||||
new-instance v0, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;
|
|
||||||
invoke-direct { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;-><init>()V
|
|
||||||
invoke-virtual { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;->build()Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
|
|
||||||
// Filter injected content from API calls out of lists.
|
// Filter injected content from API calls out of lists.
|
||||||
arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach {
|
arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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\""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package app.revanced.patches.pandora.shared
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
|
internal val constructUserDataFingerprint = fingerprint {
|
||||||
|
strings("hasAudioAds", "skipLimitBehavior")
|
||||||
|
}
|
||||||
@@ -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;"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package app.revanced.patches.primevideo.misc.extension
|
||||||
|
|
||||||
|
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||||
|
|
||||||
|
val sharedExtensionPatch = sharedExtensionPatch("primevideo", applicationInitHook)
|
||||||
@@ -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;")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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),
|
||||||
|
|||||||
@@ -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;")
|
||||||
|
|||||||
@@ -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;")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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;")
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,5 +68,5 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
dependsOn(settingsPatch, addResourcesPatch)
|
dependsOn(settingsPatch, addResourcesPatch, accountCredentialsInvalidTextPatch)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ GÃļzlÉnilmÉz hallardan xÉbÉrdar olmayacaqsÄąnÄąz."</string>
|
|||||||
âĸ Son xÉbÉrlÉr
|
âĸ Son xÉbÉrlÉr
|
||||||
âĸ İzlÉmÉyÉ davam et
|
âĸ İzlÉmÉyÉ davam et
|
||||||
âĸ Daha çox kanallar kÉÅf et
|
âĸ Daha çox kanallar kÉÅf et
|
||||||
âĸ AlÄąÅ-veriÅ
|
âĸ MaÄaza
|
||||||
âĸ TÉkrar izlÉ"</string>
|
âĸ TÉkrar izlÉ"</string>
|
||||||
<string name="revanced_hide_horizontal_shelves_summary_off">HissÉlÉr gÃļstÉrilir</string>
|
<string name="revanced_hide_horizontal_shelves_summary_off">HissÉlÉr gÃļstÉrilir</string>
|
||||||
<!-- 'Join' should be translated using the same localized wording YouTube displays.
|
<!-- 'Join' should be translated using the same localized wording YouTube displays.
|
||||||
@@ -355,7 +355,7 @@ Bu xÃŧsusiyyÉt yalnÄąz kÃļhnÉ cihazlar ÃŧçÃŧn mÃļvcuddur"</string>
|
|||||||
<string name="revanced_hide_buttoned_ads_title">DÃŧymÉli reklamlarÄą gizlÉt</string>
|
<string name="revanced_hide_buttoned_ads_title">DÃŧymÉli reklamlarÄą gizlÉt</string>
|
||||||
<string name="revanced_hide_buttoned_ads_summary_on">DÃŧymÉli reklamlar gizlÉdilir</string>
|
<string name="revanced_hide_buttoned_ads_summary_on">DÃŧymÉli reklamlar gizlÉdilir</string>
|
||||||
<string name="revanced_hide_buttoned_ads_summary_off">DÃŧymÉli reklamlar gÃļstÉrilir</string>
|
<string name="revanced_hide_buttoned_ads_summary_off">DÃŧymÉli reklamlar gÃļstÉrilir</string>
|
||||||
<string name="revanced_hide_paid_promotion_label_title">ÃdÉniÅli reklam etiketini gizlÉt</string>
|
<string name="revanced_hide_paid_promotion_label_title">ÃdÉniÅli tanÄątÄąm etiketini gizlÉt</string>
|
||||||
<string name="revanced_hide_paid_promotion_label_summary_on">ÃdÉniÅli reklam etiketi gizlÉdilib</string>
|
<string name="revanced_hide_paid_promotion_label_summary_on">ÃdÉniÅli reklam etiketi gizlÉdilib</string>
|
||||||
<string name="revanced_hide_paid_promotion_label_summary_off">ÃdÉniÅli reklam etiketi gÃļstÉrilir</string>
|
<string name="revanced_hide_paid_promotion_label_summary_off">ÃdÉniÅli reklam etiketi gÃļstÉrilir</string>
|
||||||
<string name="revanced_hide_self_sponsor_ads_title">Ãz-sponsorlu kartlarÄą gizlÉt</string>
|
<string name="revanced_hide_self_sponsor_ads_title">Ãz-sponsorlu kartlarÄą gizlÉt</string>
|
||||||
@@ -367,14 +367,14 @@ Bu xÃŧsusiyyÉt yalnÄąz kÃļhnÉ cihazlar ÃŧçÃŧn mÃļvcuddur"</string>
|
|||||||
<string name="revanced_hide_end_screen_store_banner_title">Son ekran maÄaza etiketini gizlÉt</string>
|
<string name="revanced_hide_end_screen_store_banner_title">Son ekran maÄaza etiketini gizlÉt</string>
|
||||||
<string name="revanced_hide_end_screen_store_banner_summary_on">MaÄaza etiketi gizlidir</string>
|
<string name="revanced_hide_end_screen_store_banner_summary_on">MaÄaza etiketi gizlidir</string>
|
||||||
<string name="revanced_hide_end_screen_store_banner_summary_off">MaÄaza etiketi gÃļrÃŧnÃŧr</string>
|
<string name="revanced_hide_end_screen_store_banner_summary_off">MaÄaza etiketi gÃļrÃŧnÃŧr</string>
|
||||||
<string name="revanced_hide_player_store_shelf_title">OynadÄącÄą alÄąÅ-veriÅ bÃļlmÉsin gizlÉt</string>
|
<string name="revanced_hide_player_store_shelf_title">OynadÄącÄą maÄaza bÃļlmÉsin gizlÉt</string>
|
||||||
<string name="revanced_hide_player_store_shelf_summary_on">AlÄąÅ-veriÅ rÉfi gizlidir</string>
|
<string name="revanced_hide_player_store_shelf_summary_on">AlÄąÅ-veriÅ rÉfi gizlidir</string>
|
||||||
<string name="revanced_hide_player_store_shelf_summary_off">AlÄąÅ-veriÅ rÉfi gÃļstÉrilir</string>
|
<string name="revanced_hide_player_store_shelf_summary_off">AlÄąÅ-veriÅ rÉfi gÃļstÉrilir</string>
|
||||||
<string name="revanced_hide_shopping_links_title">Video aÃ§Äąqlamada alÄąÅ-veriÅ linklÉrin gizlÉ</string>
|
<string name="revanced_hide_shopping_links_title">Video tÉsvirdÉ maÄaza linklÉrin gizlÉ</string>
|
||||||
<string name="revanced_hide_shopping_links_summary_on">Video tÉsvirindÉ alÄąÅ-veriÅ linklÉri gizlÉdilib</string>
|
<string name="revanced_hide_shopping_links_summary_on">Video tÉsvirindÉ alÄąÅ-veriÅ linklÉri gizlÉdilib</string>
|
||||||
<string name="revanced_hide_shopping_links_summary_off">Video tÉsvirindÉ alÄąÅ-veriÅ linklÉri gÃļrÃŧnÃŧr</string>
|
<string name="revanced_hide_shopping_links_summary_off">Video tÉsvirindÉ alÄąÅ-veriÅ linklÉri gÃļrÃŧnÃŧr</string>
|
||||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||||
<string name="revanced_hide_visit_store_button_title">Kanalda \"MaÄazaya ziyarÉt\" dÃŧymÉsin gizlÉ</string>
|
<string name="revanced_hide_visit_store_button_title">Kanalda \"MaÄazaya baxÄąn\" dÃŧymÉsin gizlÉ</string>
|
||||||
<string name="revanced_hide_visit_store_button_summary_on">Kanal sÉhifÉsindÉki dÃŧymÉ gizlidir</string>
|
<string name="revanced_hide_visit_store_button_summary_on">Kanal sÉhifÉsindÉki dÃŧymÉ gizlidir</string>
|
||||||
<string name="revanced_hide_visit_store_button_summary_off">Kanal sÉhifÉsindÉki dÃŧymÉ gÃļrÃŧnÃŧr</string>
|
<string name="revanced_hide_visit_store_button_summary_off">Kanal sÉhifÉsindÉki dÃŧymÉ gÃļrÃŧnÃŧr</string>
|
||||||
<string name="revanced_hide_web_search_results_title">Veb axtarÄąÅ nÉticÉlÉrini gizlÉt</string>
|
<string name="revanced_hide_web_search_results_title">Veb axtarÄąÅ nÉticÉlÉrini gizlÉt</string>
|
||||||
@@ -459,27 +459,38 @@ EkranÄąn saÄ tÉrÉfindÉ dÃŧzÃŧnÉ sÃŧrÃŧÅdÃŧrÉrÉk sÉs sÉviyyÉsini tÉnz
|
|||||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Avto-parlaqlÄąq jestini aktivlÉÅdir</string>
|
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">Avto-parlaqlÄąq jestini aktivlÉÅdir</string>
|
||||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">ParlaqlÄąq Én aÅaÄÄą dÉyÉrinÉ sÃŧrÃŧÅdÃŧrÃŧlÉndÉ avto-parlaqlÄąq aktivlÉÅir</string>
|
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">ParlaqlÄąq Én aÅaÄÄą dÉyÉrinÉ sÃŧrÃŧÅdÃŧrÃŧlÉndÉ avto-parlaqlÄąq aktivlÉÅir</string>
|
||||||
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">ParlaqlÄąÄÄą Én aÅaÄÄą dÉyÉrÉ sÃŧrÃŧÅdÃŧrÉndÉ avto-parlaqlÄąq aktivlÉÅmir</string>
|
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">ParlaqlÄąÄÄą Én aÅaÄÄą dÉyÉrÉ sÃŧrÃŧÅdÃŧrÉndÉ avto-parlaqlÄąq aktivlÉÅmir</string>
|
||||||
|
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">Avtomatik</string>
|
||||||
<string name="revanced_swipe_overlay_timeout_title">SÃŧrÃŧÅdÃŧrmÉ ÃļrtÃŧyÃŧ mÃŧddÉti</string>
|
<string name="revanced_swipe_overlay_timeout_title">SÃŧrÃŧÅdÃŧrmÉ ÃļrtÃŧyÃŧ mÃŧddÉti</string>
|
||||||
<string name="revanced_swipe_overlay_timeout_summary">ÃrtÃŧyÃŧn gÃļrÃŧndÃŧyÃŧ millisaniyÉlÉrin sayÄą</string>
|
<string name="revanced_swipe_overlay_timeout_summary">ÃrtÃŧyÃŧn gÃļrÃŧndÃŧyÃŧ millisaniyÉlÉrin sayÄą</string>
|
||||||
<string name="revanced_swipe_overlay_background_opacity_title">SÃŧrÃŧÅdÃŧrmÉ cildi arxa plan qeyri-ÅÉffaflÄąÄÄą</string>
|
<string name="revanced_swipe_overlay_background_opacity_title">SÃŧrÃŧÅdÃŧrmÉ cildi arxa plan qeyri-ÅÉffaflÄąÄÄą</string>
|
||||||
<string name="revanced_swipe_overlay_background_opacity_summary">0-100 arasÄą qeyri-ÅÉffaflÄąq dÉyÉri</string>
|
<string name="revanced_swipe_overlay_background_opacity_summary">0-100 arasÄą qeyri-ÅÉffaflÄąq dÉyÉri</string>
|
||||||
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">SÃŧrÃŧÅmÉ qeyri-ÅÉffaflÄąÄÄą 0-100 arasÄą olmalÄądÄąr</string>
|
<string name="revanced_swipe_overlay_background_opacity_invalid_toast">SÃŧrÃŧÅmÉ qeyri-ÅÉffaflÄąÄÄą 0-100 arasÄą olmalÄądÄąr</string>
|
||||||
|
<string name="revanced_swipe_overlay_progress_color_title">SÃŧrÃŧÅdÃŧrmÉ ÃļrtÃŧyÃŧ irÉlilÉyiÅ cizgisi rÉngi</string>
|
||||||
|
<string name="revanced_swipe_overlay_progress_color_summary">SÉs sÉviyyÉsinÉ vÉ parlaqlÄąÄa nÉzarÉt ÃŧçÃŧn irÉlilÉyiÅ cizgisi rÉngi</string>
|
||||||
|
<string name="revanced_swipe_overlay_progress_color_invalid_toast">YanlÄąÅ irÉlilÉyiÅ cizgisi rÉngi</string>
|
||||||
|
<string name="revanced_swipe_text_overlay_size_title">SÃŧrÃŧÅdÃŧrmÉ ÃļrtÃŧyÃŧ mÉtn ÃļlçÃŧsÃŧ</string>
|
||||||
|
<string name="revanced_swipe_text_overlay_size_summary">SÃŧrÃŧÅmÉ ÃŧçÃŧn mÉtn ÃļlçÃŧsÃŧ 1-30 arasÄąndadÄąr</string>
|
||||||
|
<string name="revanced_swipe_text_overlay_size_invalid_toast">MÉtn ÃļlçÃŧsÃŧ 1-30 arasÄą olmalÄądÄąr</string>
|
||||||
<string name="revanced_swipe_threshold_title">SÃŧrÃŧÅdÃŧrmÉ bÃļyÃŧklÃŧk hÉddi</string>
|
<string name="revanced_swipe_threshold_title">SÃŧrÃŧÅdÃŧrmÉ bÃļyÃŧklÃŧk hÉddi</string>
|
||||||
<string name="revanced_swipe_threshold_summary">SÃŧrÃŧÅdÃŧrmÉnin icra edilmÉsi ÃŧçÃŧn son dÉyÉr</string>
|
<string name="revanced_swipe_threshold_summary">SÃŧrÃŧÅdÃŧrmÉnin icra edilmÉsi ÃŧçÃŧn son dÉyÉr</string>
|
||||||
<string name="revanced_swipe_volume_sensitivity_title">SÉs sÉviyyÉsin sÃŧrÃŧÅdÃŧrmÉ tÉzyiqi</string>
|
<string name="revanced_swipe_volume_sensitivity_title">SÉs sÉviyyÉsin sÃŧrÃŧÅdÃŧrmÉ tÉzyiqi</string>
|
||||||
<string name="revanced_swipe_volume_sensitivity_summary">HÉr sÃŧrÃŧÅdÃŧrmÉdÉ sÉs sÉviyyÉsi nÉ qÉdÉr dÉyiÅir</string>
|
<string name="revanced_swipe_volume_sensitivity_summary">HÉr sÃŧrÃŧÅdÃŧrmÉdÉ sÉs sÉviyyÉsi nÉ qÉdÉr dÉyiÅir</string>
|
||||||
<string name="revanced_swipe_show_circular_overlay_title">DairÉvi ÃļrtÃŧyÃŧ gÃļstÉr</string>
|
<string name="revanced_swipe_overlay_style_title">SÃŧrÃŧÅmÉ ÃļrtÃŧyÃŧ Ãŧslubu</string>
|
||||||
<string name="revanced_swipe_show_circular_overlay_summary_on">DairÉvi ÃļrtÃŧk gÃļstÉrilir</string>
|
<string name="revanced_swipe_overlay_style_entry_1">ÃfÃŧqi ÃļrtÃŧk</string>
|
||||||
<string name="revanced_swipe_show_circular_overlay_summary_off">DÃŧzÃŧnÉ ÃļrtÃŧk gÃļstÉrilir</string>
|
<string name="revanced_swipe_overlay_style_entry_2">ÃfÃŧqi ÃļrtÃŧk (Én kiçik- yÃŧksÉk)</string>
|
||||||
<string name="revanced_swipe_overlay_minimal_style_title">Æn kiçik Ãŧslubu aktivlÉÅdir</string>
|
<string name="revanced_swipe_overlay_style_entry_3">ÃfÃŧqi ÃļrtÃŧk (Én kiçik - mÉrkÉz)</string>
|
||||||
<string name="revanced_swipe_overlay_minimal_style_summary_on">Æn kiçik ÃļrtÃŧk Ãŧslubu aktivlÉÅdirilib</string>
|
<string name="revanced_swipe_overlay_style_entry_4">DairÉvi ÃļrtÃŧk</string>
|
||||||
<string name="revanced_swipe_overlay_minimal_style_summary_off">Æn kiçik ÃļrtÃŧk Ãŧslubu qapalÄądÄąr</string>
|
<string name="revanced_swipe_overlay_style_entry_5">DairÉvi ÃļrtÃŧk (Én kiçik)</string>
|
||||||
|
<string name="revanced_swipe_overlay_style_entry_6">Åaquli ÃļrtÃŧk</string>
|
||||||
|
<string name="revanced_swipe_overlay_style_entry_7">Åaquli ÃļrtÃŧk (Én kiçik)</string>
|
||||||
<string name="revanced_swipe_change_video_title">VideolarÄą ÃļtÃŧrmÉk ÃŧçÃŧn sÃŧrÃŧÅdÃŧrmÉni aktiv et</string>
|
<string name="revanced_swipe_change_video_title">VideolarÄą ÃļtÃŧrmÉk ÃŧçÃŧn sÃŧrÃŧÅdÃŧrmÉni aktiv et</string>
|
||||||
<string name="revanced_swipe_change_video_summary_on">Tam ekran rejimindÉ sÃŧrÃŧÅdÃŧrmÉ nÃļvbÉti/ÉvvÉlki videoya ÃļtÃŧrÉcÉk</string>
|
<string name="revanced_swipe_change_video_summary_on">Tam ekran rejimindÉ sÃŧrÃŧÅdÃŧrmÉ nÃļvbÉti/ÉvvÉlki videoya ÃļtÃŧrÉcÉk</string>
|
||||||
<string name="revanced_swipe_change_video_summary_off">Tam ekran rejimindÉ sÃŧrÃŧÅdÃŧrmÉ nÃļvbÉti/ÉvvÉlki videoya ÃļtÃŧrmÉyÉcÉk</string>
|
<string name="revanced_swipe_change_video_summary_off">Tam ekran rejimindÉ sÃŧrÃŧÅdÃŧrmÉ nÃļvbÉti/ÉvvÉlki videoya ÃļtÃŧrmÉyÉcÉk</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.autocaptions.autoCaptionsPatch">
|
<patch id="layout.autocaptions.autoCaptionsPatch">
|
||||||
<string name="revanced_disable_auto_captions_title">Avtomatik titrlÉri qeyri-aktiv et</string>
|
<string name="revanced_disable_auto_captions_title">Avtomatik titrlÉri qapat</string>
|
||||||
|
<string name="revanced_disable_auto_captions_summary_on">Avtomatik titrlÉr qapalÄądÄąr</string>
|
||||||
|
<string name="revanced_disable_auto_captions_summary_off">Avtomatik titrlÉr aktivdir</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.buttons.action.hideButtonsPatch">
|
<patch id="layout.buttons.action.hideButtonsPatch">
|
||||||
<string name="revanced_hide_buttons_screen_title">FÉaliyyÉt dÃŧymÉlÉri</string>
|
<string name="revanced_hide_buttons_screen_title">FÉaliyyÉt dÃŧymÉlÉri</string>
|
||||||
@@ -511,6 +522,11 @@ EkranÄąn saÄ tÉrÉfindÉ dÃŧzÃŧnÉ sÃŧrÃŧÅdÃŧrÉrÉk sÉs sÉviyyÉsini tÉnz
|
|||||||
<string name="revanced_hide_thanks_button_title">\"TÉÅÉkkÃŧrlÉr\"i gizlÉt</string>
|
<string name="revanced_hide_thanks_button_title">\"TÉÅÉkkÃŧrlÉr\"i gizlÉt</string>
|
||||||
<string name="revanced_hide_thanks_button_summary_on">TÉÅÉkkÃŧr dÃŧymÉsi gizlidir</string>
|
<string name="revanced_hide_thanks_button_summary_on">TÉÅÉkkÃŧr dÃŧymÉsi gizlidir</string>
|
||||||
<string name="revanced_hide_thanks_button_summary_off">TÉÅÉkkÃŧr dÃŧymÉsi gÃļstÉrilir</string>
|
<string name="revanced_hide_thanks_button_summary_off">TÉÅÉkkÃŧr dÃŧymÉsi gÃļstÉrilir</string>
|
||||||
|
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||||
|
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||||
|
<string name="revanced_hide_ask_button_title">SoruÅ\'u GizlÉt</string>
|
||||||
|
<string name="revanced_hide_ask_button_summary_on">SoruÅ dÃŧymÉsi gizlidir</string>
|
||||||
|
<string name="revanced_hide_ask_button_summary_off">\"SoruÅ\" dÃŧymÉsi gÃļstÉrilir</string>
|
||||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||||
<string name="revanced_hide_clip_button_title">KÉsmÉ/ gizlÉt</string>
|
<string name="revanced_hide_clip_button_title">KÉsmÉ/ gizlÉt</string>
|
||||||
<string name="revanced_hide_clip_button_summary_on">KÉsmÉ dÃŧymÉsi gizlidir</string>
|
<string name="revanced_hide_clip_button_summary_on">KÉsmÉ dÃŧymÉsi gizlidir</string>
|
||||||
@@ -829,6 +845,8 @@ MÉhdudiyyÉt: BÉyÉnmÉmÉlÉr gizli rejimdÉ gÃļrÃŧnmÉyÉ bilÉr"</string>
|
|||||||
<string name="revanced_ryd_compact_layout_summary_on">Daha kiçik en ÃŧçÃŧn hazÄąrlanmÄąÅ BÉyÉn dÃŧymÉsi</string>
|
<string name="revanced_ryd_compact_layout_summary_on">Daha kiçik en ÃŧçÃŧn hazÄąrlanmÄąÅ BÉyÉn dÃŧymÉsi</string>
|
||||||
<string name="revanced_ryd_compact_layout_summary_off">Æn yaxÅÄą gÃļrÃŧnÃŧÅ ÃŧçÃŧn tÉrtib edilmiÅ BÉyÉn dÃŧymÉsi</string>
|
<string name="revanced_ryd_compact_layout_summary_off">Æn yaxÅÄą gÃļrÃŧnÃŧÅ ÃŧçÃŧn tÉrtib edilmiÅ BÉyÉn dÃŧymÉsi</string>
|
||||||
<string name="revanced_ryd_estimated_like_title">TÉxmini bÉyÉnmÉlÉri gÃļstÉr</string>
|
<string name="revanced_ryd_estimated_like_title">TÉxmini bÉyÉnmÉlÉri gÃļstÉr</string>
|
||||||
|
<string name="revanced_ryd_estimated_like_summary_on">BÉyÉnmÉlÉri olmayan videolar tÉxmini bÉyÉnmÉ sayÄąnÄą gÃļstÉrir</string>
|
||||||
|
<string name="revanced_ryd_estimated_like_summary_off">TÉxmini bÉyÉnmÉlÉr gÃļstÉrilmir</string>
|
||||||
<string name="revanced_ryd_toast_on_connection_error_title">API Élçatan deyilsÉ ani bildiriÅ gÃļstÉr</string>
|
<string name="revanced_ryd_toast_on_connection_error_title">API Élçatan deyilsÉ ani bildiriÅ gÃļstÉr</string>
|
||||||
<string name="revanced_ryd_toast_on_connection_error_summary_on">Return YouTube Dislike Élçatan deyilsÉ ani bildiriÅ gÃļstÉr</string>
|
<string name="revanced_ryd_toast_on_connection_error_summary_on">Return YouTube Dislike Élçatan deyilsÉ ani bildiriÅ gÃļstÉr</string>
|
||||||
<string name="revanced_ryd_toast_on_connection_error_summary_off">Return YouTube Dislike Élçatan deyilsÉ ani bildiriÅ gÃļstÉrmÉ</string>
|
<string name="revanced_ryd_toast_on_connection_error_summary_off">Return YouTube Dislike Élçatan deyilsÉ ani bildiriÅ gÃļstÉrmÉ</string>
|
||||||
@@ -1102,7 +1120,7 @@ Sonradan qapadÄąlarsa, UI sÉhvlÉrin ÃļnlÉmÉk ÃŧçÃŧn tÉtbiq mÉlumatlarÄąn
|
|||||||
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - KÃļhnÉ fÉaliyyÉt simvollarÄąn bÉrpa et</string>
|
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - KÃļhnÉ fÉaliyyÉt simvollarÄąn bÉrpa et</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.startpage.changeStartPagePatch">
|
<patch id="layout.startpage.changeStartPagePatch">
|
||||||
<string name="revanced_change_start_page_title">BaÅlanÄÄąc sÉhifÉsini tÉyin et</string>
|
<string name="revanced_change_start_page_title">BaÅlatma sÉhifÉsini dÉyiÅdir</string>
|
||||||
<string name="revanced_change_start_page_entry_default">İlkin</string>
|
<string name="revanced_change_start_page_entry_default">İlkin</string>
|
||||||
<string name="revanced_change_start_page_entry_all_subscriptions">BÃŧtÃŧn abunÉliklÉr</string>
|
<string name="revanced_change_start_page_entry_all_subscriptions">BÃŧtÃŧn abunÉliklÉr</string>
|
||||||
<string name="revanced_change_start_page_entry_browse">Kanallara nÉzÉr yetir</string>
|
<string name="revanced_change_start_page_entry_browse">Kanallara nÉzÉr yetir</string>
|
||||||
@@ -1120,13 +1138,18 @@ Sonradan qapadÄąlarsa, UI sÉhvlÉrin ÃļnlÉmÉk ÃŧçÃŧn tÉtbiq mÉlumatlarÄąn
|
|||||||
<string name="revanced_change_start_page_entry_notifications">BildiriÅlÉr</string>
|
<string name="revanced_change_start_page_entry_notifications">BildiriÅlÉr</string>
|
||||||
<string name="revanced_change_start_page_entry_playlists">PleylistlÉr</string>
|
<string name="revanced_change_start_page_entry_playlists">PleylistlÉr</string>
|
||||||
<string name="revanced_change_start_page_entry_search">AxtarÄąÅ</string>
|
<string name="revanced_change_start_page_entry_search">AxtarÄąÅ</string>
|
||||||
<string name="revanced_change_start_page_entry_shopping">AlÄąÅ-VeriÅ</string>
|
<string name="revanced_change_start_page_entry_shopping">MaÄaza</string>
|
||||||
<string name="revanced_change_start_page_entry_sports">İdman</string>
|
<string name="revanced_change_start_page_entry_sports">İdman</string>
|
||||||
<string name="revanced_change_start_page_entry_subscriptions">AbunÉliklÉr</string>
|
<string name="revanced_change_start_page_entry_subscriptions">AbunÉliklÉr</string>
|
||||||
<string name="revanced_change_start_page_entry_trending">TrendlÉr</string>
|
<string name="revanced_change_start_page_entry_trending">TrendlÉr</string>
|
||||||
<string name="revanced_change_start_page_entry_virtual_reality">Faktiki HÉyat</string>
|
<string name="revanced_change_start_page_entry_virtual_reality">Faktiki HÉyat</string>
|
||||||
<string name="revanced_change_start_page_entry_watch_later">Sonra izlÉ</string>
|
<string name="revanced_change_start_page_entry_watch_later">Sonra izlÉ</string>
|
||||||
<string name="revanced_change_start_page_entry_your_clips">KliplÉriniz</string>
|
<string name="revanced_change_start_page_entry_your_clips">KliplÉriniz</string>
|
||||||
|
<string name="revanced_change_start_page_always_title"> BaÅlatma sÉhifÉsini hÉmiÅÉ dÉyiÅdir</string>
|
||||||
|
<string name="revanced_change_start_page_always_summary_on">"BaÅlatma sÉhifÉsi hÉmiÅÉ dÉyiÅdirilir
|
||||||
|
|
||||||
|
MÉhdudiyyÉt: AlÉtlÉr cizgisindÉki geri dÃŧymÉsin istifadÉ iÅlÉmÉyÉ bilÉr"</string>
|
||||||
|
<string name="revanced_change_start_page_always_summary_off">BaÅlatma sÉhifÉsi yalnÄąz tÉtbiq iÅÉ salÄąndÄąqda dÉyiÅdirilir</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
|
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
|
||||||
<string name="revanced_disable_resuming_shorts_player_title">Shorts oynadÄącÄą baÅladÄącÄąnÄą qapat</string>
|
<string name="revanced_disable_resuming_shorts_player_title">Shorts oynadÄącÄą baÅladÄącÄąnÄą qapat</string>
|
||||||
@@ -1232,7 +1255,7 @@ Bunu aktivlÉÅdirmÉ, bÉzi regionlarda ÉngÉllÉnib silinÉn ÅÉkillÉri dÃŧ
|
|||||||
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & Orijinal miniatÃŧrlÉr</string>
|
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow & Orijinal miniatÃŧrlÉr</string>
|
||||||
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & Kadr çÉkiliÅlÉr</string>
|
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow & 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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ Second \"item\" text"</string>
|
|||||||
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
|
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
|
||||||
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
|
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
|
||||||
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
|
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
|
||||||
|
<!-- 'Ask' should be translated with the same localized wording that YouTube displays.
|
||||||
|
Button only shows if the user ip is from specific region such as the USA or EU. -->
|
||||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||||
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
|
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
|
||||||
</patch>
|
</patch>
|
||||||
@@ -195,6 +197,8 @@ Second \"item\" text"</string>
|
|||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="misc.gms.accountCredentialsInvalidTextPatch">
|
||||||
|
</patch>
|
||||||
<patch id="misc.links.bypassURLRedirectsPatch">
|
<patch id="misc.links.bypassURLRedirectsPatch">
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.links.openLinksExternallyPatch">
|
<patch id="misc.links.openLinksExternallyPatch">
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user