mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 09:53:55 +01:00
Compare commits
230 Commits
v5.14.0-de
...
v5.24.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2593c004f4 | ||
|
|
db68c41d5e | ||
|
|
a4f9cb3cef | ||
|
|
9aec1999bb | ||
|
|
26ecbe646e | ||
|
|
46ba0d8a2e | ||
|
|
f454183646 | ||
|
|
d2b440d800 | ||
|
|
494c5f04a4 | ||
|
|
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 | ||
|
|
cce21c4d4a | ||
|
|
5e069bde90 | ||
|
|
6a49208982 | ||
|
|
404bb2e86e | ||
|
|
bc869fe359 | ||
|
|
7d166cf82c | ||
|
|
8efbaae65c | ||
|
|
e27ab23279 | ||
|
|
ce42604083 | ||
|
|
fc6282d0cb | ||
|
|
0559fc7fd0 | ||
|
|
7cc6995682 | ||
|
|
476f13bf98 | ||
|
|
f216e16c0b | ||
|
|
f2a8789649 | ||
|
|
5973b64f52 | ||
|
|
102036706e | ||
|
|
2393d0a8f5 | ||
|
|
aea29b9522 | ||
|
|
4db8ef7079 | ||
|
|
7fbd26ccad | ||
|
|
91995ea01d | ||
|
|
86f867fe97 | ||
|
|
0f687ecfd3 | ||
|
|
6c8b7d09c1 | ||
|
|
3d6958f157 | ||
|
|
43d7cc7374 | ||
|
|
5ebd449f1f | ||
|
|
346a061df8 | ||
|
|
13e490a422 | ||
|
|
b4e8540bbc | ||
|
|
775c1baec2 | ||
|
|
9419fb8ec4 | ||
|
|
c510931eb0 | ||
|
|
7160699384 | ||
|
|
9db67a6eb2 | ||
|
|
e684d87dd3 | ||
|
|
2d1752a1eb | ||
|
|
c9ff7092fe | ||
|
|
d451bc6d6d | ||
|
|
741fd36872 | ||
|
|
517f8cf59a | ||
|
|
b78fb24435 | ||
|
|
a3faccb21b | ||
|
|
5f0fddc122 | ||
|
|
854a18ff72 | ||
|
|
b994a16bdc | ||
|
|
f68d06dbf3 | ||
|
|
04c6a2e5f4 | ||
|
|
e6ae55fa99 | ||
|
|
fb62474ff4 | ||
|
|
e084f01fd0 | ||
|
|
d573386e0f | ||
|
|
0f3aeb35e5 | ||
|
|
e30f593af0 | ||
|
|
df965b8a9b | ||
|
|
654587a75e | ||
|
|
9956833781 | ||
|
|
c585b26188 | ||
|
|
de0d11fcfb | ||
|
|
d321504fcf | ||
|
|
6005c97bf5 | ||
|
|
e404d84c83 | ||
|
|
1abed31968 | ||
|
|
a75a88d3c6 | ||
|
|
3d67d90473 | ||
|
|
fa1e137a43 | ||
|
|
ac71a53c73 | ||
|
|
0bff207efc | ||
|
|
e1a8b388a5 | ||
|
|
628d18489c | ||
|
|
36772b8b2e | ||
|
|
49c849979f | ||
|
|
0bdb8cdf2b | ||
|
|
2035c9e2e9 | ||
|
|
7cb38fd3fc | ||
|
|
8ed9d5bf08 | ||
|
|
cd467d6244 | ||
|
|
fdefb67d02 | ||
|
|
5274cd18f0 | ||
|
|
3d68c06146 | ||
|
|
ef3d5bafd5 | ||
|
|
2d7b1b09af | ||
|
|
0572d48fde | ||
|
|
37984b8b99 | ||
|
|
6e63193f06 | ||
|
|
b2384b22a5 | ||
|
|
ccb76983ff | ||
|
|
318b55b8fe | ||
|
|
49ade9efbc | ||
|
|
d77515bd68 | ||
|
|
087bf1e152 | ||
|
|
c2994d583d | ||
|
|
127b0a63fe | ||
|
|
27aafd0ee1 | ||
|
|
49c54c0e54 | ||
|
|
842ba4fc4d | ||
|
|
66ecadce4f | ||
|
|
73ca04da5e | ||
|
|
a5d26208c1 | ||
|
|
497291c478 | ||
|
|
b24278a544 | ||
|
|
135f9ead3c | ||
|
|
ca4f960171 | ||
|
|
7f228cc535 | ||
|
|
bf91e127d8 | ||
|
|
f07fc1ad93 | ||
|
|
c84be120bd | ||
|
|
e67f390e2b | ||
|
|
4d910fea93 | ||
|
|
72adbe5519 | ||
|
|
54d49b774e | ||
|
|
283bb31567 | ||
|
|
2724fcbd27 | ||
|
|
7c28193579 | ||
|
|
cd1ee814c4 | ||
|
|
d9ccd73b5f | ||
|
|
5c5a1e4b8b | ||
|
|
66a2ee2416 | ||
|
|
d8c276cf96 | ||
|
|
d5845abd08 | ||
|
|
54eef22ce7 | ||
|
|
e287bdc59d | ||
|
|
20a82ef956 | ||
|
|
1e29da9e06 | ||
|
|
56e6a90a90 | ||
|
|
76d32e21c2 | ||
|
|
54a7afa540 | ||
|
|
ef86438bac | ||
|
|
0683cedac0 | ||
|
|
35753410aa | ||
|
|
df838ed91d | ||
|
|
8e494d26d4 | ||
|
|
7d834e5421 | ||
|
|
60a31cf4e1 | ||
|
|
edb8bd66bc | ||
|
|
04a170054e | ||
|
|
79e6349a69 | ||
|
|
bbf3a34a2f | ||
|
|
1db7c49514 | ||
|
|
ef0506a4f8 | ||
|
|
9b38da35ff | ||
|
|
afdb771066 | ||
|
|
1b2b536d2e | ||
|
|
f39e70c648 | ||
|
|
556acdd9c1 | ||
|
|
7adfc637dc | ||
|
|
9cc0c075ad | ||
|
|
ead11e7f46 | ||
|
|
e9bc201641 | ||
|
|
99baedf355 | ||
|
|
0338d0acd3 | ||
|
|
99879f6e0a | ||
|
|
f0c70de602 | ||
|
|
737ae07a06 | ||
|
|
2c51de59de | ||
|
|
df3dc1c0b2 | ||
|
|
074c948581 |
2
.github/workflows/pull_strings.yml
vendored
2
.github/workflows/pull_strings.yml
vendored
@@ -2,7 +2,7 @@ name: Pull strings
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */8 * * *"
|
||||
- cron: "0 */12 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
760
CHANGELOG.md
760
CHANGELOG.md
@@ -1,3 +1,763 @@
|
||||
# [5.24.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.24.0-dev.3...v5.24.0-dev.4) (2025-05-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add `Fix third party launchers widgets` patch ([#4893](https://github.com/ReVanced/revanced-patches/issues/4893)) ([23bfdc9](https://github.com/ReVanced/revanced-patches/commit/23bfdc98fbbcc8ecf0ffbf8704f58dd2272e4af2))
|
||||
|
||||
# [5.24.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.24.0-dev.2...v5.24.0-dev.3) (2025-05-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Fix `Hide video recommendation labels` ([#4956](https://github.com/ReVanced/revanced-patches/issues/4956)) ([ae05ac3](https://github.com/ReVanced/revanced-patches/commit/ae05ac38151ebd3197953af97ca0dd847a04cc2d))
|
||||
|
||||
# [5.24.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.24.0-dev.1...v5.24.0-dev.2) (2025-05-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **GmsCore support:** Open vendor specific DontKillMyApp if available ([#4952](https://github.com/ReVanced/revanced-patches/issues/4952)) ([b89927a](https://github.com/ReVanced/revanced-patches/commit/b89927a10e3b909a3c37fbb75c16a7abbce44560))
|
||||
* **YouTube - Hide player components:** Hide related video overlay in fullscreen ([#4938](https://github.com/ReVanced/revanced-patches/issues/4938)) ([ac9be97](https://github.com/ReVanced/revanced-patches/commit/ac9be9760c9965e54df196b227a310d64ead4bf5))
|
||||
|
||||
# [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
|
||||
|
||||
# [5.21.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.3...v5.21.0-dev.4) (2025-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
|
||||
|
||||
# [5.21.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.2...v5.21.0-dev.3) (2025-04-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **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))
|
||||
|
||||
# [5.21.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.1...v5.21.0-dev.2) (2025-04-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
|
||||
|
||||
# [5.21.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0-dev.1) (2025-04-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **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.20.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
|
||||
|
||||
## [5.20.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1-dev.1) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
|
||||
|
||||
# [5.20.0](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
|
||||
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
|
||||
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
|
||||
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
|
||||
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
|
||||
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
|
||||
|
||||
# [5.20.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.6...v5.20.0-dev.7) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
|
||||
|
||||
# [5.20.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.5...v5.20.0-dev.6) (2025-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
|
||||
|
||||
# [5.20.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.4...v5.20.0-dev.5) (2025-04-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
|
||||
|
||||
# [5.20.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.3...v5.20.0-dev.4) (2025-04-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
|
||||
|
||||
# [5.20.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.2...v5.20.0-dev.3) (2025-04-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
|
||||
|
||||
# [5.20.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.1...v5.20.0-dev.2) (2025-04-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
|
||||
|
||||
# [5.20.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0-dev.1) (2025-04-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
|
||||
|
||||
## [5.19.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1) (2025-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([3e18e86](https://github.com/ReVanced/revanced-patches/commit/3e18e868bbd9fd0600fe81a7fe8767b4bd89a00e))
|
||||
* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](https://github.com/ReVanced/revanced-patches/commit/89d44da171c3f56f13112d1d82bc4ea4a56c7c06))
|
||||
|
||||
## [5.19.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.1-dev.1...v5.19.1-dev.2) (2025-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([3e18e86](https://github.com/ReVanced/revanced-patches/commit/3e18e868bbd9fd0600fe81a7fe8767b4bd89a00e))
|
||||
|
||||
## [5.19.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1-dev.1) (2025-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](https://github.com/ReVanced/revanced-patches/commit/89d44da171c3f56f13112d1d82bc4ea4a56c7c06))
|
||||
|
||||
# [5.19.0](https://github.com/ReVanced/revanced-patches/compare/v5.18.0...v5.19.0) (2025-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([152bb7c](https://github.com/ReVanced/revanced-patches/commit/152bb7c3ee7cf36bc07460e7a3444631ec540441))
|
||||
* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([56e48f4](https://github.com/ReVanced/revanced-patches/commit/56e48f4c89da51f81ff11a79a164eaa5b440690e))
|
||||
* **Spotify - Custom theme:** Override more color resources ([#4690](https://github.com/ReVanced/revanced-patches/issues/4690)) ([d7a7a0b](https://github.com/ReVanced/revanced-patches/commit/d7a7a0b982dbafa181b04f984a5f7618fb067c2a))
|
||||
* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([106202f](https://github.com/ReVanced/revanced-patches/commit/106202f9ebb7699c4ba4ae46b82133e35f1ac6b9))
|
||||
* **Spotify:** Remove ads sections from home ([#4722](https://github.com/ReVanced/revanced-patches/issues/4722)) ([0b9a5e7](https://github.com/ReVanced/revanced-patches/commit/0b9a5e7f89a89d971762b3539166d4f145111481))
|
||||
* **Twitter - Hide recommended users:** Make hiding work again by filtering for new entryId prefix ([#4456](https://github.com/ReVanced/revanced-patches/issues/4456)) ([ff846b0](https://github.com/ReVanced/revanced-patches/commit/ff846b0b7ef5060caaffedb08c1f901172f5b2d1))
|
||||
* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([3864f35](https://github.com/ReVanced/revanced-patches/commit/3864f3550153617e23ad9979fb543d8a7fb4dc0a))
|
||||
* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([b67bbb2](https://github.com/ReVanced/revanced-patches/commit/b67bbb299669336addb68cf52a8ce5b39c68cec0))
|
||||
* **YouTube - Remove background playback restrictions:** Do not show media controls when playing Shorts from the feed ([2ed675c](https://github.com/ReVanced/revanced-patches/commit/2ed675cdd058fb5876381a9d30dee5263f6b2e26))
|
||||
* **YouTube - Return YouTube Dislike:** Correctly update label after disliking a Short with 20.07 ([0bb3e32](https://github.com/ReVanced/revanced-patches/commit/0bb3e32244fa10809aee5c4e549f77ed4054537e))
|
||||
* **YouTube - Return YouTube Dislike:** Fix inconsistent label after disliking a Short ([ea92a2e](https://github.com/ReVanced/revanced-patches/commit/ea92a2e36c7aab3bd115f7d0ec40467179485b32))
|
||||
* **YouTube - Seekbar:** Correctly hide the feed seekbar with target 20.07 ([ddc6e4c](https://github.com/ReVanced/revanced-patches/commit/ddc6e4c34fe35fa34bd859bf34e25645a23dbdc9))
|
||||
* **YouTube:** Combine multiple seekbar patches into a single patch ([#4705](https://github.com/ReVanced/revanced-patches/issues/4705)) ([503b7eb](https://github.com/ReVanced/revanced-patches/commit/503b7eb8d413ef7f248394f128f3b2a6f3192ba6))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Angulus:** Add `Hide ads` patch ([#4604](https://github.com/ReVanced/revanced-patches/issues/4604)) ([87c86b5](https://github.com/ReVanced/revanced-patches/commit/87c86b53a91b0054ac892a3f02bbe7bf83bbf813))
|
||||
* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([e3fad97](https://github.com/ReVanced/revanced-patches/commit/e3fad97484d7eb962aeb53d44a0047b34a881071))
|
||||
* **Photomath:** Support latest version ([#4672](https://github.com/ReVanced/revanced-patches/issues/4672)) ([8e16483](https://github.com/ReVanced/revanced-patches/commit/8e1648322948151e4565fb0d86e0f37d0a02d73f))
|
||||
* **Proton Mail:** Add `Remove 'Sent from' signature` patch ([#4514](https://github.com/ReVanced/revanced-patches/issues/4514)) ([34c14c9](https://github.com/ReVanced/revanced-patches/commit/34c14c9b443092824d035afd77adb678c6f89e3e))
|
||||
* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([6d7101c](https://github.com/ReVanced/revanced-patches/commit/6d7101cb2e546e01a934eff9cad1264367aeafe3))
|
||||
* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([a3fde87](https://github.com/ReVanced/revanced-patches/commit/a3fde874af993125ba7a741820e7bd48e3641b84))
|
||||
* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([649a2c0](https://github.com/ReVanced/revanced-patches/commit/649a2c06161c72a2040b179dbed5b415847d7527))
|
||||
* **YouTube - Settings:** Add icons to the ReVanced settings ([#4496](https://github.com/ReVanced/revanced-patches/issues/4496)) ([d0c85f0](https://github.com/ReVanced/revanced-patches/commit/d0c85f044083d720c63a8ea4ff15d42eefeb9db7))
|
||||
|
||||
# [5.19.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.16...v5.19.0-dev.17) (2025-04-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([6d7101c](https://github.com/ReVanced/revanced-patches/commit/6d7101cb2e546e01a934eff9cad1264367aeafe3))
|
||||
|
||||
# [5.19.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.15...v5.19.0-dev.16) (2025-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([56e48f4](https://github.com/ReVanced/revanced-patches/commit/56e48f4c89da51f81ff11a79a164eaa5b440690e))
|
||||
|
||||
# [5.19.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.14...v5.19.0-dev.15) (2025-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([152bb7c](https://github.com/ReVanced/revanced-patches/commit/152bb7c3ee7cf36bc07460e7a3444631ec540441))
|
||||
|
||||
# [5.19.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.13...v5.19.0-dev.14) (2025-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([106202f](https://github.com/ReVanced/revanced-patches/commit/106202f9ebb7699c4ba4ae46b82133e35f1ac6b9))
|
||||
|
||||
# [5.19.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.12...v5.19.0-dev.13) (2025-04-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([a3fde87](https://github.com/ReVanced/revanced-patches/commit/a3fde874af993125ba7a741820e7bd48e3641b84))
|
||||
|
||||
# [5.19.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.11...v5.19.0-dev.12) (2025-04-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([649a2c0](https://github.com/ReVanced/revanced-patches/commit/649a2c06161c72a2040b179dbed5b415847d7527))
|
||||
|
||||
# [5.19.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.10...v5.19.0-dev.11) (2025-04-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([e3fad97](https://github.com/ReVanced/revanced-patches/commit/e3fad97484d7eb962aeb53d44a0047b34a881071))
|
||||
|
||||
# [5.19.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.9...v5.19.0-dev.10) (2025-04-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([3864f35](https://github.com/ReVanced/revanced-patches/commit/3864f3550153617e23ad9979fb543d8a7fb4dc0a))
|
||||
|
||||
# [5.19.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.8...v5.19.0-dev.9) (2025-04-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([b67bbb2](https://github.com/ReVanced/revanced-patches/commit/b67bbb299669336addb68cf52a8ce5b39c68cec0))
|
||||
|
||||
# [5.19.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.7...v5.19.0-dev.8) (2025-04-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Return YouTube Dislike:** Fix inconsistent label after disliking a Short ([ea92a2e](https://github.com/ReVanced/revanced-patches/commit/ea92a2e36c7aab3bd115f7d0ec40467179485b32))
|
||||
|
||||
# [5.19.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.6...v5.19.0-dev.7) (2025-04-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Return YouTube Dislike:** Correctly update label after disliking a Short with 20.07 ([0bb3e32](https://github.com/ReVanced/revanced-patches/commit/0bb3e32244fa10809aee5c4e549f77ed4054537e))
|
||||
|
||||
# [5.19.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.5...v5.19.0-dev.6) (2025-04-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify:** Remove ads sections from home ([#4722](https://github.com/ReVanced/revanced-patches/issues/4722)) ([0b9a5e7](https://github.com/ReVanced/revanced-patches/commit/0b9a5e7f89a89d971762b3539166d4f145111481))
|
||||
|
||||
# [5.19.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.4...v5.19.0-dev.5) (2025-04-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Custom theme:** Override more color resources ([#4690](https://github.com/ReVanced/revanced-patches/issues/4690)) ([d7a7a0b](https://github.com/ReVanced/revanced-patches/commit/d7a7a0b982dbafa181b04f984a5f7618fb067c2a))
|
||||
|
||||
# [5.19.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.3...v5.19.0-dev.4) (2025-04-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Seekbar:** Correctly hide the feed seekbar with target 20.07 ([ddc6e4c](https://github.com/ReVanced/revanced-patches/commit/ddc6e4c34fe35fa34bd859bf34e25645a23dbdc9))
|
||||
|
||||
# [5.19.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.2...v5.19.0-dev.3) (2025-04-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Proton Mail:** Add `Remove 'Sent from' signature` patch ([#4514](https://github.com/ReVanced/revanced-patches/issues/4514)) ([34c14c9](https://github.com/ReVanced/revanced-patches/commit/34c14c9b443092824d035afd77adb678c6f89e3e))
|
||||
|
||||
# [5.19.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.1...v5.19.0-dev.2) (2025-04-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Settings:** Add icons to the ReVanced settings ([#4496](https://github.com/ReVanced/revanced-patches/issues/4496)) ([d0c85f0](https://github.com/ReVanced/revanced-patches/commit/d0c85f044083d720c63a8ea4ff15d42eefeb9db7))
|
||||
|
||||
# [5.19.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.18.1-dev.2...v5.19.0-dev.1) (2025-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Twitter - Hide recommended users:** Make hiding work again by filtering for new entryId prefix ([#4456](https://github.com/ReVanced/revanced-patches/issues/4456)) ([ff846b0](https://github.com/ReVanced/revanced-patches/commit/ff846b0b7ef5060caaffedb08c1f901172f5b2d1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Angulus:** Add `Hide ads` patch ([#4604](https://github.com/ReVanced/revanced-patches/issues/4604)) ([87c86b5](https://github.com/ReVanced/revanced-patches/commit/87c86b53a91b0054ac892a3f02bbe7bf83bbf813))
|
||||
* **Photomath:** Support latest version ([#4672](https://github.com/ReVanced/revanced-patches/issues/4672)) ([8e16483](https://github.com/ReVanced/revanced-patches/commit/8e1648322948151e4565fb0d86e0f37d0a02d73f))
|
||||
|
||||
## [5.18.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.18.1-dev.1...v5.18.1-dev.2) (2025-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Combine multiple seekbar patches into a single patch ([#4705](https://github.com/ReVanced/revanced-patches/issues/4705)) ([503b7eb](https://github.com/ReVanced/revanced-patches/commit/503b7eb8d413ef7f248394f128f3b2a6f3192ba6))
|
||||
|
||||
## [5.18.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.18.0...v5.18.1-dev.1) (2025-03-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Remove background playback restrictions:** Do not show media controls when playing Shorts from the feed ([2ed675c](https://github.com/ReVanced/revanced-patches/commit/2ed675cdd058fb5876381a9d30dee5263f6b2e26))
|
||||
|
||||
# [5.18.0](https://github.com/ReVanced/revanced-patches/compare/v5.17.0...v5.18.0) (2025-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify:** Ignore optional attributes if not present ([#4688](https://github.com/ReVanced/revanced-patches/issues/4688)) ([84f5854](https://github.com/ReVanced/revanced-patches/commit/84f585492e4be3604c6c7680ffb3bebcea5a675f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Support version `20.07.39` ([#4677](https://github.com/ReVanced/revanced-patches/issues/4677)) ([c1379f6](https://github.com/ReVanced/revanced-patches/commit/c1379f6e520c683d2c9d6a490a69ca542168b3b3))
|
||||
|
||||
# [5.18.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.18.0-dev.1...v5.18.0-dev.2) (2025-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify:** Ignore optional attributes if not present ([#4688](https://github.com/ReVanced/revanced-patches/issues/4688)) ([84f5854](https://github.com/ReVanced/revanced-patches/commit/84f585492e4be3604c6c7680ffb3bebcea5a675f))
|
||||
|
||||
# [5.18.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.17.0...v5.18.0-dev.1) (2025-03-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Support version `20.07.39` ([#4677](https://github.com/ReVanced/revanced-patches/issues/4677)) ([c1379f6](https://github.com/ReVanced/revanced-patches/commit/c1379f6e520c683d2c9d6a490a69ca542168b3b3))
|
||||
|
||||
# [5.17.0](https://github.com/ReVanced/revanced-patches/compare/v5.16.1...v5.17.0) (2025-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Facebook - Hide 'Sponsored Stories':** Constrain patch to latest compatible version ([#4657](https://github.com/ReVanced/revanced-patches/issues/4657)) ([46bd1c8](https://github.com/ReVanced/revanced-patches/commit/46bd1c829acd5f83600025e0ceb7d482ae80be69))
|
||||
* **Spotify - Unlock Premium:** Override additional attributes ([#4651](https://github.com/ReVanced/revanced-patches/issues/4651)) ([568b40d](https://github.com/ReVanced/revanced-patches/commit/568b40da9692eae9039bbb3cec513a61ca627c24))
|
||||
* **Spotify - Unlock Premium:** Use correct patch description convention ([a486522](https://github.com/ReVanced/revanced-patches/commit/a4865228f8481d2efc8fbf4e90902a03289d9a3f))
|
||||
* **X / Twitter:** Constrain patches to latest compatible versions ([#4683](https://github.com/ReVanced/revanced-patches/issues/4683)) ([f579728](https://github.com/ReVanced/revanced-patches/commit/f5797289f45186052537982c7f5db6f2b0769aee))
|
||||
* **YouTube - Navigation buttons:** Add user dialog message to 'Disable translucent status bar' ([a4a0e68](https://github.com/ReVanced/revanced-patches/commit/a4a0e6869e23d15ee09262460f4e290c90629eeb))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify - Unlock Premium:** Disable the "Spotify Premium" upsell experiment in context menus ([9a10ee4](https://github.com/ReVanced/revanced-patches/commit/9a10ee4d22fb53da2012a182e038749d3ad72377))
|
||||
|
||||
# [5.17.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.17.0-dev.3...v5.17.0-dev.4) (2025-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **X / Twitter:** Constrain patches to latest compatible versions ([#4683](https://github.com/ReVanced/revanced-patches/issues/4683)) ([f579728](https://github.com/ReVanced/revanced-patches/commit/f5797289f45186052537982c7f5db6f2b0769aee))
|
||||
|
||||
# [5.17.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.17.0-dev.2...v5.17.0-dev.3) (2025-03-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Premium:** Override additional attributes ([#4651](https://github.com/ReVanced/revanced-patches/issues/4651)) ([568b40d](https://github.com/ReVanced/revanced-patches/commit/568b40da9692eae9039bbb3cec513a61ca627c24))
|
||||
|
||||
# [5.17.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.17.0-dev.1...v5.17.0-dev.2) (2025-03-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Navigation buttons:** Add user dialog message to 'Disable translucent status bar' ([a4a0e68](https://github.com/ReVanced/revanced-patches/commit/a4a0e6869e23d15ee09262460f4e290c90629eeb))
|
||||
|
||||
# [5.17.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.16.2-dev.1...v5.17.0-dev.1) (2025-03-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Premium:** Use correct patch description convention ([a486522](https://github.com/ReVanced/revanced-patches/commit/a4865228f8481d2efc8fbf4e90902a03289d9a3f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify - Unlock Premium:** Disable the "Spotify Premium" upsell experiment in context menus ([9a10ee4](https://github.com/ReVanced/revanced-patches/commit/9a10ee4d22fb53da2012a182e038749d3ad72377))
|
||||
|
||||
## [5.16.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.16.1...v5.16.2-dev.1) (2025-03-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Facebook - Hide 'Sponsored Stories':** Constrain patch to latest compatible version ([#4657](https://github.com/ReVanced/revanced-patches/issues/4657)) ([46bd1c8](https://github.com/ReVanced/revanced-patches/commit/46bd1c829acd5f83600025e0ceb7d482ae80be69))
|
||||
|
||||
## [5.16.1](https://github.com/ReVanced/revanced-patches/compare/v5.16.0...v5.16.1) (2025-03-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Premium:** Override streaming attribute attempting to fix streaming issues ([06be36c](https://github.com/ReVanced/revanced-patches/commit/06be36cddf3430b4179dff696b3d15718cd6963b))
|
||||
|
||||
## [5.16.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.16.0...v5.16.1-dev.1) (2025-03-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Spotify - Unlock Premium:** Override streaming attribute attempting to fix streaming issues ([06be36c](https://github.com/ReVanced/revanced-patches/commit/06be36cddf3430b4179dff696b3d15718cd6963b))
|
||||
|
||||
# [5.16.0](https://github.com/ReVanced/revanced-patches/compare/v5.15.0...v5.16.0) (2025-03-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Settings:** System navigation bar is located above the settings ui on Android 15+ ([f7497be](https://github.com/ReVanced/revanced-patches/commit/f7497be2c5e4abcde6eb55b84955124a28f55cae))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add `Unlock premium` patch ([#4644](https://github.com/ReVanced/revanced-patches/issues/4644)) ([f048c50](https://github.com/ReVanced/revanced-patches/commit/f048c50e56fc1f5a5c607860be4206ef83b528fe))
|
||||
* **YouTube - Comments:** Add `Hide AI Comments summary` ([#4634](https://github.com/ReVanced/revanced-patches/issues/4634)) ([e9b7f26](https://github.com/ReVanced/revanced-patches/commit/e9b7f263f739bd130f6ea79913851a52355977c5))
|
||||
* **YouTube - Video description:** Add `Hide AI-generated video summary` ([#4636](https://github.com/ReVanced/revanced-patches/issues/4636)) ([521fd48](https://github.com/ReVanced/revanced-patches/commit/521fd48602432ab436d8711c19d7130b2b05af12))
|
||||
|
||||
# [5.16.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.16.0-dev.1...v5.16.0-dev.2) (2025-03-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add `Unlock premium` patch ([#4644](https://github.com/ReVanced/revanced-patches/issues/4644)) ([f048c50](https://github.com/ReVanced/revanced-patches/commit/f048c50e56fc1f5a5c607860be4206ef83b528fe))
|
||||
|
||||
# [5.16.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.15.0...v5.16.0-dev.1) (2025-03-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Settings:** System navigation bar is located above the settings ui on Android 15+ ([f7497be](https://github.com/ReVanced/revanced-patches/commit/f7497be2c5e4abcde6eb55b84955124a28f55cae))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Comments:** Add `Hide AI Comments summary` ([#4634](https://github.com/ReVanced/revanced-patches/issues/4634)) ([e9b7f26](https://github.com/ReVanced/revanced-patches/commit/e9b7f263f739bd130f6ea79913851a52355977c5))
|
||||
* **YouTube - Video description:** Add `Hide AI-generated video summary` ([#4636](https://github.com/ReVanced/revanced-patches/issues/4636)) ([521fd48](https://github.com/ReVanced/revanced-patches/commit/521fd48602432ab436d8711c19d7130b2b05af12))
|
||||
|
||||
# [5.15.0](https://github.com/ReVanced/revanced-patches/compare/v5.14.0...v5.15.0) (2025-03-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof app version:** Change oldest spoof target to 19.01.34 ([5012439](https://github.com/ReVanced/revanced-patches/commit/5012439a8e53b2a4ab5e85c47976e1ab28a51208))
|
||||
* **YouTube - Spoof app version:** Remove broken spoof targets that YouTube no longer supports ([#4610](https://github.com/ReVanced/revanced-patches/issues/4610)) ([883fbe7](https://github.com/ReVanced/revanced-patches/commit/883fbe71233c57cb1241e57c122b43f40722acc7))
|
||||
* **YouTube:** Do not show restart prompt more than once if setting change is canceled ([49797fe](https://github.com/ReVanced/revanced-patches/commit/49797fe8d0c4a0981ef621a31356c4315ae3777b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - SponsorBlock:** Add opacity setting to category segment colors ([#4582](https://github.com/ReVanced/revanced-patches/issues/4582)) ([6e8ffba](https://github.com/ReVanced/revanced-patches/commit/6e8ffbade9e03658f725622631e44dabf2995861))
|
||||
|
||||
# [5.15.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.3...v5.15.0-dev.4) (2025-03-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof app version:** Change oldest spoof target to 19.01.34 ([5012439](https://github.com/ReVanced/revanced-patches/commit/5012439a8e53b2a4ab5e85c47976e1ab28a51208))
|
||||
|
||||
# [5.15.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.2...v5.15.0-dev.3) (2025-03-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Do not show restart prompt more than once if setting change is canceled ([49797fe](https://github.com/ReVanced/revanced-patches/commit/49797fe8d0c4a0981ef621a31356c4315ae3777b))
|
||||
|
||||
# [5.15.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.15.0-dev.1...v5.15.0-dev.2) (2025-03-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof app version:** Remove broken spoof targets that YouTube no longer supports ([#4610](https://github.com/ReVanced/revanced-patches/issues/4610)) ([883fbe7](https://github.com/ReVanced/revanced-patches/commit/883fbe71233c57cb1241e57c122b43f40722acc7))
|
||||
|
||||
# [5.15.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.14.0...v5.15.0-dev.1) (2025-03-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - SponsorBlock:** Add opacity setting to category segment colors ([#4582](https://github.com/ReVanced/revanced-patches/issues/4582)) ([6e8ffba](https://github.com/ReVanced/revanced-patches/commit/6e8ffbade9e03658f725622631e44dabf2995861))
|
||||
|
||||
# [5.14.0](https://github.com/ReVanced/revanced-patches/compare/v5.13.0...v5.14.0) (2025-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Boost for reddit - Client spoof:** Use a different user agent to combat Reddit's API issues ([5d3c817](https://github.com/ReVanced/revanced-patches/commit/5d3c8175b34a3f6ae2732b25db0851773a8c000d))
|
||||
* **YouTube - Change form factor:** Restore Automotive form factor watch history menu, channel pages, and community posts ([#4541](https://github.com/ReVanced/revanced-patches/issues/4541)) ([aa5c001](https://github.com/ReVanced/revanced-patches/commit/aa5c001968446e5270c756256724e917009612cd))
|
||||
* **YouTube - Hide ads:** Hide new type of buttoned ad ([#4528](https://github.com/ReVanced/revanced-patches/issues/4528)) ([4387a7b](https://github.com/ReVanced/revanced-patches/commit/4387a7b131f49729e902e008bb4cec073635c040))
|
||||
* **YouTube - Hide layout components:** Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled ([62a6164](https://github.com/ReVanced/revanced-patches/commit/62a6164b88b64200b517a5ba6b800d8214dbbad8))
|
||||
* **YouTube - Theme:** Resolve dark mode startup crash with Android 9.0 ([741c2d5](https://github.com/ReVanced/revanced-patches/commit/741c2d59406f5d602554bb3a3c0b8982f42848b4))
|
||||
* **YouTube:** Change language settings menu to use native language names ([#4568](https://github.com/ReVanced/revanced-patches/issues/4568)) ([6f3f8fd](https://github.com/ReVanced/revanced-patches/commit/6f3f8fdce05501e4fa4423c2170a916fbea3b199))
|
||||
* **YouTube:** Combine `Restore old video quality menu` and `Remember video quality` into `Video quality` patch ([#4552](https://github.com/ReVanced/revanced-patches/issues/4552)) ([ee67b76](https://github.com/ReVanced/revanced-patches/commit/ee67b763d5c5947a5b1ef4420b1efa820ed6af83))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Infinity for Reddit:** Add support for package name on IzzyOnDroid ([#4554](https://github.com/ReVanced/revanced-patches/issues/4554)) ([cf9f959](https://github.com/ReVanced/revanced-patches/commit/cf9f959923076c10a7f0a29f6ba277f5a055ec07))
|
||||
* **Spotify:** Add `Spoof signature` patch ([#4576](https://github.com/ReVanced/revanced-patches/issues/4576)) ([3646c70](https://github.com/ReVanced/revanced-patches/commit/3646c70556b67a6b7ecf9b86869ebf03c3611333))
|
||||
* **YouTube - Remember video quality:** Add separate Shorts default quality settings ([#4543](https://github.com/ReVanced/revanced-patches/issues/4543)) ([88142ab](https://github.com/ReVanced/revanced-patches/commit/88142ab464192b564b1b8d56a6b45663f77f5e00))
|
||||
|
||||
# [5.14.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.8...v5.14.0-dev.9) (2025-03-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Spotify:** Add `Spoof signature` patch ([#4576](https://github.com/ReVanced/revanced-patches/issues/4576)) ([3646c70](https://github.com/ReVanced/revanced-patches/commit/3646c70556b67a6b7ecf9b86869ebf03c3611333))
|
||||
|
||||
# [5.14.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.7...v5.14.0-dev.8) (2025-03-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Theme:** Resolve dark mode startup crash with Android 9.0 ([741c2d5](https://github.com/ReVanced/revanced-patches/commit/741c2d59406f5d602554bb3a3c0b8982f42848b4))
|
||||
|
||||
# [5.14.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.6...v5.14.0-dev.7) (2025-03-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Change language settings menu to use native language names ([#4568](https://github.com/ReVanced/revanced-patches/issues/4568)) ([6f3f8fd](https://github.com/ReVanced/revanced-patches/commit/6f3f8fdce05501e4fa4423c2170a916fbea3b199))
|
||||
|
||||
# [5.14.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.5...v5.14.0-dev.6) (2025-03-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Do not hide Movie/Courses start page content if 'Hide horizontal shelves' is enabled ([62a6164](https://github.com/ReVanced/revanced-patches/commit/62a6164b88b64200b517a5ba6b800d8214dbbad8))
|
||||
|
||||
# [5.14.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.4...v5.14.0-dev.5) (2025-03-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Infinity for Reddit:** Add support for package name on IzzyOnDroid ([#4554](https://github.com/ReVanced/revanced-patches/issues/4554)) ([cf9f959](https://github.com/ReVanced/revanced-patches/commit/cf9f959923076c10a7f0a29f6ba277f5a055ec07))
|
||||
|
||||
# [5.14.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.3...v5.14.0-dev.4) (2025-03-06)
|
||||
|
||||
|
||||
|
||||
16
extensions/all/misc/adb/hide-adb/build.gradle.kts
Normal file
16
extensions/all/misc/adb/hide-adb/build.gradle.kts
Normal file
@@ -0,0 +1,16 @@
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<manifest/>
|
||||
@@ -0,0 +1,28 @@
|
||||
package app.revanced.extension.all.misc.hide.adb;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.provider.Settings;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class HideAdbPatch {
|
||||
private static final List<String> SPOOF_SETTINGS = Arrays.asList("adb_enabled", "adb_wifi_enabled", "development_settings_enabled");
|
||||
|
||||
public static int getInt(ContentResolver cr, String name) throws Settings.SettingNotFoundException {
|
||||
if (SPOOF_SETTINGS.contains(name)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Settings.Global.getInt(cr, name);
|
||||
}
|
||||
|
||||
public static int getInt(ContentResolver cr, String name, int def) {
|
||||
if (SPOOF_SETTINGS.contains(name)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Settings.Global.getInt(cr, name, def);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.revanced.extension.all.connectivity.wifi.spoof;
|
||||
package app.revanced.extension.all.misc.connectivity.wifi.spoof;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.revanced.extension.all.screencapture.removerestriction;
|
||||
package app.revanced.extension.all.misc.screencapture.removerestriction;
|
||||
|
||||
import android.media.AudioAttributes;
|
||||
import android.os.Build;
|
||||
@@ -1,4 +1,4 @@
|
||||
package app.revanced.extension.all.screenshot.removerestriction;
|
||||
package app.revanced.extension.all.misc.screenshot.removerestriction;
|
||||
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
@@ -82,7 +82,7 @@ public class HideAdsPatch {
|
||||
|
||||
// Filter HeaderBlock with known ads until next HeaderBlock.
|
||||
if (currentBlock instanceof HeaderBlock headerBlock) {
|
||||
StyledText headerText = headerBlock.component20();
|
||||
StyledText headerText = headerBlock.getTitle();
|
||||
if (headerText != null) {
|
||||
skipFullHeader = false;
|
||||
for (String blockedHeaderBlock : blockedHeaderBlocks) {
|
||||
|
||||
@@ -3,8 +3,7 @@ package nl.nu.performance.api.client.objects;
|
||||
import nl.nu.performance.api.client.interfaces.Block;
|
||||
|
||||
public class HeaderBlock extends Block {
|
||||
// returns title
|
||||
public final StyledText component20() {
|
||||
public final StyledText getTitle() {
|
||||
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 {
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.revanced.extension.shared;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
@@ -15,14 +16,18 @@ import android.os.Build;
|
||||
import android.os.PowerManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
import app.revanced.extension.shared.requests.Requester;
|
||||
import app.revanced.extension.shared.requests.Route;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class GmsCoreSupport {
|
||||
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
|
||||
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";
|
||||
@@ -31,10 +36,24 @@ public class GmsCoreSupport {
|
||||
= getGmsCoreVendorGroupId() + ".android.gms";
|
||||
private static final Uri GMS_CORE_PROVIDER
|
||||
= Uri.parse("content://" + getGmsCoreVendorGroupId() + ".android.gsf.gservices/prefix");
|
||||
private static final String DONT_KILL_MY_APP_LINK
|
||||
= "https://dontkillmyapp.com";
|
||||
private static final String DONT_KILL_MY_APP_URL
|
||||
= "https://dontkillmyapp.com/";
|
||||
private static final Route DONT_KILL_MY_APP_MANUFACTURER_API
|
||||
= new Route(GET, "/api/v2/{manufacturer}.json");
|
||||
private static final String DONT_KILL_MY_APP_NAME_PARAMETER
|
||||
= "?app=MicroG";
|
||||
private static final String BUILD_MANUFACTURER
|
||||
= Build.MANUFACTURER.toLowerCase(Locale.ROOT).replace(" ", "-");
|
||||
|
||||
/**
|
||||
* If a manufacturer specific page exists on DontKillMyApp.
|
||||
*/
|
||||
@Nullable
|
||||
private static volatile Boolean DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
||||
|
||||
private static void open(String queryOrLink) {
|
||||
Logger.printInfo(() -> "Opening link: " + queryOrLink);
|
||||
|
||||
Intent intent;
|
||||
try {
|
||||
// Check if queryOrLink is a valid URL.
|
||||
@@ -88,7 +107,7 @@ public class GmsCoreSupport {
|
||||
|
||||
// Do not exit. If the app exits before launch completes (and without
|
||||
// opening another activity), then on some devices such as Pixel phone Android 10
|
||||
// no toast will be shown and the app will continually be relaunched
|
||||
// no toast will be shown and the app will continually relaunch
|
||||
// with the appearance of a hung app.
|
||||
}
|
||||
|
||||
@@ -124,11 +143,12 @@ public class GmsCoreSupport {
|
||||
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
|
||||
if (client == null) {
|
||||
Logger.printInfo(() -> "GmsCore is not running in the background");
|
||||
checkIfDontKillMyAppSupportsManufacturer();
|
||||
|
||||
showBatteryOptimizationDialog(context,
|
||||
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
|
||||
"gms_core_dialog_open_website_text",
|
||||
(dialog, id) -> open(DONT_KILL_MY_APP_LINK));
|
||||
(dialog, id) -> openDontKillMyApp());
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
@@ -143,6 +163,48 @@ public class GmsCoreSupport {
|
||||
activity.startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
private static void checkIfDontKillMyAppSupportsManufacturer() {
|
||||
Utils.runOnBackgroundThread(() -> {
|
||||
try {
|
||||
final long start = System.currentTimeMillis();
|
||||
HttpURLConnection connection = Requester.getConnectionFromRoute(
|
||||
DONT_KILL_MY_APP_URL, DONT_KILL_MY_APP_MANUFACTURER_API, BUILD_MANUFACTURER);
|
||||
connection.setConnectTimeout(5000);
|
||||
connection.setReadTimeout(5000);
|
||||
|
||||
final boolean supported = connection.getResponseCode() == 200;
|
||||
Logger.printInfo(() -> "Manufacturer is " + (supported ? "" : "NOT ")
|
||||
+ "listed on DontKillMyApp: " + BUILD_MANUFACTURER
|
||||
+ " fetch took: " + (System.currentTimeMillis() - start) + "ms");
|
||||
DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED = supported;
|
||||
} catch (Exception ex) {
|
||||
Logger.printInfo(() -> "Could not check if manufacturer is listed on DontKillMyApp: "
|
||||
+ BUILD_MANUFACTURER, ex);
|
||||
DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void openDontKillMyApp() {
|
||||
final Boolean manufacturerSupported = DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
||||
|
||||
String manufacturerPageToOpen;
|
||||
if (manufacturerSupported == null) {
|
||||
// Fetch has not completed yet. Only happens on extremely slow internet connections
|
||||
// and the user spends less than 1 second reading what's on screen.
|
||||
// Instead of waiting for the fetch (which may timeout),
|
||||
// open the website without a vendor.
|
||||
manufacturerPageToOpen = "";
|
||||
} else if (manufacturerSupported) {
|
||||
manufacturerPageToOpen = BUILD_MANUFACTURER;
|
||||
} else {
|
||||
// No manufacturer specific page exists. Open the general page.
|
||||
manufacturerPageToOpen = "general";
|
||||
}
|
||||
|
||||
open(DONT_KILL_MY_APP_URL + manufacturerPageToOpen + DONT_KILL_MY_APP_NAME_PARAMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If GmsCore is not whitelisted from battery optimizations.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -799,4 +800,14 @@ public class Utils {
|
||||
builder.getContext().setTheme(editTextDialogStyle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a color resource or hex code to an int representation of the color.
|
||||
*/
|
||||
public static int getColorFromString(String colorString) throws IllegalArgumentException, Resources.NotFoundException {
|
||||
if (colorString.startsWith("#")) {
|
||||
return Color.parseColor(colorString);
|
||||
}
|
||||
return getResourceColor(colorString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ public enum AppLanguage {
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
// Languages codes not included with YouTube, but are translated on Crowdin
|
||||
GA,
|
||||
|
||||
// Language codes found in locale_config.xml
|
||||
// All region specific variants have been removed.
|
||||
AF,
|
||||
|
||||
@@ -23,6 +23,11 @@ public class BaseSettings {
|
||||
|
||||
public static final EnumSetting<AppLanguage> REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true, "revanced_language_user_dialog_message");
|
||||
|
||||
/**
|
||||
* Use the icons declared in the preferences created during patching. If no icons or styles are declared then this setting does nothing.
|
||||
*/
|
||||
public static final BooleanSetting SHOW_MENU_ICONS = new BooleanSetting("revanced_show_menu_icons", TRUE, true);
|
||||
|
||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
|
||||
public static final EnumSetting<AppLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
|
||||
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
|
||||
|
||||
@@ -342,9 +342,12 @@ public abstract class Setting<T> {
|
||||
|
||||
/**
|
||||
* Identical to calling {@link #save(Object)} using {@link #defaultValue}.
|
||||
*
|
||||
* @return The newly saved default value.
|
||||
*/
|
||||
public void resetToDefault() {
|
||||
public T resetToDefault() {
|
||||
save(defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,12 +22,23 @@ import app.revanced.extension.shared.settings.Setting;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
|
||||
/**
|
||||
* Indicates that if a preference changes,
|
||||
* to apply the change from the Setting to the UI component.
|
||||
*/
|
||||
public static boolean settingImportInProgress;
|
||||
|
||||
/**
|
||||
* Prevents recursive calls during preference <-> UI syncing from showing extra dialogs.
|
||||
*/
|
||||
private static boolean updatingPreference;
|
||||
|
||||
/**
|
||||
* Used to prevent showing reboot dialog, if user cancels a setting user dialog.
|
||||
*/
|
||||
private static boolean showingUserDialogMessage;
|
||||
|
||||
/**
|
||||
* Confirm and restart dialog button text and title.
|
||||
* Set by subclasses if Strings cannot be added as a resource.
|
||||
@@ -35,13 +46,13 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
@Nullable
|
||||
protected static String restartDialogButtonText, restartDialogTitle, confirmDialogTitle;
|
||||
|
||||
/**
|
||||
* Used to prevent showing reboot dialog, if user cancels a setting user dialog.
|
||||
*/
|
||||
private boolean showingUserDialogMessage;
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
|
||||
try {
|
||||
if (updatingPreference) {
|
||||
Logger.printDebug(() -> "Ignoring preference change as sync is in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str));
|
||||
if (setting == null) {
|
||||
return;
|
||||
@@ -63,16 +74,18 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
}
|
||||
}
|
||||
|
||||
updatingPreference = true;
|
||||
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
|
||||
// Updating here can can cause a recursive call back into this same method.
|
||||
updatePreference(pref, setting, true, settingImportInProgress);
|
||||
// Update any other preference availability that may now be different.
|
||||
updateUIAvailability();
|
||||
updatingPreference = false;
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "OnSharedPreferenceChangeListener failure", ex);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initialize this instance, and do any custom behavior.
|
||||
* <p>
|
||||
@@ -81,7 +94,10 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
* so all app specific {@link Setting} instances are loaded before this method returns.
|
||||
*/
|
||||
protected void initialize() {
|
||||
final var identifier = Utils.getResourceIdentifier("revanced_prefs", "xml");
|
||||
String preferenceResourceName = BaseSettings.SHOW_MENU_ICONS.get()
|
||||
? "revanced_prefs_icons"
|
||||
: "revanced_prefs";
|
||||
final var identifier = Utils.getResourceIdentifier(preferenceResourceName, "xml");
|
||||
if (identifier == 0) return;
|
||||
addPreferencesFromResource(identifier);
|
||||
|
||||
@@ -97,7 +113,9 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
if (confirmDialogTitle == null) {
|
||||
confirmDialogTitle = str("revanced_settings_confirm_user_dialog_title");
|
||||
}
|
||||
|
||||
showingUserDialogMessage = true;
|
||||
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(confirmDialogTitle)
|
||||
.setMessage(Objects.requireNonNull(setting.userDialogMessage).toString())
|
||||
@@ -141,14 +159,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
* @return If the preference is currently set to the default value of the Setting.
|
||||
*/
|
||||
protected boolean prefIsSetToDefault(Preference pref, Setting<?> setting) {
|
||||
Object defaultValue = setting.defaultValue;
|
||||
if (pref instanceof SwitchPreference switchPref) {
|
||||
return switchPref.isChecked() == (Boolean) setting.defaultValue;
|
||||
return switchPref.isChecked() == (Boolean) defaultValue;
|
||||
}
|
||||
String defaultValueString = defaultValue.toString();
|
||||
if (pref instanceof EditTextPreference editPreference) {
|
||||
return editPreference.getText().equals(setting.defaultValue.toString());
|
||||
return editPreference.getText().equals(defaultValueString);
|
||||
}
|
||||
if (pref instanceof ListPreference listPref) {
|
||||
return listPref.getValue().equals(setting.defaultValue.toString());
|
||||
return listPref.getValue().equals(defaultValueString);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Must override method to handle "
|
||||
@@ -255,7 +275,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
}
|
||||
}
|
||||
|
||||
public static void showRestartDialog(@NonNull final Context context) {
|
||||
public static void showRestartDialog(Context context) {
|
||||
Utils.verifyOnMainThread();
|
||||
if (restartDialogTitle == null) {
|
||||
restartDialogTitle = str("revanced_settings_restart_title");
|
||||
@@ -263,6 +283,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||
if (restartDialogButtonText == null) {
|
||||
restartDialogButtonText = str("revanced_settings_restart");
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(context)
|
||||
.setMessage(restartDialogTitle)
|
||||
.setPositiveButton(restartDialogButtonText, (dialog, id)
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.view.ViewGroup;
|
||||
*/
|
||||
@SuppressWarnings({"unused", "deprecation"})
|
||||
public class NoTitlePreferenceCategory extends PreferenceCategory {
|
||||
|
||||
public NoTitlePreferenceCategory(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
@@ -27,8 +28,27 @@ public class NoTitlePreferenceCategory extends PreferenceCategory {
|
||||
@Override
|
||||
@SuppressLint("MissingSuperCall")
|
||||
protected View onCreateView(ViewGroup parent) {
|
||||
// Return an empty, zero-height view to eliminate spacing
|
||||
// Return an zero-height view to eliminate empty title space.
|
||||
return new View(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getTitle() {
|
||||
// Title can be used for sorting. Return the first sub preference title.
|
||||
if (getPreferenceCount() > 0) {
|
||||
return getPreference(0).getTitle();
|
||||
}
|
||||
|
||||
return super.getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTitleRes() {
|
||||
if (getPreferenceCount() > 0) {
|
||||
return getPreference(0).getTitleRes();
|
||||
}
|
||||
|
||||
return super.getTitleRes();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package app.revanced.extension.shared.settings.preference;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
@@ -8,17 +10,23 @@ import android.util.AttributeSet;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
|
||||
@SuppressWarnings({"unused", "deprecation"})
|
||||
public class ResettableEditTextPreference extends EditTextPreference {
|
||||
|
||||
/**
|
||||
* Setting to reset.
|
||||
*/
|
||||
@Nullable
|
||||
private Setting<?> setting;
|
||||
|
||||
public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
@@ -32,12 +40,22 @@ public class ResettableEditTextPreference extends EditTextPreference {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public void setSetting(@Nullable Setting<?> setting) {
|
||||
this.setting = setting;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||
super.onPrepareDialogBuilder(builder);
|
||||
Utils.setEditTextDialogTheme(builder);
|
||||
|
||||
Setting<?> setting = Setting.getSettingFromPath(getKey());
|
||||
if (setting == null) {
|
||||
String key = getKey();
|
||||
if (key != null) {
|
||||
setting = Setting.getSettingFromPath(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (setting != null) {
|
||||
builder.setNeutralButton(str("revanced_settings_reset"), null);
|
||||
}
|
||||
@@ -54,8 +72,7 @@ public class ResettableEditTextPreference extends EditTextPreference {
|
||||
}
|
||||
button.setOnClickListener(v -> {
|
||||
try {
|
||||
Setting<?> setting = Objects.requireNonNull(Setting.getSettingFromPath(getKey()));
|
||||
String defaultStringValue = setting.defaultValue.toString();
|
||||
String defaultStringValue = Objects.requireNonNull(setting).defaultValue.toString();
|
||||
EditText editText = getEditText();
|
||||
editText.setText(defaultStringValue);
|
||||
editText.setSelection(defaultStringValue.length()); // move cursor to end of text
|
||||
|
||||
@@ -122,6 +122,21 @@ public class SpoofVideoStreamsPatch {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Turns off a feature flag that interferes with video playback.
|
||||
*/
|
||||
public static boolean usePlaybackStartFeatureFlag(boolean original) {
|
||||
if (original) {
|
||||
Logger.printDebug(() -> "usePlaybackStartFeatureFlag is set on");
|
||||
}
|
||||
|
||||
if (!SPOOF_STREAMING_DATA) {
|
||||
return original;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
|
||||
@@ -204,7 +204,7 @@ public class StreamingDataRequest {
|
||||
// but empty response body does.
|
||||
if (connection.getContentLength() == 0) {
|
||||
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
|
||||
Utils.showToastShort("Ignoring empty spoof stream client: " + clientType);
|
||||
Utils.showToastShort("Debug: Ignoring empty spoof stream client " + clientType);
|
||||
}
|
||||
} else {
|
||||
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
|
||||
|
||||
16
extensions/spotify/build.gradle.kts
Normal file
16
extensions/spotify/build.gradle.kts
Normal file
@@ -0,0 +1,16 @@
|
||||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:spotify:stub"))
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
1
extensions/spotify/src/main/AndroidManifest.xml
Normal file
1
extensions/spotify/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest/>
|
||||
@@ -0,0 +1,22 @@
|
||||
package app.revanced.extension.spotify.layout.theme;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class CustomThemePatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static long getThemeColor(String colorString) {
|
||||
try {
|
||||
return Utils.getColorFromString(colorString);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Invalid custom color: " + colorString, ex);
|
||||
return Color.BLACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
extensions/spotify/stub/build.gradle.kts
Normal file
17
extensions/spotify/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 = 26
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
1
extensions/spotify/stub/src/main/AndroidManifest.xml
Normal file
1
extensions/spotify/stub/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest/>
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.spotify.home.evopage.homeapi.proto;
|
||||
|
||||
public final class Section {
|
||||
public static final int VIDEO_BRAND_AD_FIELD_NUMBER = 20;
|
||||
public static final int IMAGE_BRAND_AD_FIELD_NUMBER = 21;
|
||||
public int featureTypeCase_;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.spotify.remoteconfig.internal;
|
||||
|
||||
public final class AccountAttribute {
|
||||
public Object value_;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.spotify.useraccount.v1;
|
||||
|
||||
/**
|
||||
* Used for target 8.6.98.900. Class is still present in newer app targets.
|
||||
*/
|
||||
public class AccountAttribute {
|
||||
public Object value_;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package app.revanced.extension.tiktok.feedfilter;
|
||||
|
||||
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
||||
import com.ss.android.ugc.aweme.feed.model.FeedItemList;
|
||||
import com.ss.android.ugc.aweme.follow.presenter.FollowFeedList;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -13,22 +14,41 @@ public final class FeedItemsFilter {
|
||||
new StoryFilter(),
|
||||
new ImageVideoFilter(),
|
||||
new ViewCountFilter(),
|
||||
new LikeCountFilter()
|
||||
new LikeCountFilter(),
|
||||
new ShopFilter()
|
||||
);
|
||||
|
||||
public static void filter(FeedItemList feedItemList) {
|
||||
Iterator<Aweme> feedItemListIterator = feedItemList.items.iterator();
|
||||
while (feedItemListIterator.hasNext()) {
|
||||
Aweme item = feedItemListIterator.next();
|
||||
if (item == null) continue;
|
||||
filterFeedList(feedItemList.items, item -> item);
|
||||
}
|
||||
|
||||
for (IFilter filter : FILTERS) {
|
||||
boolean enabled = filter.getEnabled();
|
||||
if (enabled && filter.getFiltered(item)) {
|
||||
feedItemListIterator.remove();
|
||||
break;
|
||||
}
|
||||
public static void filter(FollowFeedList followFeedList) {
|
||||
filterFeedList(followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null);
|
||||
}
|
||||
|
||||
private static <T> void filterFeedList(List<T> list, AwemeExtractor<T> extractor) {
|
||||
// Could be simplified with removeIf() but requires Android 7.0+ while TikTok supports 4.0+.
|
||||
Iterator<T> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
T container = iterator.next();
|
||||
Aweme item = extractor.extract(container);
|
||||
if (item != null && shouldFilter(item)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldFilter(Aweme item) {
|
||||
for (IFilter filter : FILTERS) {
|
||||
if (filter.getEnabled() && filter.getFiltered(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface AwemeExtractor<T> {
|
||||
Aweme extract(T source);
|
||||
}
|
||||
}
|
||||
@@ -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 static final BooleanSetting REMOVE_ADS = new BooleanSetting("remove_ads", TRUE, true);
|
||||
public static final BooleanSetting HIDE_LIVE = new BooleanSetting("hide_live", FALSE, true);
|
||||
public static final BooleanSetting HIDE_SHOP = new BooleanSetting("hide_shop", FALSE, true);
|
||||
public static final BooleanSetting HIDE_STORY = new BooleanSetting("hide_story", FALSE, true);
|
||||
public static final BooleanSetting HIDE_IMAGE = new BooleanSetting("hide_image", FALSE, true);
|
||||
public static final StringSetting MIN_MAX_VIEWS = new StringSetting("min_max_views", "0-" + Long.MAX_VALUE, true);
|
||||
|
||||
@@ -9,7 +9,6 @@ import app.revanced.extension.tiktok.settings.preference.categories.DownloadsPre
|
||||
import app.revanced.extension.tiktok.settings.preference.categories.FeedFilterPreferenceCategory;
|
||||
import app.revanced.extension.tiktok.settings.preference.categories.ExtensionPreferenceCategory;
|
||||
import app.revanced.extension.tiktok.settings.preference.categories.SimSpoofPreferenceCategory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Preference fragment for ReVanced settings
|
||||
|
||||
@@ -26,6 +26,11 @@ public class FeedFilterPreferenceCategory extends ConditionalPreferenceCategory
|
||||
"Remove feed ads", "Remove ads from feed.",
|
||||
Settings.REMOVE_ADS
|
||||
));
|
||||
addPreference(new TogglePreference(
|
||||
context,
|
||||
"Hide TikTok Shop", "Hide TikTok shop from feed.",
|
||||
Settings.HIDE_SHOP
|
||||
));
|
||||
addPreference(new TogglePreference(
|
||||
context,
|
||||
"Hide livestreams", "Hide livestreams from feed.",
|
||||
|
||||
@@ -33,4 +33,8 @@ public class Aweme {
|
||||
public AwemeStatistics getStatistics() {
|
||||
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;
|
||||
}
|
||||
@@ -163,7 +163,7 @@ internal object TwiFucker {
|
||||
|
||||
private fun JSONObject.entryIsWhoToFollow(): Boolean =
|
||||
optString("entryId").let {
|
||||
it.startsWith("whoToFollow-") || it.startsWith("who-to-follow-") || it.startsWith("connect-module-")
|
||||
it.startsWith("whoToFollow-") || it.startsWith("who-to-follow-") || it.startsWith("connect-module-") || it.startsWith("who-to-subscribe-")
|
||||
}
|
||||
|
||||
private fun JSONObject.itemContainsPromotedUser(): Boolean =
|
||||
|
||||
@@ -2,6 +2,8 @@ package app.revanced.extension.youtube;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -45,13 +47,24 @@ public class ThemeHelper {
|
||||
return "@color/yt_black3";
|
||||
}
|
||||
|
||||
private static int getThemeColor(String resourceName, int defaultColor) {
|
||||
try {
|
||||
return Utils.getColorFromString(resourceName);
|
||||
} catch (Exception ex) {
|
||||
// User entered an invalid custom theme color.
|
||||
// Normally this should never be reached, and no localized strings are needed.
|
||||
Utils.showToastLong("Invalid custom theme color: " + resourceName);
|
||||
return defaultColor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The dark theme color as specified by the Theme patch (if included),
|
||||
* or the dark mode background color unpatched YT uses.
|
||||
*/
|
||||
public static int getDarkThemeColor() {
|
||||
if (darkThemeColor == null) {
|
||||
darkThemeColor = getColorInt(darkThemeResourceName());
|
||||
darkThemeColor = getThemeColor(darkThemeResourceName(), Color.BLACK);
|
||||
}
|
||||
return darkThemeColor;
|
||||
}
|
||||
@@ -71,18 +84,11 @@ public class ThemeHelper {
|
||||
*/
|
||||
public static int getLightThemeColor() {
|
||||
if (lightThemeColor == null) {
|
||||
lightThemeColor = getColorInt(lightThemeResourceName());
|
||||
lightThemeColor = getThemeColor(lightThemeResourceName(), Color.WHITE);
|
||||
}
|
||||
return lightThemeColor;
|
||||
}
|
||||
|
||||
private static int getColorInt(String colorString) {
|
||||
if (colorString.startsWith("#")) {
|
||||
return Color.parseColor(colorString);
|
||||
}
|
||||
return Utils.getResourceColor(colorString);
|
||||
}
|
||||
|
||||
public static int getBackgroundColor() {
|
||||
return isDarkTheme() ? getDarkThemeColor() : getLightThemeColor();
|
||||
}
|
||||
@@ -96,6 +102,23 @@ public class ThemeHelper {
|
||||
? "yt_black3"
|
||||
: "yt_white1";
|
||||
|
||||
return getColorInt(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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -23,7 +24,13 @@ public class BackgroundPlaybackPatch {
|
||||
// 7. Close the Short
|
||||
// 8. Resume playing the regular video
|
||||
// 9. Minimize the app (PIP should appear)
|
||||
return !ShortsPlayerState.isOpen();
|
||||
if (ShortsPlayerState.isOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the video player is opened and it's not playing in the feed.
|
||||
PlayerType current = PlayerType.getCurrent();
|
||||
return !current.isNoneOrHidden() && current != PlayerType.INLINE_MINIMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -81,6 +82,13 @@ public final class ChangeStartPagePatch {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChangeStartPageTypeAvailability implements Setting.Availability {
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return Settings.CHANGE_START_PAGE.get() != StartPage.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intent action when YouTube is cold started from the launcher.
|
||||
* <p>
|
||||
@@ -93,6 +101,8 @@ public final class ChangeStartPagePatch {
|
||||
|
||||
private static final StartPage START_PAGE = Settings.CHANGE_START_PAGE.get();
|
||||
|
||||
private static final boolean CHANGE_START_PAGE_ALWAYS = Settings.CHANGE_START_PAGE_ALWAYS.get();
|
||||
|
||||
/**
|
||||
* There is an issue where the back button on the toolbar doesn't work properly.
|
||||
* As a workaround for this issue, instead of overriding the browserId multiple times, just override it once.
|
||||
@@ -104,13 +114,13 @@ public final class ChangeStartPagePatch {
|
||||
return original;
|
||||
}
|
||||
|
||||
if (appLaunched) {
|
||||
if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
|
||||
Logger.printDebug(() -> "Ignore override browseId as the app already launched");
|
||||
return original;
|
||||
}
|
||||
appLaunched = true;
|
||||
|
||||
Logger.printDebug(() -> "Changing browseId to " + START_PAGE.id);
|
||||
Logger.printDebug(() -> "Changing browseId to: " + START_PAGE.id);
|
||||
return START_PAGE.id;
|
||||
}
|
||||
|
||||
@@ -125,14 +135,14 @@ public final class ChangeStartPagePatch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (appLaunched) {
|
||||
if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
|
||||
Logger.printDebug(() -> "Ignore override intent action as the app already launched");
|
||||
return;
|
||||
}
|
||||
appLaunched = true;
|
||||
|
||||
String intentAction = START_PAGE.id;
|
||||
Logger.printDebug(() -> "Changing intent action to " + intentAction);
|
||||
Logger.printDebug(() -> "Changing intent action to: " + intentAction);
|
||||
intent.setAction(intentAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,7 @@ public class CustomPlayerOverlayOpacityPatch {
|
||||
|
||||
if (opacity < 0 || opacity > 100) {
|
||||
Utils.showToastLong(str("revanced_player_overlay_opacity_invalid_toast"));
|
||||
Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
|
||||
opacity = Settings.PLAYER_OVERLAY_OPACITY.defaultValue;
|
||||
opacity = Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
|
||||
}
|
||||
|
||||
PLAYER_OVERLAY_OPACITY_LEVEL = (opacity * 255) / 100;
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class DisableAutoCaptionsPatch {
|
||||
|
||||
/**
|
||||
* Used by injected code. Do not delete.
|
||||
*/
|
||||
public static boolean captionsButtonDisabled;
|
||||
private static volatile boolean captionsButtonStatus;
|
||||
|
||||
public static boolean autoCaptionsEnabled() {
|
||||
return Settings.AUTO_CAPTIONS.get()
|
||||
// Do not use auto captions for Shorts.
|
||||
&& ShortsPlayerState.isOpen();
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean disableAutoCaptions() {
|
||||
return Settings.DISABLE_AUTO_CAPTIONS.get() && !captionsButtonStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void setCaptionsButtonStatus(boolean status) {
|
||||
captionsButtonStatus = status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package app.revanced.extension.youtube.patches;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
/** @noinspection unused*/
|
||||
@SuppressWarnings("unused")
|
||||
public class DisableResumingStartupShortsPlayerPatch {
|
||||
|
||||
/**
|
||||
@@ -11,4 +11,11 @@ public class DisableResumingStartupShortsPlayerPatch {
|
||||
public static boolean disableResumingStartupShortsPlayer() {
|
||||
return Settings.DISABLE_RESUMING_SHORTS_PLAYER.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean disableResumingStartupShortsPlayer(boolean original) {
|
||||
return original && !Settings.DISABLE_RESUMING_SHORTS_PLAYER.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class HideRelatedVideoOverlayPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean hideRelatedVideoOverlay() {
|
||||
return Settings.HIDE_RELATED_VIDEO_OVERLAY.get();
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,13 @@ public final class MiniplayerPatch {
|
||||
MODERN_2(null, 2),
|
||||
MODERN_3(null, 3),
|
||||
/**
|
||||
* Half broken miniplayer, that might be work in progress or left over abandoned code.
|
||||
* Can force this type by editing the import/export settings.
|
||||
* Works and is functional with 20.03+
|
||||
*/
|
||||
MODERN_4(null, 4);
|
||||
MODERN_4(null, 4),
|
||||
/**
|
||||
* Half broken miniplayer, and in 20.02 and earlier is declared as type 4.
|
||||
*/
|
||||
MODERN_5(null, 5);
|
||||
|
||||
/**
|
||||
* Legacy tablet hook value.
|
||||
@@ -126,12 +129,13 @@ public final class MiniplayerPatch {
|
||||
private static final boolean DRAG_AND_DROP_ENABLED =
|
||||
CURRENT_TYPE.isModern() && Settings.MINIPLAYER_DRAG_AND_DROP.get();
|
||||
|
||||
private static final boolean HIDE_EXPAND_CLOSE_ENABLED =
|
||||
Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.get()
|
||||
&& Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.isAvailable();
|
||||
private static final boolean HIDE_OVERLAY_BUTTONS_ENABLED =
|
||||
Settings.MINIPLAYER_HIDE_OVERLAY_BUTTONS.get()
|
||||
&& Settings.MINIPLAYER_HIDE_OVERLAY_BUTTONS.isAvailable();
|
||||
|
||||
private static final boolean HIDE_SUBTEXT_ENABLED =
|
||||
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get();
|
||||
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3 || CURRENT_TYPE == MODERN_4)
|
||||
&& Settings.MINIPLAYER_HIDE_SUBTEXT.get();
|
||||
|
||||
// 19.25 is last version that has forward/back buttons for phones,
|
||||
// but buttons still show for tablets/foldable devices and they don't work well so always hide.
|
||||
@@ -139,7 +143,7 @@ public final class MiniplayerPatch {
|
||||
&& (VersionCheckPatch.IS_19_34_OR_GREATER || Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get());
|
||||
|
||||
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
|
||||
Settings.MINIPLAYER_ROUNDED_CORNERS.get();
|
||||
CURRENT_TYPE.isModern() && Settings.MINIPLAYER_ROUNDED_CORNERS.get();
|
||||
|
||||
private static final boolean MINIPLAYER_HORIZONTAL_DRAG_ENABLED =
|
||||
DRAG_AND_DROP_ENABLED && Settings.MINIPLAYER_HORIZONTAL_DRAG.get();
|
||||
@@ -158,8 +162,7 @@ public final class MiniplayerPatch {
|
||||
|
||||
if (opacity < 0 || opacity > 100) {
|
||||
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
|
||||
Settings.MINIPLAYER_OPACITY.resetToDefault();
|
||||
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
|
||||
opacity = Settings.MINIPLAYER_OPACITY.resetToDefault();
|
||||
}
|
||||
|
||||
OPACITY_LEVEL = (opacity * 255) / 100;
|
||||
@@ -172,11 +175,12 @@ public final class MiniplayerPatch {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MiniplayerHideExpandCloseAvailability implements Setting.Availability {
|
||||
public static final class MiniplayerHideOverlayButtonsAvailability implements Setting.Availability {
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
MiniplayerType type = Settings.MINIPLAYER_TYPE.get();
|
||||
return (!IS_19_20_OR_GREATER && (type == MODERN_1 || type == MODERN_3))
|
||||
return type == MODERN_4
|
||||
|| (!IS_19_20_OR_GREATER && (type == MODERN_1 || type == MODERN_3))
|
||||
|| (!IS_19_26_OR_GREATER && type == MODERN_1
|
||||
&& !Settings.MINIPLAYER_DOUBLE_TAP_ACTION.get() && !Settings.MINIPLAYER_DRAG_AND_DROP.get())
|
||||
|| (IS_19_29_OR_GREATER && type == MODERN_3);
|
||||
@@ -227,9 +231,13 @@ public final class MiniplayerPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void adjustMiniplayerOpacity(ImageView view) {
|
||||
public static void adjustMiniplayerOpacity(View view) {
|
||||
if (CURRENT_TYPE == MODERN_1) {
|
||||
view.setImageAlpha(OPACITY_LEVEL);
|
||||
if (view instanceof ImageView imageView) {
|
||||
imageView.setImageAlpha(OPACITY_LEVEL);
|
||||
} else {
|
||||
Logger.printException(() -> "Unknown miniplayer overlay view: " + view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +255,7 @@ public final class MiniplayerPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean enableMiniplayerDoubleTapAction(boolean original) {
|
||||
public static boolean getMiniplayerDoubleTapAction(boolean original) {
|
||||
if (CURRENT_TYPE == DEFAULT) {
|
||||
return original;
|
||||
}
|
||||
@@ -258,7 +266,7 @@ public final class MiniplayerPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean enableMiniplayerDragAndDrop(boolean original) {
|
||||
public static boolean getMiniplayerDragAndDrop(boolean original) {
|
||||
if (CURRENT_TYPE == DEFAULT) {
|
||||
return original;
|
||||
}
|
||||
@@ -266,13 +274,36 @@ public final class MiniplayerPatch {
|
||||
return DRAG_AND_DROP_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean getRoundedCorners(boolean original) {
|
||||
if (CURRENT_TYPE == DEFAULT) {
|
||||
return original;
|
||||
}
|
||||
|
||||
return MINIPLAYER_ROUNDED_CORNERS_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean setRoundedCorners(boolean original) {
|
||||
if (CURRENT_TYPE.isModern()) {
|
||||
return MINIPLAYER_ROUNDED_CORNERS_ENABLED;
|
||||
public static boolean getHorizontalDrag(boolean original) {
|
||||
if (CURRENT_TYPE == DEFAULT) {
|
||||
return original;
|
||||
}
|
||||
|
||||
return MINIPLAYER_HORIZONTAL_DRAG_ENABLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean getMaximizeAnimation(boolean original) {
|
||||
// This must be forced on if horizontal drag is enabled,
|
||||
// otherwise the UI has visual glitches when maximizing the miniplayer.
|
||||
if (MINIPLAYER_HORIZONTAL_DRAG_ENABLED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return original;
|
||||
@@ -281,7 +312,7 @@ public final class MiniplayerPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static int setMiniplayerDefaultSize(int original) {
|
||||
public static int getMiniplayerDefaultSize(int original) {
|
||||
if (CURRENT_TYPE.isModern()) {
|
||||
return MINIPLAYER_SIZE;
|
||||
}
|
||||
@@ -289,29 +320,26 @@ public final class MiniplayerPatch {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void hideMiniplayerExpandClose(View view) {
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_OVERLAY_BUTTONS_ENABLED, view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean setHorizontalDrag(boolean original) {
|
||||
if (CURRENT_TYPE.isModern()) {
|
||||
return MINIPLAYER_HORIZONTAL_DRAG_ENABLED;
|
||||
public static void hideMiniplayerActionButton(View view) {
|
||||
if (CURRENT_TYPE == MODERN_4) {
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_OVERLAY_BUTTONS_ENABLED, view);
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void hideMiniplayerExpandClose(ImageView view) {
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_EXPAND_CLOSE_ENABLED, view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void hideMiniplayerRewindForward(ImageView view) {
|
||||
public static void hideMiniplayerRewindForward(View view) {
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_REWIND_FORWARD_ENABLED, view);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,26 +2,21 @@ package app.revanced.extension.youtube.patches;
|
||||
|
||||
import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike.Vote;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilterPatch;
|
||||
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
|
||||
import app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike;
|
||||
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
@@ -47,9 +42,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
||||
@SuppressWarnings("unused")
|
||||
public class ReturnYouTubeDislikePatch {
|
||||
|
||||
public static final boolean IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER =
|
||||
SpoofAppVersionPatch.isSpoofingToLessThan("18.34.00");
|
||||
|
||||
/**
|
||||
* RYD data for the current video on screen.
|
||||
*/
|
||||
@@ -64,12 +56,12 @@ public class ReturnYouTubeDislikePatch {
|
||||
private static volatile ReturnYouTubeDislike lastLithoShortsVideoData;
|
||||
|
||||
/**
|
||||
* Because the litho Shorts spans are created after {@link ReturnYouTubeDislikeFilterPatch}
|
||||
* detects the video ids, after the user votes the litho will update
|
||||
* but {@link #lastLithoShortsVideoData} is not the correct data to use.
|
||||
* If this is true, then instead use {@link #currentVideoData}.
|
||||
* Because litho Shorts spans are created offscreen after {@link ReturnYouTubeDislikeFilterPatch}
|
||||
* detects the video ids, but the current Short can arbitrarily reload the same span,
|
||||
* then use the {@link #lastLithoShortsVideoData} if this value is greater than zero.
|
||||
*/
|
||||
private static volatile boolean lithoShortsShouldUseCurrentData;
|
||||
@GuardedBy("ReturnYouTubeDislikePatch.class")
|
||||
private static int useLithoShortsVideoDataCount;
|
||||
|
||||
/**
|
||||
* Last video id prefetched. Field is to prevent prefetching the same video id multiple times in a row.
|
||||
@@ -87,12 +79,28 @@ public class ReturnYouTubeDislikePatch {
|
||||
private static void clearData() {
|
||||
currentVideoData = null;
|
||||
lastLithoShortsVideoData = null;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
synchronized (ReturnYouTubeDislike.class) {
|
||||
useLithoShortsVideoDataCount = 0;
|
||||
}
|
||||
|
||||
// Rolling number text should not be cleared,
|
||||
// as it's used if incognito Short is opened/closed
|
||||
// while a regular video is on screen.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If {@link #useLithoShortsVideoDataCount} was greater than zero.
|
||||
*/
|
||||
private static boolean decrementUseLithoDataIfNeeded() {
|
||||
synchronized (ReturnYouTubeDislikePatch.class) {
|
||||
if (useLithoShortsVideoDataCount > 0) {
|
||||
useLithoShortsVideoDataCount--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Litho player for both regular videos and Shorts.
|
||||
@@ -156,10 +164,13 @@ public class ReturnYouTubeDislikePatch {
|
||||
return getShortsSpan(original, true);
|
||||
}
|
||||
|
||||
if (conversionContextString.contains("|shorts_like_button.eml")
|
||||
&& !Utils.containsNumber(original)) {
|
||||
Logger.printDebug(() -> "Replacing hidden likes count");
|
||||
return getShortsSpan(original, false);
|
||||
if (conversionContextString.contains("|shorts_like_button.eml")) {
|
||||
if (!Utils.containsNumber(original)) {
|
||||
Logger.printDebug(() -> "Replacing hidden likes count");
|
||||
return getShortsSpan(original, false);
|
||||
} else {
|
||||
decrementUseLithoDataIfNeeded();
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onLithoTextLoaded failure", ex);
|
||||
@@ -174,7 +185,14 @@ public class ReturnYouTubeDislikePatch {
|
||||
return original;
|
||||
}
|
||||
|
||||
ReturnYouTubeDislike videoData = lastLithoShortsVideoData;
|
||||
final ReturnYouTubeDislike videoData;
|
||||
if (decrementUseLithoDataIfNeeded()) {
|
||||
// New Short is loading off screen.
|
||||
videoData = lastLithoShortsVideoData;
|
||||
} else {
|
||||
videoData = currentVideoData;
|
||||
}
|
||||
|
||||
if (videoData == null) {
|
||||
// The Shorts litho video id filter did not detect the video id.
|
||||
// This is normal in incognito mode, but otherwise is abnormal.
|
||||
@@ -182,19 +200,6 @@ public class ReturnYouTubeDislikePatch {
|
||||
return original;
|
||||
}
|
||||
|
||||
// Use the correct dislikes data after voting.
|
||||
if (lithoShortsShouldUseCurrentData) {
|
||||
if (isDislikesSpan) {
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
}
|
||||
videoData = currentVideoData;
|
||||
if (videoData == null) {
|
||||
Logger.printException(() -> "currentVideoData is null"); // Should never happen
|
||||
return original;
|
||||
}
|
||||
Logger.printDebug(() -> "Using current video data for litho span");
|
||||
}
|
||||
|
||||
return isDislikesSpan
|
||||
? videoData.getDislikeSpanForShort((Spanned) original)
|
||||
: videoData.getLikeSpanForShort((Spanned) original);
|
||||
@@ -347,137 +352,6 @@ public class ReturnYouTubeDislikePatch {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Non litho Shorts player.
|
||||
//
|
||||
|
||||
/**
|
||||
* Replacement text to use for "Dislikes" while RYD is fetching.
|
||||
*/
|
||||
private static final Spannable SHORTS_LOADING_SPAN = new SpannableString("-");
|
||||
|
||||
/**
|
||||
* Dislikes TextViews used by Shorts.
|
||||
*
|
||||
* Multiple TextViews are loaded at once (for the prior and next videos to swipe to).
|
||||
* Keep track of all of them, and later pick out the correct one based on their on screen position.
|
||||
*/
|
||||
private static final List<WeakReference<TextView>> shortsTextViewRefs = new ArrayList<>();
|
||||
|
||||
private static void clearRemovedShortsTextViews() {
|
||||
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Called when a Shorts dislike is updated. Always on main thread.
|
||||
* Handles update asynchronously, otherwise Shorts video will be frozen while the UI thread is blocked.
|
||||
*
|
||||
* @return if RYD is enabled and the TextView was updated.
|
||||
*/
|
||||
public static boolean setShortsDislikes(@NonNull View likeDislikeView) {
|
||||
try {
|
||||
if (!Settings.RYD_ENABLED.get()) {
|
||||
return false;
|
||||
}
|
||||
if (!Settings.RYD_SHORTS.get() || Settings.HIDE_SHORTS_DISLIKE_BUTTON.get()) {
|
||||
// Must clear the data here, in case a new video was loaded while PlayerType
|
||||
// suggested the video was not a short (can happen when spoofing to an old app version).
|
||||
clearData();
|
||||
return false;
|
||||
}
|
||||
Logger.printDebug(() -> "setShortsDislikes");
|
||||
|
||||
TextView textView = (TextView) likeDislikeView;
|
||||
textView.setText(SHORTS_LOADING_SPAN); // Change 'Dislike' text to the loading text.
|
||||
shortsTextViewRefs.add(new WeakReference<>(textView));
|
||||
|
||||
if (likeDislikeView.isSelected() && isShortTextViewOnScreen(textView)) {
|
||||
Logger.printDebug(() -> "Shorts dislike is already selected");
|
||||
ReturnYouTubeDislike videoData = currentVideoData;
|
||||
if (videoData != null) videoData.setUserVote(Vote.DISLIKE);
|
||||
}
|
||||
|
||||
// For the first short played, the Shorts dislike hook is called after the video id hook.
|
||||
// But for most other times this hook is called before the video id (which is not ideal).
|
||||
// Must update the TextViews here, and also after the videoId changes.
|
||||
updateOnScreenShortsTextViews(false);
|
||||
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setShortsDislikes failure", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forceUpdate if false, then only update the 'loading text views.
|
||||
* If true, update all on screen text views.
|
||||
*/
|
||||
private static void updateOnScreenShortsTextViews(boolean forceUpdate) {
|
||||
try {
|
||||
clearRemovedShortsTextViews();
|
||||
if (shortsTextViewRefs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ReturnYouTubeDislike videoData = currentVideoData;
|
||||
if (videoData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.printDebug(() -> "updateShortsTextViews");
|
||||
|
||||
Runnable update = () -> {
|
||||
Spanned shortsDislikesSpan = videoData.getDislikeSpanForShort(SHORTS_LOADING_SPAN);
|
||||
Utils.runOnMainThreadNowOrLater(() -> {
|
||||
String videoId = videoData.getVideoId();
|
||||
if (!videoId.equals(VideoInformation.getVideoId())) {
|
||||
// User swiped to new video before fetch completed
|
||||
Logger.printDebug(() -> "Ignoring stale dislikes data for short: " + videoId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update text views that appear to be visible on screen.
|
||||
// Only 1 will be the actual textview for the current Short,
|
||||
// but discarded and not yet garbage collected views can remain.
|
||||
// So must set the dislike span on all views that match.
|
||||
for (WeakReference<TextView> textViewRef : shortsTextViewRefs) {
|
||||
TextView textView = textViewRef.get();
|
||||
if (textView == null) {
|
||||
continue;
|
||||
}
|
||||
if (isShortTextViewOnScreen(textView)
|
||||
&& (forceUpdate || textView.getText().toString().equals(SHORTS_LOADING_SPAN.toString()))) {
|
||||
Logger.printDebug(() -> "Setting Shorts TextView to: " + shortsDislikesSpan);
|
||||
textView.setText(shortsDislikesSpan);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
if (videoData.fetchCompleted()) {
|
||||
update.run(); // Network call is completed, no need to wait on background thread.
|
||||
} else {
|
||||
Utils.runOnBackgroundThread(update);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "updateOnScreenShortsTextViews failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a view is within the screen bounds.
|
||||
*/
|
||||
private static boolean isShortTextViewOnScreen(@NonNull View view) {
|
||||
final int[] location = new int[2];
|
||||
view.getLocationInWindow(location);
|
||||
if (location[0] <= 0 && location[1] <= 0) { // Lower bound
|
||||
return false;
|
||||
}
|
||||
Rect windowRect = new Rect();
|
||||
view.getWindowVisibleDisplayFrame(windowRect); // Upper bound
|
||||
return location[0] < windowRect.width() && location[1] < windowRect.height();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Video Id and voting hooks (all players).
|
||||
//
|
||||
@@ -503,8 +377,7 @@ public class ReturnYouTubeDislikePatch {
|
||||
if (videoIdIsShort && (!isShortAndOpeningOrPlaying || !Settings.RYD_SHORTS.get())) {
|
||||
return;
|
||||
}
|
||||
final boolean waitForFetchToComplete = !IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
||||
&& videoIdIsShort && !lastPlayerResponseWasShort;
|
||||
final boolean waitForFetchToComplete = videoIdIsShort && !lastPlayerResponseWasShort;
|
||||
|
||||
Logger.printDebug(() -> "Prefetching RYD for video: " + videoId);
|
||||
ReturnYouTubeDislike fetch = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
@@ -557,12 +430,6 @@ public class ReturnYouTubeDislikePatch {
|
||||
data.setVideoIdIsShort(true);
|
||||
}
|
||||
currentVideoData = data;
|
||||
|
||||
// Current video id hook can be called out of order with the non litho Shorts text view hook.
|
||||
// Must manually update again here.
|
||||
if (isNoneHiddenOrSlidingMinimized) {
|
||||
updateOnScreenShortsTextViews(true);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "newVideoLoaded failure", ex);
|
||||
}
|
||||
@@ -587,7 +454,10 @@ public class ReturnYouTubeDislikePatch {
|
||||
ReturnYouTubeDislike videoData = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||
videoData.setVideoIdIsShort(true);
|
||||
lastLithoShortsVideoData = videoData;
|
||||
lithoShortsShouldUseCurrentData = false;
|
||||
synchronized (ReturnYouTubeDislikePatch.class) {
|
||||
// Use litho Shorts data for the next like and dislike spans.
|
||||
useLithoShortsVideoDataCount = 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean videoIdIsSame(@Nullable ReturnYouTubeDislike fetch, @Nullable String videoId) {
|
||||
@@ -622,13 +492,6 @@ public class ReturnYouTubeDislikePatch {
|
||||
for (Vote v : Vote.values()) {
|
||||
if (v.value == vote) {
|
||||
videoData.sendVote(v);
|
||||
|
||||
if (isNoneHiddenOrMinimized) {
|
||||
if (lastLithoShortsVideoData != null) {
|
||||
lithoShortsShouldUseCurrentData = true;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package app.revanced.extension.youtube.patches;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
@@ -76,7 +78,7 @@ public class ShortsAutoplayPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
|
||||
public static Enum<?> changeShortsRepeatBehavior(@Nullable Enum<?> original) {
|
||||
try {
|
||||
final boolean autoplay;
|
||||
|
||||
@@ -98,17 +100,35 @@ public class ShortsAutoplayPatch {
|
||||
: ShortsLoopBehavior.REPEAT;
|
||||
|
||||
if (behavior.ytEnumValue != null) {
|
||||
Logger.printDebug(() -> behavior.ytEnumValue == original
|
||||
? "Changing Shorts repeat behavior from: " + original.name() + " to: " + behavior.ytEnumValue
|
||||
: "Behavior setting is same as original. Using original: " + original.name()
|
||||
);
|
||||
Logger.printDebug(() -> {
|
||||
String name = (original == null ? "unknown (null)" : original.name());
|
||||
return behavior == original
|
||||
? "Behavior setting is same as original. Using original: " + name
|
||||
: "Changing Shorts repeat behavior from: " + name + " to: " + behavior.name();
|
||||
});
|
||||
|
||||
return behavior.ytEnumValue;
|
||||
}
|
||||
|
||||
if (original == null) {
|
||||
// Cannot return null, as null is used to indicate Short was auto played.
|
||||
// Unpatched app replaces null with unknown enum type (appears to fix for bad api data).
|
||||
Enum<?> unknown = ShortsLoopBehavior.UNKNOWN.ytEnumValue;
|
||||
Logger.printDebug(() -> "Original is null, returning: " + unknown.name());
|
||||
return unknown;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "changeShortsRepeatState failure", ex);
|
||||
Logger.printException(() -> "changeShortsRepeatBehavior failure", ex);
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean isAutoPlay(Enum<?> original) {
|
||||
return ShortsLoopBehavior.SINGLE_PLAY.ytEnumValue == original;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,15 @@ package app.revanced.extension.youtube.patches;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
public class VersionCheckPatch {
|
||||
public static final boolean IS_19_17_OR_GREATER = Utils.getAppVersionName().compareTo("19.17.00") >= 0;
|
||||
public static final boolean IS_19_20_OR_GREATER = Utils.getAppVersionName().compareTo("19.20.00") >= 0;
|
||||
public static final boolean IS_19_21_OR_GREATER = Utils.getAppVersionName().compareTo("19.21.00") >= 0;
|
||||
public static final boolean IS_19_26_OR_GREATER = Utils.getAppVersionName().compareTo("19.26.00") >= 0;
|
||||
public static final boolean IS_19_29_OR_GREATER = Utils.getAppVersionName().compareTo("19.29.00") >= 0;
|
||||
public static final boolean IS_19_34_OR_GREATER = Utils.getAppVersionName().compareTo("19.34.00") >= 0;
|
||||
public static final boolean IS_19_46_OR_GREATER = Utils.getAppVersionName().compareTo("19.46.00") >= 0;
|
||||
private static boolean isVersionOrGreater(String version) {
|
||||
return Utils.getAppVersionName().compareTo(version) >= 0;
|
||||
}
|
||||
|
||||
public static final boolean IS_19_17_OR_GREATER = isVersionOrGreater("19.17.00");
|
||||
public static final boolean IS_19_20_OR_GREATER = isVersionOrGreater("19.20.00");
|
||||
public static final boolean IS_19_21_OR_GREATER = isVersionOrGreater("19.21.00");
|
||||
public static final boolean IS_19_26_OR_GREATER = isVersionOrGreater("19.26.00");
|
||||
public static final boolean IS_19_29_OR_GREATER = isVersionOrGreater("19.29.00");
|
||||
public static final boolean IS_19_34_OR_GREATER = isVersionOrGreater("19.34.00");
|
||||
public static final boolean IS_19_46_OR_GREATER = isVersionOrGreater("19.46.00");
|
||||
}
|
||||
|
||||
@@ -1,11 +1,48 @@
|
||||
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;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class WideSearchbarPatch {
|
||||
|
||||
private static final Boolean WIDE_SEARCHBAR_ENABLED = Settings.WIDE_SEARCHBAR.get();
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
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(
|
||||
null,
|
||||
"|ContainerType|button.eml|"
|
||||
"|ContainerType|button.eml"
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
@@ -43,7 +43,7 @@ final class ButtonsFilter extends Filter {
|
||||
),
|
||||
new StringFilterGroup(
|
||||
Settings.HIDE_DOWNLOAD_BUTTON,
|
||||
"|download_button.eml|"
|
||||
"|download_button.eml"
|
||||
),
|
||||
new StringFilterGroup(
|
||||
Settings.HIDE_PLAYLIST_BUTTON,
|
||||
@@ -51,7 +51,7 @@ final class ButtonsFilter extends Filter {
|
||||
),
|
||||
new StringFilterGroup(
|
||||
Settings.HIDE_CLIP_BUTTON,
|
||||
"|clip_button.eml|"
|
||||
"|clip_button.eml"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -68,15 +68,19 @@ final class ButtonsFilter extends Filter {
|
||||
Settings.HIDE_REMIX_BUTTON,
|
||||
"yt_outline_youtube_shorts_plus"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_THANKS_BUTTON,
|
||||
"yt_outline_dollar_sign_heart"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_ASK_BUTTON,
|
||||
"yt_fill_spark"
|
||||
),
|
||||
// Check for clip button both here and using a path filter,
|
||||
// as there's a chance the path is a generic action button and won't contain 'clip_button'
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_CLIP_BUTTON,
|
||||
"yt_outline_scissors"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_THANKS_BUTTON,
|
||||
"yt_outline_dollar_sign_heart"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,10 +12,12 @@ final class CommentsFilter extends Filter {
|
||||
|
||||
private final StringFilterGroup commentComposer;
|
||||
private final ByteArrayFilterGroup emojiPickerBufferGroup;
|
||||
private final StringFilterGroup filterChipBar;
|
||||
private final ByteArrayFilterGroup aiCommentsSummary;
|
||||
|
||||
public CommentsFilter() {
|
||||
var chatSummary = new StringFilterGroup(
|
||||
Settings.HIDE_COMMENTS_CHAT_SUMMARY,
|
||||
Settings.HIDE_COMMENTS_AI_CHAT_SUMMARY,
|
||||
"live_chat_summary_banner.eml"
|
||||
);
|
||||
|
||||
@@ -58,6 +60,16 @@ final class CommentsFilter extends Filter {
|
||||
"id.comment.quick_emoji.button"
|
||||
);
|
||||
|
||||
filterChipBar = new StringFilterGroup(
|
||||
Settings.HIDE_COMMENTS_AI_SUMMARY,
|
||||
"filter_chip_bar.eml"
|
||||
);
|
||||
|
||||
aiCommentsSummary = new ByteArrayFilterGroup(
|
||||
null,
|
||||
"yt_fill_spark_"
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
chatSummary,
|
||||
commentsByMembers,
|
||||
@@ -65,7 +77,8 @@ final class CommentsFilter extends Filter {
|
||||
createAShort,
|
||||
previewComment,
|
||||
thanksButton,
|
||||
commentComposer
|
||||
commentComposer,
|
||||
filterChipBar
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,6 +97,13 @@ final class CommentsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchedGroup == filterChipBar) {
|
||||
if (aiCommentsSummary.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@ final class DescriptionComponentsFilter extends Filter {
|
||||
"metadata"
|
||||
);
|
||||
|
||||
final StringFilterGroup aiGeneratedVideoSummarySection = new StringFilterGroup(
|
||||
Settings.HIDE_AI_GENERATED_VIDEO_SUMMARY_SECTION,
|
||||
"cell_expandable_metadata.eml"
|
||||
);
|
||||
|
||||
final StringFilterGroup attributesSection = new StringFilterGroup(
|
||||
Settings.HIDE_ATTRIBUTES_SECTION,
|
||||
"gaming_section",
|
||||
@@ -67,6 +72,7 @@ final class DescriptionComponentsFilter extends Filter {
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
aiGeneratedVideoSummarySection,
|
||||
attributesSection,
|
||||
infoCardsSection,
|
||||
howThisWasMadeSection,
|
||||
|
||||
@@ -34,8 +34,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringFilterGroup notifyMe;
|
||||
private final StringFilterGroup singleItemInformationPanel;
|
||||
private final StringFilterGroup expandableMetadata;
|
||||
private final ByteArrayFilterGroup searchResultRecommendations;
|
||||
private final StringFilterGroup searchResultVideo;
|
||||
private final StringFilterGroup compactChannelBarInner;
|
||||
private final StringFilterGroup compactChannelBarInnerButton;
|
||||
private final ByteArrayFilterGroup joinMembershipButton;
|
||||
@@ -75,7 +73,10 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"post_base_wrapper_slim.eml",
|
||||
"poll_post_root.eml",
|
||||
"videos_post_root.eml",
|
||||
"post_shelf_slim.eml"
|
||||
"post_shelf_slim.eml",
|
||||
"videos_post_responsive_root.eml",
|
||||
"text_post_responsive_root.eml",
|
||||
"poll_post_responsive_root.eml"
|
||||
);
|
||||
|
||||
final var communityGuidelines = new StringFilterGroup(
|
||||
@@ -211,7 +212,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
|
||||
compactChannelBarInnerButton = new StringFilterGroup(
|
||||
null,
|
||||
"|button.eml|"
|
||||
"|button.eml"
|
||||
);
|
||||
|
||||
joinMembershipButton = new ByteArrayFilterGroup(
|
||||
@@ -230,14 +231,9 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"mixed_content_shelf"
|
||||
);
|
||||
|
||||
searchResultVideo = new StringFilterGroup(
|
||||
Settings.HIDE_SEARCH_RESULT_RECOMMENDATIONS,
|
||||
"search_video_with_context.eml"
|
||||
);
|
||||
|
||||
searchResultRecommendations = new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SEARCH_RESULT_RECOMMENDATIONS,
|
||||
"endorsement_header_footer"
|
||||
final var searchResultRecommendationLabels = new StringFilterGroup(
|
||||
Settings.HIDE_SEARCH_RESULT_RECOMMENDATION_LABELS,
|
||||
"endorsement_header_footer.eml"
|
||||
);
|
||||
|
||||
horizontalShelves = new StringFilterGroup(
|
||||
@@ -255,7 +251,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
compactChannelBar,
|
||||
communityPosts,
|
||||
paidPromotion,
|
||||
searchResultVideo,
|
||||
searchResultRecommendationLabels,
|
||||
latestPosts,
|
||||
channelWatermark,
|
||||
communityGuidelines,
|
||||
@@ -297,13 +293,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchedGroup == searchResultVideo) {
|
||||
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The groups are excluded from the filter due to the exceptions list below.
|
||||
// Filter them separately here.
|
||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
||||
@@ -454,14 +443,24 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
}
|
||||
|
||||
private static boolean hideShelves() {
|
||||
// If the player is opened while library is selected,
|
||||
// then filter any recommendations below the player.
|
||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()
|
||||
// Or if the search is active while library is selected, then also filter.
|
||||
|| NavigationBar.isSearchBarActive()) {
|
||||
// Horizontal shelves are used for music/game links in video descriptions,
|
||||
// such as https://youtube.com/watch?v=W8kI1na3S2M
|
||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must check search bar after player type, since search results
|
||||
// can be in the background behind an open player.
|
||||
if (NavigationBar.isSearchBarActive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do not hide if the navigation back button is visible,
|
||||
// otherwise the content shelves in the explore/music/courses pages are hidde.
|
||||
if (NavigationBar.isBackButtonVisible()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check navigation button last.
|
||||
// Only filter if the library tab is not selected.
|
||||
// This check is important as the shelf layout is used for the library tab playlists.
|
||||
|
||||
@@ -87,6 +87,10 @@ public final class LithoFilterPatch {
|
||||
* the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads.
|
||||
*/
|
||||
private static final ThreadLocal<ByteBuffer> bufferThreadLocal = new ThreadLocal<>();
|
||||
/**
|
||||
* Results of calling {@link #filter(String, StringBuilder)}.
|
||||
*/
|
||||
private static final ThreadLocal<Boolean> filterResult = new ThreadLocal<>();
|
||||
|
||||
static {
|
||||
for (Filter filter : filters) {
|
||||
@@ -140,11 +144,22 @@ public final class LithoFilterPatch {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean shouldFilter() {
|
||||
Boolean shouldFilter = filterResult.get();
|
||||
return shouldFilter != null && shouldFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point. Called off the main thread, and commonly called by multiple threads at the same time.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean filter(@Nullable String lithoIdentifier, @NonNull StringBuilder pathBuilder) {
|
||||
public static void filter(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
|
||||
filterResult.set(handleFiltering(lithoIdentifier, pathBuilder));
|
||||
}
|
||||
|
||||
private static boolean handleFiltering(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
|
||||
try {
|
||||
if (pathBuilder.length() == 0) {
|
||||
return false;
|
||||
|
||||
@@ -2,12 +2,25 @@ package app.revanced.extension.youtube.patches.components;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||
|
||||
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
|
||||
private static final boolean AVAILABLE_ON_LAUNCH = SpoofVideoStreamsPatch.notSpoofingToAndroid();
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
// Check conditions of launch and now. Otherwise if spoofing is changed
|
||||
// without a restart the setting will show as available when it's not.
|
||||
return AVAILABLE_ON_LAUNCH && SpoofVideoStreamsPatch.notSpoofingToAndroid();
|
||||
}
|
||||
}
|
||||
|
||||
private final ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList();
|
||||
|
||||
private final ByteArrayFilterGroup exception;
|
||||
@@ -27,7 +40,7 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||
|
||||
addPathCallbacks(
|
||||
videoQualityMenuFooter,
|
||||
new StringFilterGroup(null, "overflow_menu_item.eml|")
|
||||
new StringFilterGroup(null, "overflow_menu_item.eml")
|
||||
);
|
||||
|
||||
flyoutFilterGroupList.addAll(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package app.revanced.extension.youtube.patches.components;
|
||||
|
||||
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
|
||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.view.View;
|
||||
@@ -52,6 +51,7 @@ public final class ShortsFilter extends Filter {
|
||||
private final StringFilterGroup suggestedAction;
|
||||
private final ByteArrayFilterGroupList suggestedActionsGroupList = new ByteArrayFilterGroupList();
|
||||
|
||||
private final StringFilterGroup shortsActionBar;
|
||||
private final StringFilterGroup actionButton;
|
||||
private final ByteArrayFilterGroupList videoActionButtonGroupList = new ByteArrayFilterGroupList();
|
||||
|
||||
@@ -141,6 +141,16 @@ public final class ShortsFilter extends Filter {
|
||||
"like_fountain.eml"
|
||||
);
|
||||
|
||||
StringFilterGroup likeButton = new StringFilterGroup(
|
||||
Settings.HIDE_SHORTS_LIKE_BUTTON,
|
||||
"shorts_like_button.eml"
|
||||
);
|
||||
|
||||
StringFilterGroup dislikeButton = new StringFilterGroup(
|
||||
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
|
||||
"shorts_dislike_button.eml"
|
||||
);
|
||||
|
||||
joinButton = new StringFilterGroup(
|
||||
Settings.HIDE_SHORTS_JOIN_BUTTON,
|
||||
"sponsor_button"
|
||||
@@ -156,9 +166,15 @@ public final class ShortsFilter extends Filter {
|
||||
"reel_player_disclosure.eml"
|
||||
);
|
||||
|
||||
shortsActionBar = new StringFilterGroup(
|
||||
null,
|
||||
"shorts_action_bar.eml"
|
||||
);
|
||||
|
||||
actionButton = new StringFilterGroup(
|
||||
null,
|
||||
"shorts_video_action_button.eml"
|
||||
// Can be simply 'button.eml' or 'shorts_video_action_button.eml'
|
||||
"button.eml"
|
||||
);
|
||||
|
||||
suggestedAction = new StringFilterGroup(
|
||||
@@ -167,27 +183,16 @@ public final class ShortsFilter extends Filter {
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
shortsCompactFeedVideoPath, suggestedAction, actionButton, joinButton, subscribeButton,
|
||||
paidPromotionButton, pausedOverlayButtons, channelBar, fullVideoLinkLabel, videoTitle,
|
||||
reelSoundMetadata, soundButton, infoPanel, stickers, likeFountain
|
||||
shortsCompactFeedVideoPath, joinButton, subscribeButton, paidPromotionButton,
|
||||
shortsActionBar, suggestedAction, pausedOverlayButtons, channelBar,
|
||||
fullVideoLinkLabel, videoTitle, reelSoundMetadata, soundButton, infoPanel,
|
||||
stickers, likeFountain, likeButton, dislikeButton
|
||||
);
|
||||
|
||||
//
|
||||
// Action buttons
|
||||
// All other action buttons.
|
||||
//
|
||||
videoActionButtonGroupList.addAll(
|
||||
// This also appears as the path item 'shorts_like_button.eml'
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHORTS_LIKE_BUTTON,
|
||||
"reel_like_button",
|
||||
"reel_like_toggled_button"
|
||||
),
|
||||
// This also appears as the path item 'shorts_dislike_button.eml'
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHORTS_DISLIKE_BUTTON,
|
||||
"reel_dislike_button",
|
||||
"reel_dislike_toggled_button"
|
||||
),
|
||||
new ByteArrayFilterGroup(
|
||||
Settings.HIDE_SHORTS_COMMENTS_BUTTON,
|
||||
"reel_comment_button"
|
||||
@@ -286,9 +291,11 @@ public final class ShortsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Video action buttons (like, dislike, comment, share, remix) have the same path.
|
||||
if (matchedGroup == actionButton) {
|
||||
if (videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
|
||||
// Video action buttons (comment, share, remix) have the same path.
|
||||
// Like and dislike are separate path filters and don't require buffer searching.
|
||||
if (matchedGroup == shortsActionBar) {
|
||||
if (actionButton.check(path).isFiltered()
|
||||
&& videoActionButtonGroupList.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
return false;
|
||||
@@ -392,37 +399,6 @@ public final class ShortsFilter extends Filter {
|
||||
return original;
|
||||
}
|
||||
|
||||
// region Hide the buttons in older versions of YouTube. New versions use Litho.
|
||||
|
||||
public static void hideLikeButton(final View likeButtonView) {
|
||||
// Cannot set the visibility to gone for like/dislike,
|
||||
// as some other unknown YT code also sets the visibility after this hook.
|
||||
//
|
||||
// Setting the view to 0dp works, but that leaves a blank space where
|
||||
// the button was (only relevant for dislikes button).
|
||||
//
|
||||
// Instead remove the view from the parent.
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_LIKE_BUTTON, likeButtonView);
|
||||
}
|
||||
|
||||
public static void hideDislikeButton(final View dislikeButtonView) {
|
||||
Utils.hideViewByRemovingFromParentUnderCondition(Settings.HIDE_SHORTS_DISLIKE_BUTTON, dislikeButtonView);
|
||||
}
|
||||
|
||||
public static void hideShortsCommentsButton(final View commentsButtonView) {
|
||||
hideViewUnderCondition(Settings.HIDE_SHORTS_COMMENTS_BUTTON, commentsButtonView);
|
||||
}
|
||||
|
||||
public static void hideShortsRemixButton(final View remixButtonView) {
|
||||
hideViewUnderCondition(Settings.HIDE_SHORTS_REMIX_BUTTON, remixButtonView);
|
||||
}
|
||||
|
||||
public static void hideShortsShareButton(final View shareButtonView) {
|
||||
hideViewUnderCondition(Settings.HIDE_SHORTS_SHARE_BUTTON, shareButtonView);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
public static void setNavigationBar(PivotBar view) {
|
||||
pivotBarRef = new WeakReference<>(view);
|
||||
}
|
||||
|
||||
@@ -54,12 +54,12 @@ public class CustomPlaybackSpeedPatch {
|
||||
|
||||
static {
|
||||
final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get();
|
||||
|
||||
if (holdSpeed > 0 && holdSpeed <= PLAYBACK_SPEED_MAXIMUM) {
|
||||
TAP_AND_HOLD_SPEED = holdSpeed;
|
||||
} else {
|
||||
showInvalidCustomSpeedToast();
|
||||
Settings.SPEED_TAP_AND_HOLD.resetToDefault();
|
||||
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.get();
|
||||
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.resetToDefault();
|
||||
}
|
||||
|
||||
loadCustomSpeeds();
|
||||
|
||||
@@ -226,6 +226,7 @@ public final class SeekbarColorPatch {
|
||||
}
|
||||
|
||||
private static String loadRawResourceAsString(int resourceId) {
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId);
|
||||
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
|
||||
return scanner.next();
|
||||
@@ -281,6 +282,20 @@ public final class SeekbarColorPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* 19.49+
|
||||
*/
|
||||
public static int[] getPlayerLinearGradient(int[] original, int x0, int y1) {
|
||||
// This hook is used for both the player and the feed.
|
||||
// Feed usage always has x0 and y1 value of zero, and the player is always non zero.
|
||||
if (HIDE_SEEKBAR_THUMBNAIL_ENABLED && x0 == 0 && y1 == 0) {
|
||||
return HIDDEN_SEEKBAR_GRADIENT_COLORS;
|
||||
}
|
||||
return getPlayerLinearGradient(original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Pre 19.49
|
||||
*/
|
||||
public static int[] getPlayerLinearGradient(int[] original) {
|
||||
return SEEKBAR_CUSTOM_COLOR_ENABLED
|
||||
|
||||
@@ -37,7 +37,6 @@ import java.util.concurrent.*;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.ThemeHelper;
|
||||
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
|
||||
import app.revanced.extension.youtube.returnyoutubedislike.requests.RYDVoteData;
|
||||
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
@@ -87,9 +86,6 @@ public class ReturnYouTubeDislike {
|
||||
*/
|
||||
private static final char MIDDLE_SEPARATOR_CHARACTER = '◎'; // 'bullseye'
|
||||
|
||||
private static final boolean IS_SPOOFING_TO_OLD_SEPARATOR_COLOR
|
||||
= SpoofAppVersionPatch.isSpoofingToLessThan("18.10.00");
|
||||
|
||||
/**
|
||||
* Cached lookup of all video ids.
|
||||
*/
|
||||
@@ -118,7 +114,7 @@ public class ReturnYouTubeDislike {
|
||||
private static final Rect middleSeparatorBounds;
|
||||
|
||||
/**
|
||||
* Left separator horizontal padding for Rolling Number layout.
|
||||
* Horizontal padding between the left and middle separator.
|
||||
*/
|
||||
public static final int leftSeparatorShapePaddingPixels;
|
||||
private static final ShapeDrawable leftSeparatorShape;
|
||||
@@ -133,7 +129,7 @@ public class ReturnYouTubeDislike {
|
||||
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3.7f, dp);
|
||||
middleSeparatorBounds = new Rect(0, 0, middleSeparatorSize, middleSeparatorSize);
|
||||
|
||||
leftSeparatorShapePaddingPixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10.0f, dp);
|
||||
leftSeparatorShapePaddingPixels = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8.4f, dp);
|
||||
|
||||
leftSeparatorShape = new ShapeDrawable(new RectShape());
|
||||
leftSeparatorShape.setBounds(leftSeparatorBounds);
|
||||
@@ -184,17 +180,8 @@ public class ReturnYouTubeDislike {
|
||||
* Color of the left and middle separator, based on the color of the right separator.
|
||||
* It's unknown where YT gets the color from, and the values here are approximated by hand.
|
||||
* Ideally, this would be the actual color YT uses at runtime.
|
||||
*
|
||||
* Older versions before the 'Me' library tab use a slightly different color.
|
||||
* If spoofing was previously used and is now turned off,
|
||||
* or an old version was recently upgraded then the old colors are sometimes still used.
|
||||
*/
|
||||
private static int getSeparatorColor() {
|
||||
if (IS_SPOOFING_TO_OLD_SEPARATOR_COLOR) {
|
||||
return ThemeHelper.isDarkTheme()
|
||||
? 0x29AAAAAA // transparent dark gray
|
||||
: 0xFFD9D9D9; // light gray
|
||||
}
|
||||
return ThemeHelper.isDarkTheme()
|
||||
? 0x33FFFFFF
|
||||
: 0xFFD9D9D9;
|
||||
@@ -251,7 +238,7 @@ public class ReturnYouTubeDislike {
|
||||
String leftSeparatorString = getTextDirectionString();
|
||||
final Spannable leftSeparatorSpan;
|
||||
if (isRollingNumber) {
|
||||
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
||||
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
||||
} else {
|
||||
leftSeparatorString += " ";
|
||||
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
||||
@@ -636,7 +623,7 @@ public class ReturnYouTubeDislike {
|
||||
userVote = vote;
|
||||
clearUICache();
|
||||
}
|
||||
|
||||
|
||||
if (future.isDone()) {
|
||||
// Update the fetched vote data.
|
||||
RYDVoteData voteData = getFetchData(MAX_MILLISECONDS_TO_BLOCK_UI_WAITING_FOR_FETCH);
|
||||
|
||||
@@ -84,6 +84,7 @@ public class LicenseActivityHook {
|
||||
public static void initialize(Activity licenseActivity) {
|
||||
try {
|
||||
ThemeHelper.setActivityTheme(licenseActivity);
|
||||
ThemeHelper.setNavigationBarColor(licenseActivity.getWindow());
|
||||
licenseActivity.setContentView(getResourceIdentifier(
|
||||
"revanced_settings_with_toolbar", "layout"));
|
||||
|
||||
@@ -126,7 +127,7 @@ public class LicenseActivityHook {
|
||||
// This is required to fix submenu title alignment issue with Android ASOP 15+
|
||||
ViewGroup toolBarParent = activity.findViewById(
|
||||
getResourceIdentifier("revanced_toolbar_parent", "id"));
|
||||
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
|
||||
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent, "revanced_toolbar");
|
||||
toolbarLayoutParams = dummyToolbar.getLayoutParams();
|
||||
toolBarParent.removeView(dummyToolbar);
|
||||
|
||||
@@ -149,5 +150,4 @@ public class LicenseActivityHook {
|
||||
|
||||
toolBarParent.addView(toolbar, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,14 @@ package app.revanced.extension.youtube.settings;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static app.revanced.extension.shared.settings.Setting.Availability;
|
||||
import static app.revanced.extension.shared.settings.Setting.migrateFromOldPreferences;
|
||||
import static app.revanced.extension.shared.settings.Setting.migrateOldSettingToNew;
|
||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||
import static app.revanced.extension.shared.settings.Setting.parentsAny;
|
||||
import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormFactor;
|
||||
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.ChangeStartPageTypeAvailability;
|
||||
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
||||
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
|
||||
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MINIMAL;
|
||||
@@ -21,11 +20,12 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
|
||||
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
|
||||
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
|
||||
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_19_17_OR_GREATER;
|
||||
import static app.revanced.extension.youtube.patches.components.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
|
||||
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
@@ -38,11 +38,11 @@ import app.revanced.extension.shared.settings.IntegerSetting;
|
||||
import app.revanced.extension.shared.settings.LongSetting;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.shared.settings.StringSetting;
|
||||
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
|
||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrowAvailability;
|
||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
|
||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
|
||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
|
||||
import app.revanced.extension.youtube.patches.MiniplayerPatch;
|
||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||
|
||||
public class Settings extends BaseSettings {
|
||||
@@ -103,7 +103,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_MOVIES_SECTION = new BooleanSetting("revanced_hide_movies_section", TRUE);
|
||||
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
|
||||
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
|
||||
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
|
||||
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATION_LABELS = new BooleanSetting("revanced_hide_search_result_recommendation_labels", TRUE);
|
||||
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
|
||||
// Alternative thumbnails
|
||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
|
||||
@@ -126,6 +126,7 @@ public class Settings extends BaseSettings {
|
||||
// Player
|
||||
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
||||
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
||||
public static final BooleanSetting DISABLE_AUTO_CAPTIONS = new BooleanSetting("revanced_disable_auto_captions", FALSE, true);
|
||||
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
|
||||
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
||||
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
|
||||
@@ -138,6 +139,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
|
||||
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
|
||||
public static final BooleanSetting HIDE_END_SCREEN_SUGGESTED_VIDEO = new BooleanSetting("revanced_end_screen_suggested_video", FALSE, true);
|
||||
public static final BooleanSetting HIDE_RELATED_VIDEO_OVERLAY = new BooleanSetting("revanced_hide_related_video_overlay", FALSE, true);
|
||||
public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE);
|
||||
public static final BooleanSetting HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
|
||||
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
|
||||
@@ -159,7 +161,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting MINIPLAYER_DOUBLE_TAP_ACTION = new BooleanSetting("revanced_miniplayer_double_tap_action", TRUE, true, MINIPLAYER_ANY_MODERN);
|
||||
public static final BooleanSetting MINIPLAYER_DRAG_AND_DROP = new BooleanSetting("revanced_miniplayer_drag_and_drop", TRUE, true, MINIPLAYER_ANY_MODERN);
|
||||
public static final BooleanSetting MINIPLAYER_HORIZONTAL_DRAG = new BooleanSetting("revanced_miniplayer_horizontal_drag", FALSE, true, new MiniplayerHorizontalDragAvailability());
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, new MiniplayerHideExpandCloseAvailability());
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_OVERLAY_BUTTONS = new BooleanSetting("revanced_miniplayer_hide_overlay_buttons", FALSE, true, new MiniplayerPatch.MiniplayerHideOverlayButtonsAvailability());
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, MINIPLAYER_TYPE.availability(MODERN_1));
|
||||
public static final BooleanSetting MINIPLAYER_ROUNDED_CORNERS = new BooleanSetting("revanced_miniplayer_rounded_corners", TRUE, true, MINIPLAYER_ANY_MODERN);
|
||||
@@ -171,7 +173,8 @@ public class Settings extends BaseSettings {
|
||||
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name",
|
||||
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
|
||||
// Comments
|
||||
public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_summary", FALSE);
|
||||
public static final BooleanSetting HIDE_COMMENTS_AI_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_ai_chat_summary", FALSE);
|
||||
public static final BooleanSetting HIDE_COMMENTS_AI_SUMMARY = new BooleanSetting("revanced_hide_comments_ai_summary", FALSE);
|
||||
public static final BooleanSetting HIDE_COMMENTS_BY_MEMBERS_HEADER = new BooleanSetting("revanced_hide_comments_by_members_header", FALSE);
|
||||
public static final BooleanSetting HIDE_COMMENTS_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE);
|
||||
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
|
||||
@@ -179,6 +182,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE);
|
||||
public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE);
|
||||
// Description
|
||||
public static final BooleanSetting HIDE_AI_GENERATED_VIDEO_SUMMARY_SECTION = new BooleanSetting("revanced_hide_ai_generated_video_summary_section", FALSE);
|
||||
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE);
|
||||
public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE);
|
||||
public static final BooleanSetting HIDE_HOW_THIS_WAS_MADE_SECTION = new BooleanSetting("revanced_hide_how_this_was_made_section", FALSE);
|
||||
@@ -196,10 +200,11 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
|
||||
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
|
||||
public static final BooleanSetting HIDE_ASK_BUTTON = new BooleanSetting("revanced_hide_ask_button", FALSE);
|
||||
// Player flyout menu items
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE, new HideAudioFlyoutMenuAvailability());
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_CAPTIONS = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_HELP = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
|
||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
||||
@@ -221,7 +226,9 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
||||
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
||||
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
|
||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", IS_19_17_OR_GREATER ? "19.26.42" : "17.33.42", true, parent(SPOOF_APP_VERSION));
|
||||
public static final BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
|
||||
new ChangeStartPageTypeAvailability());
|
||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
|
||||
// Custom filter
|
||||
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
||||
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
||||
@@ -234,7 +241,8 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_hide_notifications_button", FALSE, true);
|
||||
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true,
|
||||
"revanced_switch_create_with_notifications_button_user_dialog_message");
|
||||
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true);
|
||||
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true,
|
||||
"revanced_disable_translucent_status_bar_user_dialog_message");
|
||||
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true);
|
||||
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true);
|
||||
|
||||
@@ -293,7 +301,6 @@ public class Settings extends BaseSettings {
|
||||
// Misc
|
||||
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
|
||||
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
||||
public static final BooleanSetting AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
|
||||
public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
|
||||
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
||||
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
|
||||
@@ -318,12 +325,15 @@ public class Settings extends BaseSettings {
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
|
||||
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
|
||||
public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL,true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
|
||||
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final StringSetting SWIPE_OVERLAY_PROGRESS_COLOR = new StringSetting("revanced_swipe_overlay_progress_color", "#FFFFFF", true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
|
||||
@@ -365,47 +375,47 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false);
|
||||
public static final StringSetting SB_CATEGORY_SPONSOR = new StringSetting("sb_sponsor", SKIP_AUTOMATICALLY_ONCE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_SPONSOR_COLOR = new StringSetting("sb_sponsor_color", "#00D400");
|
||||
public static final FloatSetting SB_CATEGORY_SPONSOR_OPACITY = new FloatSetting("sb_sponsor_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_SELF_PROMO = new StringSetting("sb_selfpromo", MANUAL_SKIP.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_SELF_PROMO_COLOR = new StringSetting("sb_selfpromo_color", "#FFFF00");
|
||||
public static final FloatSetting SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_INTERACTION = new StringSetting("sb_interaction", MANUAL_SKIP.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF");
|
||||
public static final FloatSetting SB_CATEGORY_INTERACTION_OPACITY = new FloatSetting("sb_interaction_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684");
|
||||
public static final FloatSetting SB_CATEGORY_HIGHLIGHT_OPACITY = new FloatSetting("sb_highlight_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", MANUAL_SKIP.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF");
|
||||
public static final FloatSetting SB_CATEGORY_INTRO_OPACITY = new FloatSetting("sb_intro_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_OUTRO = new StringSetting("sb_outro", MANUAL_SKIP.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED");
|
||||
public static final FloatSetting SB_CATEGORY_OUTRO_OPACITY = new FloatSetting("sb_outro_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_PREVIEW = new StringSetting("sb_preview", IGNORE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6");
|
||||
public static final FloatSetting SB_CATEGORY_PREVIEW_OPACITY = new FloatSetting("sb_preview_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_FILLER = new StringSetting("sb_filler", IGNORE.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF");
|
||||
public static final FloatSetting SB_CATEGORY_FILLER_OPACITY = new FloatSetting("sb_filler_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC = new StringSetting("sb_music_offtopic", MANUAL_SKIP.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_MUSIC_OFFTOPIC_COLOR = new StringSetting("sb_music_offtopic_color", "#FF9900");
|
||||
public static final FloatSetting SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY = new FloatSetting("sb_music_offtopic_opacity", 0.8f);
|
||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
|
||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
|
||||
public static final FloatSetting SB_CATEGORY_UNSUBMITTED_OPACITY = new FloatSetting("sb_unsubmitted_opacity", 1.0f);
|
||||
|
||||
// Deprecated migrations
|
||||
private static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024
|
||||
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true);
|
||||
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", FALSE);
|
||||
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
|
||||
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
|
||||
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
|
||||
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
|
||||
private static final BooleanSetting DEPRECATED_AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
|
||||
|
||||
static {
|
||||
// region Migration
|
||||
|
||||
// Do _not_ delete this SB private user id migration property until sometime in early 2025.
|
||||
// This is the only setting that cannot be reconfigured if lost,
|
||||
// and more time should be given for users who rarely upgrade.
|
||||
SharedPrefCategory sbPrefs = new SharedPrefCategory("sponsor-block");
|
||||
// Remove the "sb_" prefix, as old settings are saved without it.
|
||||
String key = DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING.key.substring(3);
|
||||
migrateFromOldPreferences(sbPrefs, DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING, key);
|
||||
|
||||
migrateOldSettingToNew(DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING, SB_PRIVATE_USER_ID);
|
||||
|
||||
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_BUTTONS, HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS);
|
||||
|
||||
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
|
||||
@@ -449,6 +459,17 @@ public class Settings extends BaseSettings {
|
||||
DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.resetToDefault();
|
||||
}
|
||||
|
||||
// Old spoof versions that no longer work.
|
||||
if (SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0) {
|
||||
Logger.printInfo(() -> "Resetting spoof app version target");
|
||||
SPOOF_APP_VERSION_TARGET.resetToDefault();
|
||||
}
|
||||
|
||||
if (!DEPRECATED_AUTO_CAPTIONS.isSetToDefault()) {
|
||||
DISABLE_AUTO_CAPTIONS.save(true);
|
||||
DEPRECATED_AUTO_CAPTIONS.resetToDefault();
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region SB import/export callbacks
|
||||
@@ -458,4 +479,3 @@ public class Settings extends BaseSettings {
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package app.revanced.extension.youtube.settings.preference;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class HideAudioFlyoutMenuPreference extends SwitchPreference {
|
||||
|
||||
{
|
||||
// Audio menu is not available if spoofing to Android client type.
|
||||
if (!SpoofVideoStreamsPatch.notSpoofingToAndroid()) {
|
||||
String summary = str("revanced_hide_player_flyout_audio_track_not_available");
|
||||
setSummary(summary);
|
||||
setSummaryOn(summary);
|
||||
setSummaryOff(summary);
|
||||
}
|
||||
}
|
||||
|
||||
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
public HideAudioFlyoutMenuPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
@@ -74,7 +73,8 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(pairsToSort, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
|
||||
pairsToSort.sort((pair1, pair2)
|
||||
-> pair1.first.compareToIgnoreCase(pair2.first));
|
||||
|
||||
CharSequence[] sortedEntries = new CharSequence[entrySize];
|
||||
CharSequence[] sortedEntryValues = new CharSequence[entrySize];
|
||||
@@ -109,6 +109,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
|
||||
}
|
||||
|
||||
sortPreferenceListMenu(Settings.CHANGE_START_PAGE);
|
||||
sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE);
|
||||
sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE);
|
||||
} catch (Exception ex) {
|
||||
@@ -137,11 +138,16 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
.findViewById(android.R.id.content)
|
||||
.getParent();
|
||||
|
||||
// Fix required for Android 15 and YT 19.45+
|
||||
// Fix the system navigation bar color for submenus.
|
||||
ThemeHelper.setNavigationBarColor(preferenceScreenDialog.getWindow());
|
||||
|
||||
// Fix edge-to-edge screen with Android 15 and YT 19.45+
|
||||
// https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
rootView.setOnApplyWindowInsetsListener((v, insets) -> {
|
||||
Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars());
|
||||
v.setPadding(0, statusInsets.top, 0, 0);
|
||||
Insets navInsets = insets.getInsets(WindowInsets.Type.navigationBars());
|
||||
v.setPadding(0, statusInsets.top, 0, navInsets.bottom);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import android.preference.PreferenceScreen;
|
||||
import android.preference.SwitchPreference;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch;
|
||||
@@ -85,9 +86,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
||||
shortsPreference = new SwitchPreference(context);
|
||||
shortsPreference.setChecked(Settings.RYD_SHORTS.get());
|
||||
shortsPreference.setTitle(str("revanced_ryd_shorts_title"));
|
||||
String shortsSummary = ReturnYouTubeDislikePatch.IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
||||
? str("revanced_ryd_shorts_summary_on")
|
||||
: str("revanced_ryd_shorts_summary_on_disclaimer");
|
||||
String shortsSummary = str("revanced_ryd_shorts_summary_on_disclaimer");
|
||||
shortsPreference.setSummaryOn(shortsSummary);
|
||||
shortsPreference.setSummaryOff(str("revanced_ryd_shorts_summary_off"));
|
||||
shortsPreference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
@@ -237,6 +236,8 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
||||
"revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_non_zero_summary"));
|
||||
preferenceScreen.addPreference(statisticPreference);
|
||||
}
|
||||
|
||||
Utils.setPreferenceTitlesToMultiLineIfNeeded(preferenceScreen);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onCreate failure", ex);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.annotation.Nullable;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.shared.settings.preference.ResettableEditTextPreference;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
|
||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||
@@ -44,8 +45,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
private SwitchPreference showTimeWithoutSegments;
|
||||
private SwitchPreference toastOnConnectionError;
|
||||
|
||||
private EditTextPreference newSegmentStep;
|
||||
private EditTextPreference minSegmentDuration;
|
||||
private ResettableEditTextPreference newSegmentStep;
|
||||
private ResettableEditTextPreference minSegmentDuration;
|
||||
private EditTextPreference privateUserId;
|
||||
private EditTextPreference importExport;
|
||||
private Preference apiUrl;
|
||||
@@ -159,6 +160,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
|
||||
addAboutCategory(context, preferenceScreen);
|
||||
|
||||
Utils.setPreferenceTitlesToMultiLineIfNeeded(preferenceScreen);
|
||||
|
||||
updateUI();
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onCreate failure", ex);
|
||||
@@ -268,7 +271,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
return true;
|
||||
});
|
||||
|
||||
newSegmentStep = new EditTextPreference(context);
|
||||
newSegmentStep = new ResettableEditTextPreference(context);
|
||||
newSegmentStep.setSetting(Settings.SB_CREATE_NEW_SEGMENT_STEP);
|
||||
newSegmentStep.setTitle(str("revanced_sb_general_adjusting"));
|
||||
newSegmentStep.setSummary(str("revanced_sb_general_adjusting_sum"));
|
||||
newSegmentStep.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
@@ -326,7 +330,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
});
|
||||
category.addPreference(trackSkips);
|
||||
|
||||
minSegmentDuration = new EditTextPreference(context);
|
||||
minSegmentDuration = new ResettableEditTextPreference(context);
|
||||
minSegmentDuration.setSetting(Settings.SB_SEGMENT_MIN_DURATION);
|
||||
minSegmentDuration.setTitle(str("revanced_sb_general_min_duration"));
|
||||
minSegmentDuration.setSummary(str("revanced_sb_general_min_duration_sum"));
|
||||
minSegmentDuration.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
||||
@@ -345,7 +350,15 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
});
|
||||
category.addPreference(minSegmentDuration);
|
||||
|
||||
privateUserId = new EditTextPreference(context);
|
||||
privateUserId = new EditTextPreference(context) {
|
||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||
Utils.setEditTextDialogTheme(builder);
|
||||
|
||||
builder.setNeutralButton(str("revanced_sb_settings_copy"), (dialog, which) -> {
|
||||
Utils.setClipboard(getEditText().getText().toString());
|
||||
});
|
||||
}
|
||||
};
|
||||
privateUserId.setTitle(str("revanced_sb_general_uuid"));
|
||||
privateUserId.setSummary(str("revanced_sb_general_uuid_sum"));
|
||||
privateUserId.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
@@ -504,7 +517,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
|
||||
if (stats.totalSegmentCountIncludingIgnored > 0) {
|
||||
// If user has not created any segments, there's no reason to set a username.
|
||||
EditTextPreference preference = new EditTextPreference(context);
|
||||
EditTextPreference preference = new ResettableEditTextPreference(context);
|
||||
statsCategory.addPreference(preference);
|
||||
String userName = stats.userName;
|
||||
preference.setTitle(fromHtml(str("revanced_sb_stats_username", userName)));
|
||||
|
||||
@@ -73,6 +73,7 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
||||
if (currentClientType == clientType) {
|
||||
return;
|
||||
}
|
||||
currentClientType = clientType;
|
||||
|
||||
Logger.printDebug(() -> "Updating spoof stream side effects preference");
|
||||
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
|
||||
|
||||
@@ -136,7 +136,7 @@ public class SponsorBlockSettings {
|
||||
for (SegmentCategory category : categories) {
|
||||
JSONObject categoryObject = new JSONObject();
|
||||
String categoryKey = category.keyValue;
|
||||
categoryObject.put("color", category.colorString());
|
||||
categoryObject.put("color", category.getColorString());
|
||||
barTypesObject.put(categoryKey, categoryObject);
|
||||
|
||||
if (category.behaviour != CategoryBehaviour.IGNORE) {
|
||||
|
||||
@@ -5,7 +5,12 @@ import static app.revanced.extension.shared.StringRef.str;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.text.Html;
|
||||
import android.graphics.Color;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -33,7 +38,7 @@ import app.revanced.extension.youtube.sponsorblock.ui.SponsorBlockViewController
|
||||
* Not thread safe. All fields/methods must be accessed from the main thread.
|
||||
*/
|
||||
public class SponsorBlockUtils {
|
||||
private static final String LOCKED_COLOR = "#FFC83D";
|
||||
private static final int LOCKED_COLOR = Color.parseColor("#FFC83D");
|
||||
private static final String MANUAL_EDIT_TIME_TEXT_HINT = "hh:mm:ss.sss";
|
||||
private static final Pattern manualEditTimePattern
|
||||
= Pattern.compile("((\\d{1,2}):)?(\\d{1,2}):(\\d{2})(\\.(\\d{1,3}))?");
|
||||
@@ -160,32 +165,34 @@ public class SponsorBlockUtils {
|
||||
SegmentVote[] voteOptions = (segment.category == SegmentCategory.HIGHLIGHT)
|
||||
? SegmentVote.voteTypesWithoutCategoryChange // highlight segments cannot change category
|
||||
: SegmentVote.values();
|
||||
CharSequence[] items = new CharSequence[voteOptions.length];
|
||||
final int voteOptionsLength = voteOptions.length;
|
||||
final boolean userIsVip = Settings.SB_USER_IS_VIP.get();
|
||||
CharSequence[] items = new CharSequence[voteOptionsLength];
|
||||
|
||||
for (int i = 0; i < voteOptions.length; i++) {
|
||||
for (int i = 0; i < voteOptionsLength; i++) {
|
||||
SegmentVote voteOption = voteOptions[i];
|
||||
String title = voteOption.title.toString();
|
||||
if (Settings.SB_USER_IS_VIP.get() && segment.isLocked && voteOption.shouldHighlight) {
|
||||
items[i] = Html.fromHtml(String.format("<font color=\"%s\">%s</font>", LOCKED_COLOR, title));
|
||||
} else {
|
||||
items[i] = title;
|
||||
CharSequence title = voteOption.title.toString();
|
||||
if (userIsVip && segment.isLocked && voteOption.highlightIfVipAndVideoIsLocked) {
|
||||
SpannableString coloredTitle = new SpannableString(title);
|
||||
coloredTitle.setSpan(new ForegroundColorSpan(LOCKED_COLOR),
|
||||
0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
title = coloredTitle;
|
||||
}
|
||||
items[i] = title;
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(context)
|
||||
.setItems(items, (dialog1, which1) -> {
|
||||
SegmentVote voteOption = voteOptions[which1];
|
||||
switch (voteOption) {
|
||||
case UPVOTE:
|
||||
case DOWNVOTE:
|
||||
SBRequester.voteForSegmentOnBackgroundThread(segment, voteOption);
|
||||
break;
|
||||
case CATEGORY_CHANGE:
|
||||
onNewCategorySelect(segment, context);
|
||||
break;
|
||||
}
|
||||
})
|
||||
.show();
|
||||
new AlertDialog.Builder(context).setItems(items, (dialog1, which1) -> {
|
||||
SegmentVote voteOption = voteOptions[which1];
|
||||
switch (voteOption) {
|
||||
case UPVOTE:
|
||||
case DOWNVOTE:
|
||||
SBRequester.voteForSegmentOnBackgroundThread(segment, voteOption);
|
||||
break;
|
||||
case CATEGORY_CHANGE:
|
||||
onNewCategorySelect(segment, context);
|
||||
break;
|
||||
}
|
||||
}).show();
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "segmentVoteClickListener failure", ex);
|
||||
}
|
||||
@@ -282,7 +289,6 @@ public class SponsorBlockUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final int numberOfSegments = segments.length;
|
||||
CharSequence[] titles = new CharSequence[numberOfSegments];
|
||||
for (int i = 0; i < numberOfSegments; i++) {
|
||||
@@ -290,22 +296,33 @@ public class SponsorBlockUtils {
|
||||
if (segment.category == SegmentCategory.UNSUBMITTED) {
|
||||
continue;
|
||||
}
|
||||
StringBuilder htmlBuilder = new StringBuilder();
|
||||
htmlBuilder.append(String.format("<b><font color=\"#%06X\">⬤</font> %s<br>",
|
||||
segment.category.color, segment.category.title));
|
||||
htmlBuilder.append(formatSegmentTime(segment.start));
|
||||
if (segment.category != SegmentCategory.HIGHLIGHT) {
|
||||
htmlBuilder.append(" to ").append(formatSegmentTime(segment.end));
|
||||
|
||||
SpannableStringBuilder spannableBuilder = new SpannableStringBuilder();
|
||||
|
||||
spannableBuilder.append(segment.category.getTitleWithColorDot());
|
||||
spannableBuilder.append('\n');
|
||||
|
||||
String startTime = formatSegmentTime(segment.start);
|
||||
if (segment.category == SegmentCategory.HIGHLIGHT) {
|
||||
spannableBuilder.append(startTime);
|
||||
} else {
|
||||
String toFromString = str("revanced_sb_vote_segment_time_to_from",
|
||||
startTime, formatSegmentTime(segment.end));
|
||||
spannableBuilder.append(toFromString);
|
||||
}
|
||||
htmlBuilder.append("</b>");
|
||||
if (i + 1 != numberOfSegments) // prevents trailing new line after last segment
|
||||
htmlBuilder.append("<br>");
|
||||
titles[i] = Html.fromHtml(htmlBuilder.toString());
|
||||
|
||||
if (i + 1 != numberOfSegments) {
|
||||
// prevents trailing new line after last segment
|
||||
spannableBuilder.append('\n');
|
||||
}
|
||||
|
||||
spannableBuilder.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
|
||||
0, spannableBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
titles[i] = spannableBuilder;
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(context)
|
||||
.setItems(titles, segmentVoteClickListener)
|
||||
.show();
|
||||
new AlertDialog.Builder(context).setItems(titles, segmentVoteClickListener).show();
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onVotingClicked failure", ex);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package app.revanced.extension.youtube.sponsorblock.objects;
|
||||
|
||||
import static app.revanced.extension.youtube.settings.Settings.*;
|
||||
import static app.revanced.extension.shared.StringRef.sf;
|
||||
import static app.revanced.extension.youtube.settings.Settings.*;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -15,43 +16,45 @@ import androidx.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.StringSetting;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.StringRef;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.FloatSetting;
|
||||
import app.revanced.extension.shared.settings.StringSetting;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
public enum SegmentCategory {
|
||||
SPONSOR("sponsor", sf("revanced_sb_segments_sponsor"), sf("revanced_sb_segments_sponsor_sum"), sf("revanced_sb_skip_button_sponsor"), sf("revanced_sb_skipped_sponsor"),
|
||||
SB_CATEGORY_SPONSOR, SB_CATEGORY_SPONSOR_COLOR),
|
||||
SB_CATEGORY_SPONSOR, SB_CATEGORY_SPONSOR_COLOR, SB_CATEGORY_SPONSOR_OPACITY),
|
||||
SELF_PROMO("selfpromo", sf("revanced_sb_segments_selfpromo"), sf("revanced_sb_segments_selfpromo_sum"), sf("revanced_sb_skip_button_selfpromo"), sf("revanced_sb_skipped_selfpromo"),
|
||||
SB_CATEGORY_SELF_PROMO, SB_CATEGORY_SELF_PROMO_COLOR),
|
||||
SB_CATEGORY_SELF_PROMO, SB_CATEGORY_SELF_PROMO_COLOR, SB_CATEGORY_SELF_PROMO_OPACITY),
|
||||
INTERACTION("interaction", sf("revanced_sb_segments_interaction"), sf("revanced_sb_segments_interaction_sum"), sf("revanced_sb_skip_button_interaction"), sf("revanced_sb_skipped_interaction"),
|
||||
SB_CATEGORY_INTERACTION, SB_CATEGORY_INTERACTION_COLOR),
|
||||
SB_CATEGORY_INTERACTION, SB_CATEGORY_INTERACTION_COLOR, SB_CATEGORY_INTERACTION_OPACITY),
|
||||
/**
|
||||
* Unique category that is treated differently than the rest.
|
||||
*/
|
||||
HIGHLIGHT("poi_highlight", sf("revanced_sb_segments_highlight"), sf("revanced_sb_segments_highlight_sum"), sf("revanced_sb_skip_button_highlight"), sf("revanced_sb_skipped_highlight"),
|
||||
SB_CATEGORY_HIGHLIGHT, SB_CATEGORY_HIGHLIGHT_COLOR),
|
||||
SB_CATEGORY_HIGHLIGHT, SB_CATEGORY_HIGHLIGHT_COLOR, SB_CATEGORY_HIGHLIGHT_OPACITY),
|
||||
INTRO("intro", sf("revanced_sb_segments_intro"), sf("revanced_sb_segments_intro_sum"),
|
||||
sf("revanced_sb_skip_button_intro_beginning"), sf("revanced_sb_skip_button_intro_middle"), sf("revanced_sb_skip_button_intro_end"),
|
||||
sf("revanced_sb_skipped_intro_beginning"), sf("revanced_sb_skipped_intro_middle"), sf("revanced_sb_skipped_intro_end"),
|
||||
SB_CATEGORY_INTRO, SB_CATEGORY_INTRO_COLOR),
|
||||
SB_CATEGORY_INTRO, SB_CATEGORY_INTRO_COLOR, SB_CATEGORY_INTRO_OPACITY),
|
||||
OUTRO("outro", sf("revanced_sb_segments_outro"), sf("revanced_sb_segments_outro_sum"), sf("revanced_sb_skip_button_outro"), sf("revanced_sb_skipped_outro"),
|
||||
SB_CATEGORY_OUTRO, SB_CATEGORY_OUTRO_COLOR),
|
||||
SB_CATEGORY_OUTRO, SB_CATEGORY_OUTRO_COLOR, SB_CATEGORY_OUTRO_OPACITY),
|
||||
PREVIEW("preview", sf("revanced_sb_segments_preview"), sf("revanced_sb_segments_preview_sum"),
|
||||
sf("revanced_sb_skip_button_preview_beginning"), sf("revanced_sb_skip_button_preview_middle"), sf("revanced_sb_skip_button_preview_end"),
|
||||
sf("revanced_sb_skipped_preview_beginning"), sf("revanced_sb_skipped_preview_middle"), sf("revanced_sb_skipped_preview_end"),
|
||||
SB_CATEGORY_PREVIEW, SB_CATEGORY_PREVIEW_COLOR),
|
||||
SB_CATEGORY_PREVIEW, SB_CATEGORY_PREVIEW_COLOR, SB_CATEGORY_PREVIEW_OPACITY),
|
||||
FILLER("filler", sf("revanced_sb_segments_filler"), sf("revanced_sb_segments_filler_sum"), sf("revanced_sb_skip_button_filler"), sf("revanced_sb_skipped_filler"),
|
||||
SB_CATEGORY_FILLER, SB_CATEGORY_FILLER_COLOR),
|
||||
SB_CATEGORY_FILLER, SB_CATEGORY_FILLER_COLOR, SB_CATEGORY_FILLER_OPACITY),
|
||||
MUSIC_OFFTOPIC("music_offtopic", sf("revanced_sb_segments_nomusic"), sf("revanced_sb_segments_nomusic_sum"), sf("revanced_sb_skip_button_nomusic"), sf("revanced_sb_skipped_nomusic"),
|
||||
SB_CATEGORY_MUSIC_OFFTOPIC, SB_CATEGORY_MUSIC_OFFTOPIC_COLOR),
|
||||
SB_CATEGORY_MUSIC_OFFTOPIC, SB_CATEGORY_MUSIC_OFFTOPIC_COLOR, SB_CATEGORY_MUSIC_OFFTOPIC_OPACITY),
|
||||
UNSUBMITTED("unsubmitted", StringRef.empty, StringRef.empty, sf("revanced_sb_skip_button_unsubmitted"), sf("revanced_sb_skipped_unsubmitted"),
|
||||
SB_CATEGORY_UNSUBMITTED, SB_CATEGORY_UNSUBMITTED_COLOR),;
|
||||
SB_CATEGORY_UNSUBMITTED, SB_CATEGORY_UNSUBMITTED_COLOR, SB_CATEGORY_UNSUBMITTED_OPACITY);
|
||||
|
||||
private static final StringRef skipSponsorTextCompact = sf("revanced_sb_skip_button_compact");
|
||||
private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight");
|
||||
@@ -90,12 +93,10 @@ public enum SegmentCategory {
|
||||
mValuesMap.put(value.keyValue, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static SegmentCategory[] categoriesWithoutUnsubmitted() {
|
||||
return categoriesWithoutUnsubmitted;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static SegmentCategory[] categoriesWithoutHighlights() {
|
||||
return categoriesWithoutHighlights;
|
||||
}
|
||||
@@ -106,7 +107,7 @@ public enum SegmentCategory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called if behavior of any category is changed
|
||||
* Must be called if behavior of any category is changed.
|
||||
*/
|
||||
public static void updateEnabledCategories() {
|
||||
Utils.verifyOnMainThread();
|
||||
@@ -133,32 +134,33 @@ public enum SegmentCategory {
|
||||
updateEnabledCategories();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public final String keyValue;
|
||||
@NonNull
|
||||
public final StringSetting behaviorSetting;
|
||||
@NonNull
|
||||
private final StringSetting colorSetting;
|
||||
public static int applyOpacityToColor(int color, float opacity) {
|
||||
if (opacity < 0 || opacity > 1.0f) {
|
||||
throw new IllegalArgumentException("Invalid opacity: " + opacity);
|
||||
}
|
||||
final int opacityInt = (int) (255 * opacity);
|
||||
return (color & 0x00FFFFFF) | (opacityInt << 24);
|
||||
}
|
||||
|
||||
public final String keyValue;
|
||||
public final StringSetting behaviorSetting; // TODO: Replace with EnumSetting.
|
||||
private final StringSetting colorSetting;
|
||||
private final FloatSetting opacitySetting;
|
||||
|
||||
@NonNull
|
||||
public final StringRef title;
|
||||
@NonNull
|
||||
public final StringRef description;
|
||||
|
||||
/**
|
||||
* Skip button text, if the skip occurs in the first quarter of the video
|
||||
*/
|
||||
@NonNull
|
||||
public final StringRef skipButtonTextBeginning;
|
||||
/**
|
||||
* Skip button text, if the skip occurs in the middle half of the video
|
||||
*/
|
||||
@NonNull
|
||||
public final StringRef skipButtonTextMiddle;
|
||||
/**
|
||||
* Skip button text, if the skip occurs in the last quarter of the video
|
||||
*/
|
||||
@NonNull
|
||||
public final StringRef skipButtonTextEnd;
|
||||
/**
|
||||
* Skipped segment toast, if the skip occurred in the first quarter of the video
|
||||
@@ -179,10 +181,7 @@ public enum SegmentCategory {
|
||||
@NonNull
|
||||
public final Paint paint;
|
||||
|
||||
/**
|
||||
* Value must be changed using {@link #setColor(String)}.
|
||||
*/
|
||||
public int color;
|
||||
private int color;
|
||||
|
||||
/**
|
||||
* Value must be changed using {@link #setBehaviour(CategoryBehaviour)}.
|
||||
@@ -194,17 +193,20 @@ public enum SegmentCategory {
|
||||
SegmentCategory(String keyValue, StringRef title, StringRef description,
|
||||
StringRef skipButtonText,
|
||||
StringRef skippedToastText,
|
||||
StringSetting behavior, StringSetting color) {
|
||||
StringSetting behavior,
|
||||
StringSetting color, FloatSetting opacity) {
|
||||
this(keyValue, title, description,
|
||||
skipButtonText, skipButtonText, skipButtonText,
|
||||
skippedToastText, skippedToastText, skippedToastText,
|
||||
behavior, color);
|
||||
behavior,
|
||||
color, opacity);
|
||||
}
|
||||
|
||||
SegmentCategory(String keyValue, StringRef title, StringRef description,
|
||||
StringRef skipButtonTextBeginning, StringRef skipButtonTextMiddle, StringRef skipButtonTextEnd,
|
||||
StringRef skippedToastBeginning, StringRef skippedToastMiddle, StringRef skippedToastEnd,
|
||||
StringSetting behavior, StringSetting color) {
|
||||
StringSetting behavior,
|
||||
StringSetting color, FloatSetting opacity) {
|
||||
this.keyValue = Objects.requireNonNull(keyValue);
|
||||
this.title = Objects.requireNonNull(title);
|
||||
this.description = Objects.requireNonNull(description);
|
||||
@@ -216,6 +218,7 @@ public enum SegmentCategory {
|
||||
this.skippedToastEnd = Objects.requireNonNull(skippedToastEnd);
|
||||
this.behaviorSetting = Objects.requireNonNull(behavior);
|
||||
this.colorSetting = Objects.requireNonNull(color);
|
||||
this.opacitySetting = Objects.requireNonNull(opacity);
|
||||
this.paint = new Paint();
|
||||
loadFromSettings();
|
||||
}
|
||||
@@ -232,11 +235,14 @@ public enum SegmentCategory {
|
||||
this.behaviour = savedBehavior;
|
||||
|
||||
String colorString = colorSetting.get();
|
||||
final float opacity = opacitySetting.get();
|
||||
try {
|
||||
setColor(colorString);
|
||||
setOpacity(opacity);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "Invalid color: " + colorString, ex);
|
||||
Logger.printException(() -> "Invalid color: " + colorString + " opacity: " + opacity, ex);
|
||||
colorSetting.resetToDefault();
|
||||
opacitySetting.resetToDefault();
|
||||
loadFromSettings();
|
||||
}
|
||||
}
|
||||
@@ -245,45 +251,78 @@ public enum SegmentCategory {
|
||||
this.behaviour = Objects.requireNonNull(behaviour);
|
||||
this.behaviorSetting.save(behaviour.reVancedKeyValue);
|
||||
}
|
||||
/**
|
||||
* @return HTML color format string
|
||||
*/
|
||||
@NonNull
|
||||
public String colorString() {
|
||||
return String.format("#%06X", color);
|
||||
}
|
||||
|
||||
public void setColor(@NonNull String colorString) throws IllegalArgumentException {
|
||||
final int color = Color.parseColor(colorString) & 0xFFFFFF;
|
||||
this.color = color;
|
||||
private void updateColor() {
|
||||
color = applyOpacityToColor(color, opacitySetting.get());
|
||||
paint.setColor(color);
|
||||
paint.setAlpha(255);
|
||||
colorSetting.save(colorString); // Save after parsing.
|
||||
}
|
||||
|
||||
public void resetColor() {
|
||||
/**
|
||||
* @param opacity Segment color opacity between [0, 1].
|
||||
*/
|
||||
public void setOpacity(float opacity) throws IllegalArgumentException {
|
||||
if (opacity < 0 || opacity > 1) {
|
||||
throw new IllegalArgumentException("Invalid opacity: " + opacity);
|
||||
}
|
||||
|
||||
opacitySetting.save(opacity);
|
||||
updateColor();
|
||||
}
|
||||
|
||||
public float getOpacity() {
|
||||
return opacitySetting.get();
|
||||
}
|
||||
|
||||
public void resetColorAndOpacity() {
|
||||
setColor(colorSetting.defaultValue);
|
||||
setOpacity(opacitySetting.defaultValue);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getCategoryColorDotHTML(int color) {
|
||||
color &= 0xFFFFFF;
|
||||
return String.format("<font color=\"#%06X\">⬤</font>", color);
|
||||
/**
|
||||
* @param colorString Segment color with #RRGGBB format.
|
||||
*/
|
||||
public void setColor(String colorString) throws IllegalArgumentException {
|
||||
color = Color.parseColor(colorString);
|
||||
colorSetting.save(colorString);
|
||||
|
||||
updateColor();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Spanned getCategoryColorDot(int color) {
|
||||
return Html.fromHtml(getCategoryColorDotHTML(color));
|
||||
/**
|
||||
* @return Integer color of #RRGGBB format.
|
||||
*/
|
||||
public int getColorNoOpacity() {
|
||||
return color & 0x00FFFFFF;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Spanned getCategoryColorDot() {
|
||||
/**
|
||||
* @return Hex color string of #RRGGBB format with no opacity level.
|
||||
*/
|
||||
public String getColorString() {
|
||||
return String.format(Locale.US, "#%06X", getColorNoOpacity());
|
||||
}
|
||||
|
||||
private static SpannableString getCategoryColorDotSpan(String text, int color) {
|
||||
SpannableString dotSpan = new SpannableString('⬤' + text);
|
||||
dotSpan.setSpan(new ForegroundColorSpan(color), 0, 1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
return dotSpan;
|
||||
}
|
||||
|
||||
public static SpannableString getCategoryColorDot(int color) {
|
||||
return getCategoryColorDotSpan("", color);
|
||||
}
|
||||
|
||||
public SpannableString getCategoryColorDot() {
|
||||
return getCategoryColorDot(color);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Spanned getTitleWithColorDot() {
|
||||
return Html.fromHtml(getCategoryColorDotHTML(color) + " " + title);
|
||||
public SpannableString getTitleWithColorDot(int categoryColor) {
|
||||
return getCategoryColorDotSpan(" " + title, categoryColor);
|
||||
}
|
||||
|
||||
public SpannableString getTitleWithColorDot() {
|
||||
return getTitleWithColorDot(color);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,7 +330,6 @@ public enum SegmentCategory {
|
||||
* @param videoLength length of the video
|
||||
* @return the skip button text
|
||||
*/
|
||||
@NonNull
|
||||
StringRef getSkipButtonText(long segmentStartTime, long videoLength) {
|
||||
if (Settings.SB_COMPACT_SKIP_BUTTON.get()) {
|
||||
return (this == SegmentCategory.HIGHLIGHT)
|
||||
@@ -300,7 +338,7 @@ public enum SegmentCategory {
|
||||
}
|
||||
|
||||
if (videoLength == 0) {
|
||||
return skipButtonTextBeginning; // video is still loading. Assume it's the beginning
|
||||
return skipButtonTextBeginning; // Video is still loading. Assume it's the beginning.
|
||||
}
|
||||
final float position = segmentStartTime / (float) videoLength;
|
||||
if (position < 0.25f) {
|
||||
@@ -316,10 +354,9 @@ public enum SegmentCategory {
|
||||
* @param videoLength length of the video
|
||||
* @return 'skipped segment' toast message
|
||||
*/
|
||||
@NonNull
|
||||
StringRef getSkippedToastText(long segmentStartTime, long videoLength) {
|
||||
if (videoLength == 0) {
|
||||
return skippedToastBeginning; // video is still loading. Assume it's the beginning
|
||||
return skippedToastBeginning; // Video is still loading. Assume it's the beginning.
|
||||
}
|
||||
final float position = segmentStartTime / (float) videoLength;
|
||||
if (position < 0.25f) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.revanced.extension.youtube.sponsorblock.objects;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory.applyOpacityToColor;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
@@ -11,11 +12,10 @@ import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TableLayout;
|
||||
import android.widget.TableRow;
|
||||
import android.widget.GridLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
@@ -24,27 +24,38 @@ import app.revanced.extension.shared.Utils;
|
||||
@SuppressWarnings("deprecation")
|
||||
public class SegmentCategoryListPreference extends ListPreference {
|
||||
private final SegmentCategory category;
|
||||
private EditText mEditText;
|
||||
private int mClickedDialogEntryIndex;
|
||||
private TextView colorDotView;
|
||||
private EditText colorEditText;
|
||||
private EditText opacityEditText;
|
||||
/**
|
||||
* #RRGGBB
|
||||
*/
|
||||
private int categoryColor;
|
||||
/**
|
||||
* [0, 1]
|
||||
*/
|
||||
private float categoryOpacity;
|
||||
private int selectedDialogEntryIndex;
|
||||
|
||||
public SegmentCategoryListPreference(Context context, SegmentCategory category) {
|
||||
super(context);
|
||||
final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
|
||||
this.category = Objects.requireNonNull(category);
|
||||
|
||||
// Edit: Using preferences to sync together multiple pieces
|
||||
// of code together is messy and should be rethought.
|
||||
// of code is messy and should be rethought.
|
||||
setKey(category.behaviorSetting.key);
|
||||
setDefaultValue(category.behaviorSetting.defaultValue);
|
||||
|
||||
final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
|
||||
setEntries(isHighlightCategory
|
||||
? CategoryBehaviour.getBehaviorDescriptionsWithoutSkipOnce()
|
||||
: CategoryBehaviour.getBehaviorDescriptions());
|
||||
setEntryValues(isHighlightCategory
|
||||
? CategoryBehaviour.getBehaviorKeyValuesWithoutSkipOnce()
|
||||
: CategoryBehaviour.getBehaviorKeyValues());
|
||||
|
||||
setSummary(category.description.toString());
|
||||
updateTitle();
|
||||
|
||||
updateTitleFromCategory();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,26 +63,40 @@ public class SegmentCategoryListPreference extends ListPreference {
|
||||
try {
|
||||
Utils.setEditTextDialogTheme(builder);
|
||||
|
||||
categoryColor = category.getColorNoOpacity();
|
||||
categoryOpacity = category.getOpacity();
|
||||
|
||||
Context context = builder.getContext();
|
||||
TableLayout table = new TableLayout(context);
|
||||
table.setOrientation(LinearLayout.HORIZONTAL);
|
||||
table.setPadding(70, 0, 150, 0);
|
||||
|
||||
TableRow row = new TableRow(context);
|
||||
GridLayout gridLayout = new GridLayout(context);
|
||||
gridLayout.setPadding(70, 0, 150, 0); // Padding for the entire layout.
|
||||
gridLayout.setColumnCount(3);
|
||||
gridLayout.setRowCount(2);
|
||||
|
||||
GridLayout.LayoutParams gridParams = new GridLayout.LayoutParams();
|
||||
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
||||
gridParams.columnSpec = GridLayout.spec(0); // First column.
|
||||
TextView colorTextLabel = new TextView(context);
|
||||
colorTextLabel.setText(str("revanced_sb_color_dot_label"));
|
||||
row.addView(colorTextLabel);
|
||||
colorTextLabel.setLayoutParams(gridParams);
|
||||
gridLayout.addView(colorTextLabel);
|
||||
|
||||
TextView colorDotView = new TextView(context);
|
||||
colorDotView.setText(category.getCategoryColorDot());
|
||||
colorDotView.setPadding(30, 0, 30, 0);
|
||||
row.addView(colorDotView);
|
||||
gridParams = new GridLayout.LayoutParams();
|
||||
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
||||
gridParams.columnSpec = GridLayout.spec(1); // Second column.
|
||||
gridParams.setMargins(0, 0, 10, 0);
|
||||
colorDotView = new TextView(context);
|
||||
colorDotView.setLayoutParams(gridParams);
|
||||
gridLayout.addView(colorDotView);
|
||||
updateCategoryColorDot();
|
||||
|
||||
mEditText = new EditText(context);
|
||||
mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
|
||||
mEditText.setText(category.colorString());
|
||||
mEditText.addTextChangedListener(new TextWatcher() {
|
||||
gridParams = new GridLayout.LayoutParams();
|
||||
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
||||
gridParams.columnSpec = GridLayout.spec(2); // Third column.
|
||||
colorEditText = new EditText(context);
|
||||
colorEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
|
||||
colorEditText.setTextLocale(Locale.US);
|
||||
colorEditText.setText(category.getColorString());
|
||||
colorEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
@@ -81,29 +106,94 @@ public class SegmentCategoryListPreference extends ListPreference {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
public void afterTextChanged(Editable edit) {
|
||||
try {
|
||||
String colorString = s.toString();
|
||||
String colorString = edit.toString();
|
||||
final int colorStringLength = colorString.length();
|
||||
|
||||
if (!colorString.startsWith("#")) {
|
||||
s.insert(0, "#"); // recursively calls back into this method
|
||||
edit.insert(0, "#"); // Recursively calls back into this method.
|
||||
return;
|
||||
}
|
||||
if (colorString.length() > 7) {
|
||||
s.delete(7, colorString.length());
|
||||
|
||||
final int maxColorStringLength = 7; // #RRGGBB
|
||||
if (colorStringLength > maxColorStringLength) {
|
||||
edit.delete(maxColorStringLength, colorStringLength);
|
||||
return;
|
||||
}
|
||||
final int color = Color.parseColor(colorString);
|
||||
colorDotView.setText(SegmentCategory.getCategoryColorDot(color));
|
||||
|
||||
categoryColor = Color.parseColor(colorString);
|
||||
updateCategoryColorDot();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// ignore
|
||||
// Ignore.
|
||||
}
|
||||
}
|
||||
});
|
||||
mEditText.setLayoutParams(new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 1f));
|
||||
row.addView(mEditText);
|
||||
colorEditText.setLayoutParams(gridParams);
|
||||
gridLayout.addView(colorEditText);
|
||||
|
||||
table.addView(row);
|
||||
builder.setView(table);
|
||||
gridParams = new GridLayout.LayoutParams();
|
||||
gridParams.rowSpec = GridLayout.spec(1); // Second row.
|
||||
gridParams.columnSpec = GridLayout.spec(0, 1); // First and second column.
|
||||
TextView opacityLabel = new TextView(context);
|
||||
opacityLabel.setText(str("revanced_sb_color_opacity_label"));
|
||||
opacityLabel.setLayoutParams(gridParams);
|
||||
gridLayout.addView(opacityLabel);
|
||||
|
||||
gridParams = new GridLayout.LayoutParams();
|
||||
gridParams.rowSpec = GridLayout.spec(1); // Second row.
|
||||
gridParams.columnSpec = GridLayout.spec(2); // Third column.
|
||||
opacityEditText = new EditText(context);
|
||||
opacityEditText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
||||
opacityEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable edit) {
|
||||
try {
|
||||
String editString = edit.toString();
|
||||
final int opacityStringLength = editString.length();
|
||||
|
||||
final int maxOpacityStringLength = 4; // [0.00, 1.00]
|
||||
if (opacityStringLength > maxOpacityStringLength) {
|
||||
edit.delete(maxOpacityStringLength, opacityStringLength);
|
||||
return;
|
||||
}
|
||||
|
||||
final float opacity = opacityStringLength == 0
|
||||
? 0
|
||||
: Float.parseFloat(editString);
|
||||
if (opacity < 0) {
|
||||
categoryOpacity = 0;
|
||||
edit.replace(0, opacityStringLength, "0");
|
||||
return;
|
||||
} else if (opacity > 1.0f) {
|
||||
categoryOpacity = 1;
|
||||
edit.replace(0, opacityStringLength, "1.0");
|
||||
return;
|
||||
} else if (!editString.endsWith(".")) {
|
||||
// Ignore "0." and "1." until the user finishes entering a valid number.
|
||||
categoryOpacity = opacity;
|
||||
}
|
||||
|
||||
updateCategoryColorDot();
|
||||
} catch (NumberFormatException ex) {
|
||||
// Should never happen.
|
||||
Logger.printException(() -> "Could not parse opacity string", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
opacityEditText.setLayoutParams(gridParams);
|
||||
gridLayout.addView(opacityEditText);
|
||||
updateOpacityText();
|
||||
|
||||
builder.setView(gridLayout);
|
||||
builder.setTitle(category.title.toString());
|
||||
|
||||
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
||||
@@ -111,8 +201,8 @@ public class SegmentCategoryListPreference extends ListPreference {
|
||||
});
|
||||
builder.setNeutralButton(str("revanced_sb_reset_color"), (dialog, which) -> {
|
||||
try {
|
||||
category.resetColor();
|
||||
updateTitle();
|
||||
category.resetColorAndOpacity();
|
||||
updateTitleFromCategory();
|
||||
Utils.showToastShort(str("revanced_sb_color_reset"));
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setNeutralButton failure", ex);
|
||||
@@ -120,8 +210,9 @@ public class SegmentCategoryListPreference extends ListPreference {
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
|
||||
mClickedDialogEntryIndex = findIndexOfValue(getValue());
|
||||
builder.setSingleChoiceItems(getEntries(), mClickedDialogEntryIndex, (dialog, which) -> mClickedDialogEntryIndex = which);
|
||||
selectedDialogEntryIndex = findIndexOfValue(getValue());
|
||||
builder.setSingleChoiceItems(getEntries(), selectedDialogEntryIndex,
|
||||
(dialog, which) -> selectedDialogEntryIndex = which);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
|
||||
}
|
||||
@@ -130,30 +221,51 @@ public class SegmentCategoryListPreference extends ListPreference {
|
||||
@Override
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
try {
|
||||
if (positiveResult && mClickedDialogEntryIndex >= 0 && getEntryValues() != null) {
|
||||
String value = getEntryValues()[mClickedDialogEntryIndex].toString();
|
||||
if (positiveResult && selectedDialogEntryIndex >= 0 && getEntryValues() != null) {
|
||||
String value = getEntryValues()[selectedDialogEntryIndex].toString();
|
||||
if (callChangeListener(value)) {
|
||||
setValue(value);
|
||||
category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
|
||||
SegmentCategory.updateEnabledCategories();
|
||||
}
|
||||
String colorString = mEditText.getText().toString();
|
||||
|
||||
try {
|
||||
if (!colorString.equals(category.colorString())) {
|
||||
String colorString = colorEditText.getText().toString();
|
||||
if (!colorString.equals(category.getColorString()) || categoryOpacity != category.getOpacity()) {
|
||||
category.setColor(colorString);
|
||||
category.setOpacity(categoryOpacity);
|
||||
Utils.showToastShort(str("revanced_sb_color_changed"));
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
Utils.showToastShort(str("revanced_sb_color_invalid"));
|
||||
}
|
||||
updateTitle();
|
||||
|
||||
updateTitleFromCategory();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "onDialogClosed failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTitle() {
|
||||
setTitle(category.getTitleWithColorDot());
|
||||
private void applyOpacityToCategoryColor() {
|
||||
categoryColor = applyOpacityToColor(categoryColor, categoryOpacity);
|
||||
}
|
||||
|
||||
private void updateTitleFromCategory() {
|
||||
categoryColor = category.getColorNoOpacity();
|
||||
categoryOpacity = category.getOpacity();
|
||||
applyOpacityToCategoryColor();
|
||||
|
||||
setTitle(category.getTitleWithColorDot(categoryColor));
|
||||
}
|
||||
|
||||
private void updateCategoryColorDot() {
|
||||
applyOpacityToCategoryColor();
|
||||
|
||||
colorDotView.setText(SegmentCategory.getCategoryColorDot(categoryColor));
|
||||
}
|
||||
|
||||
private void updateOpacityText() {
|
||||
opacityEditText.setText(String.format(Locale.US, "%.2f", categoryOpacity));
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,15 @@ public class SponsorSegment implements Comparable<SponsorSegment> {
|
||||
@NonNull
|
||||
public final StringRef title;
|
||||
public final int apiVoteType;
|
||||
public final boolean shouldHighlight;
|
||||
/**
|
||||
* If the option should be highlighted for VIP users.
|
||||
*/
|
||||
public final boolean highlightIfVipAndVideoIsLocked;
|
||||
|
||||
SegmentVote(@NonNull StringRef title, int apiVoteType, boolean shouldHighlight) {
|
||||
SegmentVote(@NonNull StringRef title, int apiVoteType, boolean highlightIfVipAndVideoIsLocked) {
|
||||
this.title = title;
|
||||
this.apiVoteType = apiVoteType;
|
||||
this.shouldHighlight = shouldHighlight;
|
||||
this.highlightIfVipAndVideoIsLocked = highlightIfVipAndVideoIsLocked;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,83 +1,98 @@
|
||||
package app.revanced.extension.youtube.swipecontrols
|
||||
|
||||
import android.content.Context
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Color
|
||||
import app.revanced.extension.shared.Logger
|
||||
import app.revanced.extension.shared.StringRef.str
|
||||
import app.revanced.extension.shared.Utils
|
||||
import app.revanced.extension.youtube.settings.Settings
|
||||
import app.revanced.extension.youtube.shared.PlayerType
|
||||
|
||||
/**
|
||||
* provider for configuration for volume and brightness swipe controls
|
||||
*
|
||||
* @param context the context to create in
|
||||
* Provides configuration settings for volume and brightness swipe controls in the YouTube player.
|
||||
* Manages enabling/disabling gestures, overlay appearance, and behavior preferences.
|
||||
*/
|
||||
class SwipeControlsConfigurationProvider(
|
||||
private val context: Context,
|
||||
) {
|
||||
//region swipe enable
|
||||
class SwipeControlsConfigurationProvider {
|
||||
//region swipe enable
|
||||
/**
|
||||
* should swipe controls be enabled? (global setting)
|
||||
* Indicates whether swipe controls are enabled globally.
|
||||
* Returns true if either volume or brightness controls are enabled and the video is in fullscreen mode.
|
||||
*/
|
||||
val enableSwipeControls: Boolean
|
||||
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
|
||||
|
||||
/**
|
||||
* should swipe controls for volume be enabled?
|
||||
* Indicates whether swipe controls for adjusting volume are enabled.
|
||||
*/
|
||||
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
|
||||
|
||||
/**
|
||||
* should swipe controls for volume be enabled?
|
||||
* Indicates whether swipe controls for adjusting brightness are enabled.
|
||||
*/
|
||||
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
|
||||
|
||||
/**
|
||||
* is the video player currently in fullscreen mode?
|
||||
* Checks if the video player is currently in fullscreen mode.
|
||||
*/
|
||||
private val isFullscreenVideo: Boolean
|
||||
get() = PlayerType.current == PlayerType.WATCH_WHILE_FULLSCREEN
|
||||
//endregion
|
||||
//endregion
|
||||
|
||||
//region keys enable
|
||||
//region keys enable
|
||||
/**
|
||||
* should volume key controls be overwritten? (global setting)
|
||||
* Indicates whether volume key controls should be overridden by swipe controls.
|
||||
* Returns true if volume controls are enabled and the video is in fullscreen mode.
|
||||
*/
|
||||
val overwriteVolumeKeyControls: Boolean
|
||||
get() = enableVolumeControls && isFullscreenVideo
|
||||
//endregion
|
||||
//endregion
|
||||
|
||||
//region gesture adjustments
|
||||
//region gesture adjustments
|
||||
/**
|
||||
* should press-to-swipe be enabled?
|
||||
* Indicates whether press-to-swipe mode is enabled, requiring a press before swiping to activate controls.
|
||||
*/
|
||||
val shouldEnablePressToSwipe: Boolean
|
||||
get() = Settings.SWIPE_PRESS_TO_ENGAGE.get()
|
||||
|
||||
/**
|
||||
* threshold for swipe detection
|
||||
* this may be called rapidly in onScroll, so we have to load it once and then leave it constant
|
||||
* The threshold for detecting swipe gestures, in pixels.
|
||||
* Loaded once to ensure consistent behavior during rapid scroll events.
|
||||
*/
|
||||
val swipeMagnitudeThreshold: Int
|
||||
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
|
||||
//endregion
|
||||
|
||||
//region overlay adjustments
|
||||
/**
|
||||
* should the overlay enable haptic feedback?
|
||||
* The sensitivity of volume swipe gestures, determining how much volume changes per swipe.
|
||||
* Resets to default if set to 0, as it would disable swiping.
|
||||
*/
|
||||
val volumeSwipeSensitivity: Int
|
||||
get() {
|
||||
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
|
||||
|
||||
if (sensitivity < 1) {
|
||||
return Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
|
||||
}
|
||||
|
||||
return sensitivity
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region overlay adjustments
|
||||
/**
|
||||
* Indicates whether haptic feedback should be enabled for swipe control interactions.
|
||||
*/
|
||||
val shouldEnableHapticFeedback: Boolean
|
||||
get() = Settings.SWIPE_HAPTIC_FEEDBACK.get()
|
||||
|
||||
/**
|
||||
* how long the overlay should be shown on changes
|
||||
* The duration in milliseconds that the overlay should remain visible after a change.
|
||||
*/
|
||||
val overlayShowTimeoutMillis: Long
|
||||
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
|
||||
|
||||
/**
|
||||
* Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency.
|
||||
* If the opacity value is out of range, it resets to the default and displays a warning message.
|
||||
* The background opacity of the overlay, converted from a percentage (0-100) to an alpha value (0-255).
|
||||
* Resets to default and shows a toast if the value is out of range.
|
||||
*/
|
||||
val overlayBackgroundOpacity: Int
|
||||
get() {
|
||||
@@ -85,8 +100,7 @@ class SwipeControlsConfigurationProvider(
|
||||
|
||||
if (opacity < 0 || opacity > 100) {
|
||||
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
|
||||
Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
|
||||
opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
|
||||
opacity = Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
|
||||
}
|
||||
|
||||
opacity = opacity * 255 / 100
|
||||
@@ -94,55 +108,125 @@ class SwipeControlsConfigurationProvider(
|
||||
}
|
||||
|
||||
/**
|
||||
* The color of the progress overlay.
|
||||
* The color of the progress bar in the overlay.
|
||||
* Resets to default and shows a toast if the color string is invalid or empty.
|
||||
*/
|
||||
val overlayProgressColor: Int
|
||||
get() = 0xBFFFFFFF.toInt()
|
||||
get() {
|
||||
try {
|
||||
@SuppressLint("UseKtx")
|
||||
val color = Color.parseColor(Settings.SWIPE_OVERLAY_PROGRESS_COLOR.get())
|
||||
return (0xBF000000.toInt() or (color and 0xFFFFFF))
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
Logger.printDebug({ "Could not parse color" }, ex)
|
||||
Utils.showToastLong(str("revanced_swipe_overlay_progress_color_invalid_toast"))
|
||||
Settings.SWIPE_OVERLAY_PROGRESS_COLOR.resetToDefault()
|
||||
return overlayProgressColor // Recursively return.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The color used for the background of the progress overlay fill.
|
||||
* The background color used for the filled portion of the progress bar in the overlay.
|
||||
*/
|
||||
val overlayFillBackgroundPaint: Int
|
||||
get() = 0x80D3D3D3.toInt()
|
||||
|
||||
/**
|
||||
* The color used for the text and icons in the overlay.
|
||||
* The color used for text and icons in the overlay.
|
||||
*/
|
||||
val overlayTextColor: Int
|
||||
get() = Color.WHITE
|
||||
|
||||
/**
|
||||
* A flag that determines if the overlay should only show the icon.
|
||||
* The text size in the overlay, in density-independent pixels (dp).
|
||||
* Must be between 1 and 30 dp; resets to default and shows a toast if invalid.
|
||||
*/
|
||||
val overlayShowOverlayMinimalStyle: Boolean
|
||||
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get()
|
||||
val overlayTextSize: Int
|
||||
get() {
|
||||
val size = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
|
||||
if (size < 1 || size > 30) {
|
||||
Utils.showToastLong(str("revanced_swipe_text_overlay_size_invalid_toast"))
|
||||
return Settings.SWIPE_OVERLAY_TEXT_SIZE.resetToDefault()
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
/**
|
||||
* A flag that determines if the progress bar should be circular.
|
||||
* Defines the style of the swipe controls overlay, determining its layout and appearance.
|
||||
*
|
||||
* @property isMinimal Indicates whether the style is minimalistic, omitting detailed progress indicators.
|
||||
* @property isHorizontalMinimalCenter Indicates whether the style is a minimal horizontal bar centered vertically.
|
||||
* @property isCircular Indicates whether the style uses a circular progress bar.
|
||||
* @property isVertical Indicates whether the style uses a vertical progress bar.
|
||||
*/
|
||||
val isCircularProgressBar: Boolean
|
||||
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get()
|
||||
//endregion
|
||||
@Suppress("unused")
|
||||
enum class SwipeOverlayStyle(
|
||||
val isMinimal: Boolean = false,
|
||||
val isHorizontalMinimalCenter: Boolean = false,
|
||||
val isCircular: Boolean = false,
|
||||
val isVertical: Boolean = false
|
||||
) {
|
||||
/**
|
||||
* A full horizontal progress bar with detailed indicators.
|
||||
*/
|
||||
HORIZONTAL,
|
||||
|
||||
//region behaviour
|
||||
/**
|
||||
* A minimal horizontal progress bar positioned at the top.
|
||||
*/
|
||||
HORIZONTAL_MINIMAL_TOP(isMinimal = true),
|
||||
|
||||
/**
|
||||
* A minimal horizontal progress bar centered vertically.
|
||||
*/
|
||||
HORIZONTAL_MINIMAL_CENTER(isMinimal = true, isHorizontalMinimalCenter = true),
|
||||
|
||||
/**
|
||||
* A full circular progress bar with detailed indicators.
|
||||
*/
|
||||
CIRCULAR(isCircular = true),
|
||||
|
||||
/**
|
||||
* A minimal circular progress bar.
|
||||
*/
|
||||
CIRCULAR_MINIMAL(isMinimal = true, isCircular = true),
|
||||
|
||||
/**
|
||||
* A full vertical progress bar with detailed indicators.
|
||||
*/
|
||||
VERTICAL(isVertical = true),
|
||||
|
||||
/**
|
||||
* A minimal vertical progress bar.
|
||||
*/
|
||||
VERTICAL_MINIMAL(isMinimal = true, isVertical = true)
|
||||
}
|
||||
|
||||
/**
|
||||
* should the brightness be saved and restored when exiting or entering fullscreen
|
||||
* The current style of the overlay, determining its layout and appearance.
|
||||
*/
|
||||
val overlayStyle: SwipeOverlayStyle
|
||||
get() = Settings.SWIPE_OVERLAY_STYLE.get()
|
||||
//endregion
|
||||
|
||||
//region behaviour
|
||||
/**
|
||||
* Indicates whether the brightness level should be saved and restored when entering or exiting fullscreen mode.
|
||||
*/
|
||||
val shouldSaveAndRestoreBrightness: Boolean
|
||||
get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get()
|
||||
|
||||
/**
|
||||
* should auto-brightness be enabled at the lowest value of the brightness gesture
|
||||
* Indicates whether auto-brightness should be enabled when the brightness gesture reaches its lowest value.
|
||||
*/
|
||||
val shouldLowestValueEnableAutoBrightness: Boolean
|
||||
get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get()
|
||||
|
||||
/**
|
||||
* variable that stores the brightness gesture value in the settings
|
||||
* The saved brightness value for the swipe gesture, used to restore brightness in fullscreen mode.
|
||||
*/
|
||||
var savedScreenBrightnessValue: Float
|
||||
get() = Settings.SWIPE_BRIGHTNESS_VALUE.get()
|
||||
set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value)
|
||||
//endregion
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
@@ -23,9 +23,7 @@ import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* The main controller for volume and brightness swipe controls.
|
||||
* note that the superclass is overwritten to the superclass of the MainActivity at patch time
|
||||
*
|
||||
* @smali Lapp/revanced/extension/swipecontrols/SwipeControlsHostActivity;
|
||||
* note that the superclass is overwritten to the superclass of the MainActivity at patch time.
|
||||
*/
|
||||
class SwipeControlsHostActivity : Activity() {
|
||||
/**
|
||||
@@ -127,7 +125,7 @@ class SwipeControlsHostActivity : Activity() {
|
||||
private fun initialize() {
|
||||
// create controllers
|
||||
printDebug { "initializing swipe controls controllers" }
|
||||
config = SwipeControlsConfigurationProvider(this)
|
||||
config = SwipeControlsConfigurationProvider()
|
||||
keys = VolumeKeysController(this)
|
||||
audio = createAudioController()
|
||||
screen = createScreenController()
|
||||
|
||||
@@ -41,7 +41,7 @@ class VolumeKeysController(
|
||||
private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean {
|
||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||
controller.audio?.apply {
|
||||
volume += if (volumeUp) 1 else -1
|
||||
volume += controller.config.volumeSwipeSensitivity * if (volumeUp) 1 else -1
|
||||
controller.overlay.onVolumeChanged(volume, maxVolume)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ abstract class BaseGestureController(
|
||||
controller.overlay,
|
||||
10,
|
||||
1,
|
||||
controller.config.volumeSwipeSensitivity,
|
||||
) {
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,6 +41,7 @@ interface VolumeAndBrightnessScroller {
|
||||
* @param overlayController overlay controller instance
|
||||
* @param volumeDistance unit distance for volume scrolling, in dp
|
||||
* @param brightnessDistance unit distance for brightness scrolling, in dp
|
||||
* @param volumeSwipeSensitivity how much volume will change by single swipe
|
||||
*/
|
||||
class VolumeAndBrightnessScrollerImpl(
|
||||
context: Context,
|
||||
@@ -49,6 +50,7 @@ class VolumeAndBrightnessScrollerImpl(
|
||||
private val overlayController: SwipeControlsOverlay,
|
||||
volumeDistance: Int = 10,
|
||||
brightnessDistance: Int = 1,
|
||||
private val volumeSwipeSensitivity: Int,
|
||||
) : VolumeAndBrightnessScroller {
|
||||
|
||||
// region volume
|
||||
@@ -60,7 +62,7 @@ class VolumeAndBrightnessScrollerImpl(
|
||||
),
|
||||
) { _, _, direction ->
|
||||
volumeController?.run {
|
||||
volume += direction
|
||||
volume += direction * volumeSwipeSensitivity
|
||||
overlayController.onVolumeChanged(volume, maxVolume)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package app.revanced.extension.youtube.swipecontrols.views
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Handler
|
||||
@@ -11,21 +14,30 @@ import android.util.AttributeSet
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.View
|
||||
import android.widget.RelativeLayout
|
||||
import app.revanced.extension.shared.StringRef.str
|
||||
import app.revanced.extension.shared.Utils
|
||||
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
|
||||
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
|
||||
import kotlin.math.min
|
||||
import kotlin.math.max
|
||||
import kotlin.math.round
|
||||
|
||||
/**
|
||||
* Main overlay layout for displaying volume and brightness level with both circular and horizontal progress bars.
|
||||
* Convert dp to pixels based on system display density.
|
||||
*/
|
||||
fun Float.toDisplayPixels(): Float {
|
||||
return this * Resources.getSystem().displayMetrics.density
|
||||
}
|
||||
|
||||
/**
|
||||
* Main overlay layout for displaying volume and brightness level with circular, horizontal and vertical progress bars.
|
||||
*/
|
||||
class SwipeControlsOverlayLayout(
|
||||
context: Context,
|
||||
private val config: SwipeControlsConfigurationProvider,
|
||||
) : RelativeLayout(context), SwipeControlsOverlay {
|
||||
|
||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
|
||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider())
|
||||
|
||||
// Drawable icons for brightness and volume
|
||||
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
||||
@@ -51,18 +63,21 @@ class SwipeControlsOverlayLayout(
|
||||
// Initialize progress bars
|
||||
private val circularProgressView: CircularProgressView
|
||||
private val horizontalProgressView: HorizontalProgressView
|
||||
private val verticalBrightnessProgressView: VerticalProgressView
|
||||
private val verticalVolumeProgressView: VerticalProgressView
|
||||
|
||||
init {
|
||||
// Initialize circular progress bar
|
||||
circularProgressView = CircularProgressView(
|
||||
context,
|
||||
config.overlayBackgroundOpacity,
|
||||
config.overlayShowOverlayMinimalStyle,
|
||||
config.overlayStyle.isMinimal,
|
||||
config.overlayProgressColor,
|
||||
config.overlayFillBackgroundPaint,
|
||||
config.overlayTextColor
|
||||
config.overlayTextColor,
|
||||
config.overlayTextSize
|
||||
).apply {
|
||||
layoutParams = LayoutParams(300, 300).apply {
|
||||
layoutParams = LayoutParams(100f.toDisplayPixels().toInt(), 100f.toDisplayPixels().toInt()).apply {
|
||||
addRule(CENTER_IN_PARENT, TRUE)
|
||||
}
|
||||
visibility = GONE // Initially hidden
|
||||
@@ -71,22 +86,65 @@ class SwipeControlsOverlayLayout(
|
||||
|
||||
// Initialize horizontal progress bar
|
||||
val screenWidth = resources.displayMetrics.widthPixels
|
||||
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
|
||||
val layoutWidth = (screenWidth * 4 / 5).toInt() // Cap at ~360dp
|
||||
horizontalProgressView = HorizontalProgressView(
|
||||
context,
|
||||
config.overlayBackgroundOpacity,
|
||||
config.overlayShowOverlayMinimalStyle,
|
||||
config.overlayStyle.isMinimal,
|
||||
config.overlayProgressColor,
|
||||
config.overlayFillBackgroundPaint,
|
||||
config.overlayTextColor
|
||||
config.overlayTextColor,
|
||||
config.overlayTextSize
|
||||
).apply {
|
||||
layoutParams = LayoutParams(layoutWidth, 100).apply {
|
||||
layoutParams = LayoutParams(layoutWidth, 32f.toDisplayPixels().toInt()).apply {
|
||||
addRule(CENTER_HORIZONTAL)
|
||||
topMargin = 40 // Top margin
|
||||
if (config.overlayStyle.isHorizontalMinimalCenter) {
|
||||
addRule(CENTER_VERTICAL)
|
||||
} else {
|
||||
topMargin = 20f.toDisplayPixels().toInt()
|
||||
}
|
||||
}
|
||||
visibility = GONE // Initially hidden
|
||||
}
|
||||
addView(horizontalProgressView)
|
||||
|
||||
// Initialize vertical progress bar for brightness (right side)
|
||||
verticalBrightnessProgressView = VerticalProgressView(
|
||||
context,
|
||||
config.overlayBackgroundOpacity,
|
||||
config.overlayStyle.isMinimal,
|
||||
config.overlayProgressColor,
|
||||
config.overlayFillBackgroundPaint,
|
||||
config.overlayTextColor,
|
||||
config.overlayTextSize
|
||||
).apply {
|
||||
layoutParams = LayoutParams(40f.toDisplayPixels().toInt(), 150f.toDisplayPixels().toInt()).apply {
|
||||
addRule(ALIGN_PARENT_RIGHT)
|
||||
rightMargin = 40f.toDisplayPixels().toInt()
|
||||
addRule(CENTER_VERTICAL)
|
||||
}
|
||||
visibility = GONE // Initially hidden
|
||||
}
|
||||
addView(verticalBrightnessProgressView)
|
||||
|
||||
// Initialize vertical progress bar for volume (left side)
|
||||
verticalVolumeProgressView = VerticalProgressView(
|
||||
context,
|
||||
config.overlayBackgroundOpacity,
|
||||
config.overlayStyle.isMinimal,
|
||||
config.overlayProgressColor,
|
||||
config.overlayFillBackgroundPaint,
|
||||
config.overlayTextColor,
|
||||
config.overlayTextSize
|
||||
).apply {
|
||||
layoutParams = LayoutParams(40f.toDisplayPixels().toInt(), 150f.toDisplayPixels().toInt()).apply {
|
||||
addRule(ALIGN_PARENT_LEFT)
|
||||
leftMargin = 40f.toDisplayPixels().toInt()
|
||||
addRule(CENTER_VERTICAL)
|
||||
}
|
||||
visibility = GONE // Initially hidden
|
||||
}
|
||||
addView(verticalVolumeProgressView)
|
||||
}
|
||||
|
||||
// Handler and callback for hiding progress bars
|
||||
@@ -94,6 +152,8 @@ class SwipeControlsOverlayLayout(
|
||||
private val feedbackHideCallback = Runnable {
|
||||
circularProgressView.visibility = GONE
|
||||
horizontalProgressView.visibility = GONE
|
||||
verticalBrightnessProgressView.visibility = GONE
|
||||
verticalVolumeProgressView.visibility = GONE
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +163,11 @@ class SwipeControlsOverlayLayout(
|
||||
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
|
||||
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
|
||||
|
||||
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
|
||||
val viewToShow = when {
|
||||
config.overlayStyle.isCircular -> circularProgressView
|
||||
config.overlayStyle.isVertical -> if (isBrightness) verticalBrightnessProgressView else verticalVolumeProgressView
|
||||
else -> horizontalProgressView
|
||||
}
|
||||
viewToShow.apply {
|
||||
setProgress(progress, max, value, isBrightness)
|
||||
this.icon = icon
|
||||
@@ -126,7 +190,9 @@ class SwipeControlsOverlayLayout(
|
||||
// Handle brightness change
|
||||
override fun onBrightnessChanged(brightness: Double) {
|
||||
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
|
||||
showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
|
||||
val displayText = if (config.overlayStyle.isVertical) "А"
|
||||
else str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text")
|
||||
showFeedbackView(displayText, 0, 100, autoBrightnessIcon, isBrightness = true)
|
||||
} else {
|
||||
val brightnessValue = round(brightness).toInt()
|
||||
val icon = when {
|
||||
@@ -135,7 +201,8 @@ class SwipeControlsOverlayLayout(
|
||||
brightnessValue < 75 -> highBrightnessIcon
|
||||
else -> fullBrightnessIcon
|
||||
}
|
||||
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
|
||||
val displayText = if (config.overlayStyle.isVertical) "$brightnessValue" else "$brightnessValue%"
|
||||
showFeedbackView(displayText, brightnessValue, 100, icon, isBrightness = true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,11 +223,12 @@ class SwipeControlsOverlayLayout(
|
||||
*/
|
||||
abstract class AbstractProgressView(
|
||||
context: Context,
|
||||
protected val overlayBackgroundOpacity: Int,
|
||||
protected val overlayShowOverlayMinimalStyle: Boolean,
|
||||
protected val overlayProgressColor: Int,
|
||||
protected val overlayFillBackgroundPaint: Int,
|
||||
protected val overlayTextColor: Int,
|
||||
overlayBackgroundOpacity: Int,
|
||||
protected val isMinimalStyle: Boolean,
|
||||
overlayProgressColor: Int,
|
||||
overlayFillBackgroundPaint: Int,
|
||||
private val overlayTextColor: Int,
|
||||
protected val overlayTextSize: Int,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
@@ -174,26 +242,25 @@ abstract class AbstractProgressView(
|
||||
}
|
||||
|
||||
// Initialize paints
|
||||
public val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
|
||||
public val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
|
||||
public val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
|
||||
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
|
||||
val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 6f.toDisplayPixels())
|
||||
val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
|
||||
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
color = overlayTextColor
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = 40f // Can adjust based on need
|
||||
textSize = overlayTextSize.toFloat().toDisplayPixels()
|
||||
}
|
||||
|
||||
// Rect for text measurement
|
||||
protected val textBounds = Rect()
|
||||
|
||||
protected var progress = 0
|
||||
protected var maxProgress = 100
|
||||
protected var displayText: String = "0"
|
||||
protected var isBrightness = true
|
||||
public var icon: Drawable? = null
|
||||
var icon: Drawable? = null
|
||||
|
||||
init {
|
||||
// Stroke widths are now set in createPaint for progressPaint and fillBackgroundPaint
|
||||
}
|
||||
|
||||
fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||
open fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||
progress = value
|
||||
maxProgress = max
|
||||
displayText = text
|
||||
@@ -201,6 +268,11 @@ abstract class AbstractProgressView(
|
||||
invalidate()
|
||||
}
|
||||
|
||||
protected fun measureTextWidth(text: String, paint: Paint): Int {
|
||||
paint.getTextBounds(text, 0, text.length, textBounds)
|
||||
return textBounds.width()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
// Base class implementation can be empty
|
||||
}
|
||||
@@ -209,34 +281,36 @@ abstract class AbstractProgressView(
|
||||
/**
|
||||
* Custom view for rendering a circular progress indicator with icons and text.
|
||||
*/
|
||||
@SuppressLint("ViewConstructor")
|
||||
class CircularProgressView(
|
||||
context: Context,
|
||||
overlayBackgroundOpacity: Int,
|
||||
overlayShowOverlayMinimalStyle: Boolean,
|
||||
isMinimalStyle: Boolean,
|
||||
overlayProgressColor: Int,
|
||||
overlayFillBackgroundPaint: Int,
|
||||
overlayTextColor: Int,
|
||||
overlayTextSize: Int,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : AbstractProgressView(
|
||||
context,
|
||||
overlayBackgroundOpacity,
|
||||
overlayShowOverlayMinimalStyle,
|
||||
isMinimalStyle,
|
||||
overlayProgressColor,
|
||||
overlayFillBackgroundPaint,
|
||||
overlayTextColor,
|
||||
overlayTextSize,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
) {
|
||||
private val rectF = RectF()
|
||||
|
||||
init {
|
||||
textPaint.textSize = 40f // Override default text size for circular view
|
||||
progressPaint.strokeWidth = 20f
|
||||
fillBackgroundPaint.strokeWidth = 20f
|
||||
progressPaint.strokeCap = Paint.Cap.ROUND
|
||||
progressPaint.strokeWidth = 6f.toDisplayPixels()
|
||||
fillBackgroundPaint.strokeWidth = 6f.toDisplayPixels()
|
||||
progressPaint.strokeCap = Paint.Cap.ROUND
|
||||
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.STROKE
|
||||
progressPaint.style = Paint.Style.STROKE
|
||||
fillBackgroundPaint.style = Paint.Style.STROKE
|
||||
}
|
||||
|
||||
@@ -244,7 +318,8 @@ class CircularProgressView(
|
||||
super.onDraw(canvas)
|
||||
|
||||
val size = min(width, height).toFloat()
|
||||
rectF.set(20f, 20f, size - 20f, size - 20f)
|
||||
val inset = 6f.toDisplayPixels()
|
||||
rectF.set(inset, inset, size - inset, size - inset)
|
||||
|
||||
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
|
||||
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
|
||||
@@ -255,124 +330,307 @@ class CircularProgressView(
|
||||
|
||||
// Draw the icon in the center.
|
||||
icon?.let {
|
||||
val iconSize = if (overlayShowOverlayMinimalStyle) 100 else 80
|
||||
val iconSize = (if (isMinimalStyle) 36f else 24f).toDisplayPixels().toInt()
|
||||
val iconX = (width - iconSize) / 2
|
||||
val iconY = (height / 2) - if (overlayShowOverlayMinimalStyle) 50 else 80
|
||||
val iconY = if (isMinimalStyle) {
|
||||
(height - iconSize) / 2
|
||||
} else {
|
||||
(height / 2) - 24f.toDisplayPixels().toInt()
|
||||
}
|
||||
it.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
|
||||
it.draw(canvas)
|
||||
}
|
||||
|
||||
// If not a minimal style mode, draw the text inside the ring.
|
||||
if (!overlayShowOverlayMinimalStyle) {
|
||||
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
|
||||
if (!isMinimalStyle) {
|
||||
canvas.drawText(displayText, width / 2f, height / 2f + 20f.toDisplayPixels(), textPaint)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||
super.setProgress(value, max, text, isBrightnessMode)
|
||||
requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom view for rendering a rectangular progress bar with icons and text.
|
||||
*/
|
||||
@SuppressLint("ViewConstructor")
|
||||
class HorizontalProgressView(
|
||||
context: Context,
|
||||
overlayBackgroundOpacity: Int,
|
||||
overlayShowOverlayMinimalStyle: Boolean,
|
||||
isMinimalStyle: Boolean,
|
||||
overlayProgressColor: Int,
|
||||
overlayFillBackgroundPaint: Int,
|
||||
overlayTextColor: Int,
|
||||
overlayTextSize: Int,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : AbstractProgressView(
|
||||
context,
|
||||
overlayBackgroundOpacity,
|
||||
overlayShowOverlayMinimalStyle,
|
||||
isMinimalStyle,
|
||||
overlayProgressColor,
|
||||
overlayFillBackgroundPaint,
|
||||
overlayTextColor,
|
||||
overlayTextSize,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
) {
|
||||
|
||||
private val iconSize = 60f
|
||||
private val padding = 40f
|
||||
private val iconSize = 20f.toDisplayPixels()
|
||||
private val padding = 12f.toDisplayPixels()
|
||||
private var textWidth = 0f
|
||||
private val progressBarHeight = 3f.toDisplayPixels()
|
||||
private val progressBarWidth: Float = resources.displayMetrics.widthPixels / 4f
|
||||
|
||||
init {
|
||||
textPaint.textSize = 36f // Override default text size for horizontal view
|
||||
progressPaint.strokeWidth = 0f
|
||||
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.FILL
|
||||
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.FILL
|
||||
fillBackgroundPaint.style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate required width based on content
|
||||
* @return Required width to display all elements
|
||||
*/
|
||||
private fun calculateRequiredWidth(): Float {
|
||||
textWidth = measureTextWidth(displayText, textPaint).toFloat()
|
||||
|
||||
return if (!isMinimalStyle) {
|
||||
padding + iconSize + padding + progressBarWidth + padding + textWidth + padding
|
||||
} else {
|
||||
padding + iconSize + padding + textWidth + padding
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
|
||||
val suggestedWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||
val suggestedHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||
|
||||
val height = suggestedHeight
|
||||
val requiredWidth = calculateRequiredWidth().toInt()
|
||||
val width = min(max(100, requiredWidth), suggestedWidth)
|
||||
|
||||
setMeasuredDimension(width, height)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
val width = width.toFloat()
|
||||
val height = height.toFloat()
|
||||
val viewWidth = width.toFloat()
|
||||
val viewHeight = height.toFloat()
|
||||
val viewHeightHalf = viewHeight / 2
|
||||
|
||||
// Radius for rounded corners
|
||||
val cornerRadius = min(width, height) / 2
|
||||
textWidth = measureTextWidth(displayText, textPaint).toFloat()
|
||||
|
||||
// Calculate the total width for the elements
|
||||
val minimalElementWidth = 5 * padding + iconSize
|
||||
val cornerRadius = viewHeightHalf
|
||||
|
||||
// Calculate the starting point (X) to center the elements
|
||||
val minimalStartX = (width - minimalElementWidth) / 2
|
||||
val startX = padding
|
||||
val iconEndX = startX + iconSize
|
||||
|
||||
// Draw the background
|
||||
if (!overlayShowOverlayMinimalStyle) {
|
||||
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
|
||||
} else {
|
||||
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
|
||||
}
|
||||
val textStartX = (viewWidth - 1.5f * padding - textWidth)
|
||||
|
||||
if (!overlayShowOverlayMinimalStyle) {
|
||||
// Draw the fill background
|
||||
val startX = 2 * padding + iconSize
|
||||
val endX = width - 4 * padding
|
||||
val fillWidth = endX - startX
|
||||
canvas.drawRoundRect(
|
||||
0f, 0f, viewWidth, viewHeight,
|
||||
cornerRadius, cornerRadius, backgroundPaint
|
||||
)
|
||||
|
||||
canvas.drawRoundRect(
|
||||
startX,
|
||||
height / 2 - 5f,
|
||||
endX,
|
||||
height / 2 + 5f,
|
||||
10f, 10f,
|
||||
fillBackgroundPaint
|
||||
)
|
||||
|
||||
// Draw the progress
|
||||
val progressWidth = (progress.toFloat() / maxProgress) * fillWidth
|
||||
canvas.drawRoundRect(
|
||||
startX,
|
||||
height / 2 - 5f,
|
||||
startX + progressWidth,
|
||||
height / 2 + 5f,
|
||||
10f, 10f,
|
||||
progressPaint
|
||||
)
|
||||
}
|
||||
|
||||
// Draw the icon
|
||||
icon?.let {
|
||||
val iconX = if (!overlayShowOverlayMinimalStyle) {
|
||||
padding
|
||||
} else {
|
||||
padding + minimalStartX
|
||||
}
|
||||
val iconY = height / 2 - iconSize / 2
|
||||
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
|
||||
val iconY = viewHeightHalf - iconSize / 2
|
||||
it.setBounds(
|
||||
startX.toInt(),
|
||||
iconY.toInt(),
|
||||
(startX + iconSize).toInt(),
|
||||
(iconY + iconSize).toInt()
|
||||
)
|
||||
it.draw(canvas)
|
||||
}
|
||||
|
||||
// Draw the text on the right
|
||||
val textX = if (!overlayShowOverlayMinimalStyle) {
|
||||
width - 2 * padding
|
||||
} else {
|
||||
minimalStartX + minimalElementWidth - 2 * padding
|
||||
}
|
||||
val textY = height / 2 + textPaint.textSize / 3
|
||||
val textY = viewHeightHalf + textPaint.textSize / 3
|
||||
textPaint.textAlign = Paint.Align.LEFT
|
||||
|
||||
// Draw the text
|
||||
canvas.drawText(displayText, textX, textY, textPaint)
|
||||
if (isMinimalStyle) {
|
||||
canvas.drawText(displayText, textStartX, textY, textPaint)
|
||||
} else {
|
||||
val progressStartX = iconEndX + padding
|
||||
val progressEndX = textStartX - padding
|
||||
val progressWidth = progressEndX - progressStartX
|
||||
|
||||
if (progressWidth > 50) {
|
||||
val progressBarHeightHalf = progressBarHeight / 2.0f
|
||||
val viewHeightHalfMinusProgressBarHeightHalf = viewHeightHalf - progressBarHeightHalf
|
||||
val viewHeightHalfPlusProgressBarHeightHalf = viewHeightHalf + progressBarHeightHalf
|
||||
|
||||
canvas.drawRoundRect(
|
||||
progressStartX,
|
||||
viewHeightHalfMinusProgressBarHeightHalf,
|
||||
progressEndX,
|
||||
viewHeightHalfPlusProgressBarHeightHalf,
|
||||
progressBarHeightHalf,
|
||||
progressBarHeightHalf,
|
||||
fillBackgroundPaint
|
||||
)
|
||||
|
||||
val progressValue = (progress.toFloat() / maxProgress) * progressWidth
|
||||
canvas.drawRoundRect(
|
||||
progressStartX,
|
||||
viewHeightHalfMinusProgressBarHeightHalf,
|
||||
progressStartX + progressValue,
|
||||
viewHeightHalfPlusProgressBarHeightHalf,
|
||||
progressBarHeightHalf,
|
||||
progressBarHeightHalf,
|
||||
progressPaint
|
||||
)
|
||||
}
|
||||
|
||||
canvas.drawText(displayText, textStartX, textY, textPaint)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||
super.setProgress(value, max, text, isBrightnessMode)
|
||||
requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom view for rendering a vertical progress bar with icons and text.
|
||||
*/
|
||||
@SuppressLint("ViewConstructor")
|
||||
class VerticalProgressView(
|
||||
context: Context,
|
||||
overlayBackgroundOpacity: Int,
|
||||
isMinimalStyle: Boolean,
|
||||
overlayProgressColor: Int,
|
||||
overlayFillBackgroundPaint: Int,
|
||||
overlayTextColor: Int,
|
||||
overlayTextSize: Int,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : AbstractProgressView(
|
||||
context,
|
||||
overlayBackgroundOpacity,
|
||||
isMinimalStyle,
|
||||
overlayProgressColor,
|
||||
overlayFillBackgroundPaint,
|
||||
overlayTextColor,
|
||||
overlayTextSize,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
) {
|
||||
|
||||
private val iconSize = 20f.toDisplayPixels()
|
||||
private val padding = 12f.toDisplayPixels()
|
||||
private val progressBarWidth = 3f.toDisplayPixels()
|
||||
private val progressBarHeight: Float = resources.displayMetrics.widthPixels / 3f
|
||||
|
||||
init {
|
||||
progressPaint.strokeWidth = 0f
|
||||
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||
progressPaint.style = Paint.Style.FILL
|
||||
fillBackgroundPaint.style = Paint.Style.FILL
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate required height based on content
|
||||
* @return Required height to display all elements
|
||||
*/
|
||||
private fun calculateRequiredHeight(): Float {
|
||||
return if (!isMinimalStyle) {
|
||||
padding + iconSize + padding + progressBarHeight + padding + textPaint.textSize + padding
|
||||
} else {
|
||||
padding + iconSize + padding + textPaint.textSize + padding
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
|
||||
val suggestedWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||
val suggestedHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||
|
||||
val requiredHeight = calculateRequiredHeight().toInt()
|
||||
val height = min(max(100, requiredHeight), suggestedHeight)
|
||||
|
||||
setMeasuredDimension(suggestedWidth, height)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
|
||||
val viewWidth = width.toFloat()
|
||||
val viewHeight = height.toFloat()
|
||||
val viewWidthHalf = viewWidth / 2
|
||||
val cornerRadius = viewWidthHalf
|
||||
|
||||
val startY = padding
|
||||
val iconEndY = startY + iconSize
|
||||
|
||||
val textStartY = viewHeight - padding - textPaint.textSize / 2
|
||||
|
||||
canvas.drawRoundRect(
|
||||
0f, 0f, viewWidth, viewHeight,
|
||||
cornerRadius, cornerRadius, backgroundPaint
|
||||
)
|
||||
|
||||
icon?.let {
|
||||
val iconX = viewWidthHalf - iconSize / 2
|
||||
it.setBounds(
|
||||
iconX.toInt(),
|
||||
startY.toInt(),
|
||||
(iconX + iconSize).toInt(),
|
||||
(startY + iconSize).toInt()
|
||||
)
|
||||
it.draw(canvas)
|
||||
}
|
||||
|
||||
val textX = viewWidthHalf
|
||||
textPaint.textAlign = Paint.Align.CENTER
|
||||
|
||||
if (isMinimalStyle) {
|
||||
canvas.drawText(displayText, textX, textStartY, textPaint)
|
||||
} else {
|
||||
val progressStartY = (iconEndY + padding).toFloat()
|
||||
val progressEndY = textStartY - textPaint.textSize - padding
|
||||
val progressHeight = progressEndY - progressStartY
|
||||
|
||||
if (progressHeight > 50) {
|
||||
val progressBarWidthHalf = progressBarWidth / 2
|
||||
val viewHeightHalfMinusProgressBarHeightHalf = viewWidthHalf - progressBarWidthHalf
|
||||
val viewHeightHalfPlusProgressBarHeightHalf = viewWidthHalf + progressBarWidthHalf
|
||||
|
||||
canvas.drawRoundRect(
|
||||
viewHeightHalfMinusProgressBarHeightHalf,
|
||||
progressStartY,
|
||||
viewHeightHalfPlusProgressBarHeightHalf,
|
||||
progressEndY,
|
||||
progressBarWidthHalf,
|
||||
progressBarWidthHalf,
|
||||
fillBackgroundPaint
|
||||
)
|
||||
|
||||
val progressValue = (progress.toFloat() / maxProgress) * progressHeight
|
||||
canvas.drawRoundRect(
|
||||
viewHeightHalfMinusProgressBarHeightHalf,
|
||||
progressEndY - progressValue,
|
||||
viewHeightHalfPlusProgressBarHeightHalf,
|
||||
progressEndY,
|
||||
progressBarWidthHalf,
|
||||
progressBarWidthHalf,
|
||||
progressPaint
|
||||
)
|
||||
}
|
||||
canvas.drawText(displayText, textX, textStartY, textPaint)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||
super.setProgress(value, max, text, isBrightnessMode)
|
||||
requestLayout()
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.14.0-dev.4
|
||||
version = 5.24.0-dev.4
|
||||
|
||||
@@ -2,6 +2,10 @@ public final class app/revanced/patches/all/misc/activity/exportall/ExportAllAct
|
||||
public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/adb/HideAdbPatchKt {
|
||||
public static final fun getHideAdbStatusPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt {
|
||||
public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -108,6 +112,10 @@ public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveSha
|
||||
public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt {
|
||||
public static final fun getSetTargetSdkVersion34 ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall {
|
||||
public abstract fun getDefinedClassName ()Ljava/lang/String;
|
||||
public abstract fun getMethodName ()Ljava/lang/String;
|
||||
@@ -132,6 +140,10 @@ public final class app/revanced/patches/amazon/DeepLinkingPatchKt {
|
||||
public static final fun getDeepLinkingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/angulus/ads/RemoveAdsPatchKt {
|
||||
public static final fun getAngulusPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatchKt {
|
||||
public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -228,6 +240,10 @@ public final class app/revanced/patches/instagram/ads/HideAdsPatchKt {
|
||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/misc/signature/SignatureCheckPatchKt {
|
||||
public static final fun getSignatureCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt {
|
||||
public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -268,6 +284,10 @@ public final class app/revanced/patches/messenger/inputfield/DisableTypingIndica
|
||||
public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/messenger/navbar/RemoveMetaAITabPatchKt {
|
||||
public static final fun getRemoveMetaAITabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt {
|
||||
public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -364,6 +384,14 @@ public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatc
|
||||
public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/pandora/ads/DisableAudioAdsPatchKt {
|
||||
public static final fun getDisableAudioAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/pandora/misc/EnableUnlimitedSkipsPatchKt {
|
||||
public static final fun getEnableUnlimitedSkipsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatchKt {
|
||||
public static final fun getGetDeviceIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -396,6 +424,18 @@ public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt {
|
||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/primevideo/ads/SkipAdsPatchKt {
|
||||
public static final fun getSkipAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/primevideo/misc/extension/ExtensionPatchKt {
|
||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/protonmail/signature/RemoveSentFromSignaturePatchKt {
|
||||
public static final fun getRemoveSentFromSignaturePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatchKt {
|
||||
public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -556,7 +596,9 @@ public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt {
|
||||
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||
public static final fun sharedExtensionPatch (Ljava/lang/String;[Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
@@ -620,14 +662,12 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP
|
||||
public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;
|
||||
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun equals (Ljava/lang/Object;)Z
|
||||
public final fun getIcon ()Ljava/lang/String;
|
||||
public final fun getKey ()Ljava/lang/String;
|
||||
public final fun getLayout ()Ljava/lang/String;
|
||||
public final fun getSummaryKey ()Ljava/lang/String;
|
||||
public final fun getTag ()Ljava/lang/String;
|
||||
public final fun getTitleKey ()Ljava/lang/String;
|
||||
public fun hashCode ()I
|
||||
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
|
||||
}
|
||||
|
||||
@@ -776,8 +816,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/TextPref
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt {
|
||||
public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
|
||||
@@ -816,6 +856,30 @@ public final class app/revanced/patches/spotify/lite/ondemand/OnDemandPatchKt {
|
||||
public static final fun getOnDemandPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/UnlockPremiumPatchKt {
|
||||
public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt {
|
||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatchKt {
|
||||
public static final fun getSpoofPackageInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
|
||||
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/privacy/SanitizeSharingLinksPatchKt {
|
||||
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/misc/widgets/FixThirdPartyLaunchersWidgetsKt {
|
||||
public static final fun getFixThirdPartyLaunchersWidgets ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt {
|
||||
public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1084,6 +1148,14 @@ public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideT
|
||||
public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/HideSeekbarPatchKt {
|
||||
public static final fun getHideSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarPatchKt {
|
||||
public static final fun getSeekbarPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatchKt {
|
||||
public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1152,6 +1224,10 @@ public final class app/revanced/patches/youtube/layout/hide/player/flyoutmenupan
|
||||
public static final fun getHidePlayerFlyoutMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/relatedvideooverlay/HideRelatedVideoOverlayPatchKt {
|
||||
public static final fun getHideRelatedVideoOverlayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/hide/rollingnumber/DisableRollingNumberAnimationPatchKt {
|
||||
public static final fun getDisableRollingNumberAnimationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1173,17 +1249,7 @@ public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPa
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatchKt {
|
||||
public static final fun getFloatyBarButtonTopMargin ()J
|
||||
public static final fun getMiniplayerMaxSize ()J
|
||||
public static final fun getMiniplayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public static final fun getModernMiniplayerClose ()J
|
||||
public static final fun getModernMiniplayerExpand ()J
|
||||
public static final fun getModernMiniplayerForwardButton ()J
|
||||
public static final fun getModernMiniplayerRewindButton ()J
|
||||
public static final fun getPlayerOverlays ()J
|
||||
public static final fun getScrimOverlay ()J
|
||||
public static final fun getYtOutlinePictureInPictureWhite24 ()J
|
||||
public static final fun getYtOutlineXWhite24 ()J
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatchKt {
|
||||
@@ -1378,6 +1444,14 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
|
||||
public static final fun is_19_46_or_greater ()Z
|
||||
public static final fun is_19_47_or_greater ()Z
|
||||
public static final fun is_19_49_or_greater ()Z
|
||||
public static final fun is_20_02_or_greater ()Z
|
||||
public static final fun is_20_03_or_greater ()Z
|
||||
public static final fun is_20_05_or_greater ()Z
|
||||
public static final fun is_20_07_or_greater ()Z
|
||||
public static final fun is_20_09_or_greater ()Z
|
||||
public static final fun is_20_10_or_greater ()Z
|
||||
public static final fun is_20_14_or_greater ()Z
|
||||
public static final fun is_20_15_or_greater ()Z
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
|
||||
@@ -1491,7 +1565,12 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
|
||||
}
|
||||
|
||||
public final class app/revanced/util/BytecodeUtilsKt {
|
||||
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;[Lapp/revanced/patcher/util/smali/ExternalLabel;)V
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
|
||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
|
||||
public static final fun findFreeRegister (Lcom/android/tools/smali/dexlib2/iface/Method;I[I)I
|
||||
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
|
||||
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||
@@ -1518,9 +1597,17 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
||||
public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
|
||||
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
|
||||
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
||||
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstResourceId (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package app.revanced.patches.all.misc.adb
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/all/misc/hide/adb/HideAdbPatch;"
|
||||
|
||||
private val SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE = ImmutableMethodReference(
|
||||
"Landroid/provider/Settings\$Global;",
|
||||
"getInt",
|
||||
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;"),
|
||||
"I"
|
||||
)
|
||||
|
||||
private val SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE = ImmutableMethodReference(
|
||||
"Landroid/provider/Settings\$Global;",
|
||||
"getInt",
|
||||
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;", "I"),
|
||||
"I"
|
||||
)
|
||||
|
||||
private fun MethodReference.anyMethodSignatureMatches(vararg anyOf: MethodReference): Boolean {
|
||||
return anyOf.any {
|
||||
MethodUtil.methodSignaturesMatch(it, this)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
val hideAdbStatusPatch = bytecodePatch(
|
||||
name = "Hide ADB status",
|
||||
description = "Hides enabled development settings and/or ADB.",
|
||||
use = false,
|
||||
) {
|
||||
extendWith("extensions/all/misc/adb/hide-adb.rve")
|
||||
|
||||
dependsOn(
|
||||
transformInstructionsPatch(
|
||||
filterMap = filterMap@{ classDef, method, instruction, instructionIndex ->
|
||||
val reference = instruction
|
||||
.takeIf { it.opcode == Opcode.INVOKE_STATIC }
|
||||
?.getReference<MethodReference>()
|
||||
?.takeIf {
|
||||
it.anyMethodSignatureMatches(it,
|
||||
SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE,
|
||||
SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE
|
||||
)
|
||||
}
|
||||
?: return@filterMap null
|
||||
|
||||
Triple(instruction as Instruction35c, instructionIndex, reference.parameterTypes)
|
||||
},
|
||||
transform = { method, entry ->
|
||||
val (instruction, index, parameterTypes) = entry
|
||||
val parameterString = parameterTypes.joinToString(separator = "")
|
||||
|
||||
val registerString = when (parameterTypes.size) {
|
||||
2 -> "v${instruction.registerC}, v${instruction.registerD}"
|
||||
else -> "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}"
|
||||
}
|
||||
|
||||
method.replaceInstruction(
|
||||
index,
|
||||
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->getInt($parameterString)I"
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user