mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 18:03:55 +01:00
Compare commits
194 Commits
v5.13.0-de
...
v5.20.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
2a88b1f895 | ||
|
|
ee5c830df8 | ||
|
|
e63a4b31f3 | ||
|
|
8d0bca3b03 | ||
|
|
c162d65d5b | ||
|
|
67dcd091c4 | ||
|
|
ac5ce2d67f | ||
|
|
4b78d056fd | ||
|
|
f8c901b2c1 | ||
|
|
2a67c312e1 | ||
|
|
a7eed30f46 | ||
|
|
e2de2d8d44 | ||
|
|
7ebbf356c0 | ||
|
|
2ced5c6e2a | ||
|
|
4a090ba659 | ||
|
|
cb609a6d9d | ||
|
|
42e6de9e8f | ||
|
|
c4a5b9a28c | ||
|
|
c86c85947f | ||
|
|
cbbf474c50 | ||
|
|
f147b7b73d | ||
|
|
fb8dbb4723 | ||
|
|
1e0d27e689 | ||
|
|
a2185bce09 | ||
|
|
1b60a72ede | ||
|
|
12b4ee04ad | ||
|
|
f9a6cc96de | ||
|
|
93ea250bf3 | ||
|
|
fdb946a2cc | ||
|
|
7cc939ab03 | ||
|
|
228d72428d | ||
|
|
4db7ab4207 | ||
|
|
329f993024 | ||
|
|
7cd1fb22d8 | ||
|
|
ae111bc0b9 | ||
|
|
79f1dfd3e8 | ||
|
|
f5dd902915 | ||
|
|
10e2b08eb2 | ||
|
|
4ae1155e51 | ||
|
|
69fbfaea19 | ||
|
|
f44fede67c | ||
|
|
3c52ab8017 | ||
|
|
d1641a6e3d | ||
|
|
09773e8934 | ||
|
|
d77d5bfbdd | ||
|
|
a84bded9e7 | ||
|
|
e664a24f73 | ||
|
|
5bf964fff6 | ||
|
|
0c0bbb8713 | ||
|
|
8afe48cd92 | ||
|
|
dde8ea31cb | ||
|
|
d3abbe3e93 | ||
|
|
c8179776ed | ||
|
|
c6c6516b12 | ||
|
|
d6eae01e12 | ||
|
|
ba88603f4b | ||
|
|
d5aab3d464 | ||
|
|
fca2f70c0e | ||
|
|
348f7e12cb | ||
|
|
b6b7208eeb | ||
|
|
a2c79f1349 | ||
|
|
4f5bb3c915 |
2
.github/workflows/pull_strings.yml
vendored
2
.github/workflows/pull_strings.yml
vendored
@@ -2,7 +2,7 @@ name: Pull strings
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 */6 * * *"
|
- cron: "0 */12 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
638
CHANGELOG.md
638
CHANGELOG.md
@@ -1,3 +1,641 @@
|
|||||||
|
# [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **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))
|
||||||
|
|
||||||
|
# [5.14.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.2...v5.14.0-dev.3) (2025-03-06)
|
||||||
|
|
||||||
|
|
||||||
|
### 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))
|
||||||
|
|
||||||
|
# [5.14.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.14.0-dev.1...v5.14.0-dev.2) (2025-03-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **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))
|
||||||
|
|
||||||
|
# [5.14.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.1-dev.1...v5.14.0-dev.1) (2025-03-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **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.13.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.13.0...v5.13.1-dev.1) (2025-03-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **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))
|
||||||
|
|
||||||
|
# [5.13.0](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0) (2025-03-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok:** Resolve startup app crash ([18c0fc2](https://github.com/ReVanced/revanced-patches/commit/18c0fc2a7f186f50a904fd25dbaa739abdd24993))
|
||||||
|
* **TikTok:** Resolve startup app crash ([6466398](https://github.com/ReVanced/revanced-patches/commit/64663983b84de1f28636205f61bf0a24c83968d1))
|
||||||
|
* **TikTok:** Resolve startup app crash ([c14bc24](https://github.com/ReVanced/revanced-patches/commit/c14bc244550de30eca975ca7c09e8eb0c47534b5))
|
||||||
|
* **TikTok:** Resolve startup app crash ([d700076](https://github.com/ReVanced/revanced-patches/commit/d7000768a5e5a688c9f4e48858ac34e352222c1e))
|
||||||
|
* **YouTube - Copy video URL:** Use correct button ordering ([5e622cc](https://github.com/ReVanced/revanced-patches/commit/5e622ccf66d34af31c6026fa7f4d332460c6ecb0))
|
||||||
|
* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([634d0ee](https://github.com/ReVanced/revanced-patches/commit/634d0ee12e31491c7ee1d4ceb002daf8366a3c15))
|
||||||
|
* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([4ac8854](https://github.com/ReVanced/revanced-patches/commit/4ac8854b99808a8957f3b0b7438e1e0cdedffbaf))
|
||||||
|
* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([6c4885a](https://github.com/ReVanced/revanced-patches/commit/6c4885a1d5dfff50100b01840b5552d92e83ee4a))
|
||||||
|
* **YouTube - Hide video action buttons:** Move 'Disable Like and Subscribe glow' to action buttons settings menu ([29b265d](https://github.com/ReVanced/revanced-patches/commit/29b265d8fdaa48502650be9623bfc518a57a0bb1))
|
||||||
|
* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([edf66f4](https://github.com/ReVanced/revanced-patches/commit/edf66f4e16d46156cb8b8e31d18cb8dbcb87737e))
|
||||||
|
* **YouTube - Spoof app version:** Force old settings menus if spoofing to older app targets ([#4490](https://github.com/ReVanced/revanced-patches/issues/4490)) ([45e7c46](https://github.com/ReVanced/revanced-patches/commit/45e7c46dd9c70c926b8b1a97ada668f90f5f6f8c))
|
||||||
|
* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([647e764](https://github.com/ReVanced/revanced-patches/commit/647e7642efc0c00db17ccb6a620d1c96ccf4afed))
|
||||||
|
* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([6dc4bf7](https://github.com/ReVanced/revanced-patches/commit/6dc4bf75e09ed6f05534919d7b769b720043abce))
|
||||||
|
* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([63fe870](https://github.com/ReVanced/revanced-patches/commit/63fe870d48ca2217327b952bde241b7f16ced850))
|
||||||
|
* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([bf8e775](https://github.com/ReVanced/revanced-patches/commit/bf8e7759f9bdbdfef419a879fb3dd7cf0dff0098))
|
||||||
|
* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([1f08047](https://github.com/ReVanced/revanced-patches/commit/1f08047b48cc9555a4887d16ec7219a55a77251f))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([d74732b](https://github.com/ReVanced/revanced-patches/commit/d74732b7596104321bde263201d95649e4bd0eee))
|
||||||
|
* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([f3268fb](https://github.com/ReVanced/revanced-patches/commit/f3268fb03ca25fb5465e36015b6c9dec2c84a655))
|
||||||
|
* **YouTube - Navigation buttons:** Add 'Hide notifications' setting ([#4485](https://github.com/ReVanced/revanced-patches/issues/4485)) ([506d241](https://github.com/ReVanced/revanced-patches/commit/506d2414bbc760e764e5a514b32926083d6ecb6b))
|
||||||
|
* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([198e4d2](https://github.com/ReVanced/revanced-patches/commit/198e4d2a2315c24a09eb9ecfefbd131a75384d2c))
|
||||||
|
|
||||||
|
# [5.13.0-dev.19](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.18...v5.13.0-dev.19) (2025-03-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Resolve playback issues with dynamic player config ([#4521](https://github.com/ReVanced/revanced-patches/issues/4521)) ([647e764](https://github.com/ReVanced/revanced-patches/commit/647e7642efc0c00db17ccb6a620d1c96ccf4afed))
|
||||||
|
|
||||||
|
# [5.13.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.17...v5.13.0-dev.18) (2025-02-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Infinity for Reddit:** Add support for Infinity for Reddit Plus ([#4511](https://github.com/ReVanced/revanced-patches/issues/4511)) ([d74732b](https://github.com/ReVanced/revanced-patches/commit/d74732b7596104321bde263201d95649e4bd0eee))
|
||||||
|
|
||||||
|
# [5.13.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.16...v5.13.0-dev.17) (2025-02-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide filter bar:** Fix `Hide in feed` not working in subscriptions feed ([#4512](https://github.com/ReVanced/revanced-patches/issues/4512)) ([634d0ee](https://github.com/ReVanced/revanced-patches/commit/634d0ee12e31491c7ee1d4ceb002daf8366a3c15))
|
||||||
|
|
||||||
|
# [5.13.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.15...v5.13.0-dev.16) (2025-02-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **NU.nl:** Add `Hide ads` and `Spoof Certificate` patch ([#4368](https://github.com/ReVanced/revanced-patches/issues/4368)) ([f3268fb](https://github.com/ReVanced/revanced-patches/commit/f3268fb03ca25fb5465e36015b6c9dec2c84a655))
|
||||||
|
|
||||||
|
# [5.13.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.14...v5.13.0-dev.15) (2025-02-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide player components:** Show correct end video thumbnail if `Hide end screen suggested video` is enabled ([#4502](https://github.com/ReVanced/revanced-patches/issues/4502)) ([6c4885a](https://github.com/ReVanced/revanced-patches/commit/6c4885a1d5dfff50100b01840b5552d92e83ee4a))
|
||||||
|
|
||||||
|
# [5.13.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.13...v5.13.0-dev.14) (2025-02-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Swipe controls:** Adjust the overlay text size ([#4503](https://github.com/ReVanced/revanced-patches/issues/4503)) ([6dc4bf7](https://github.com/ReVanced/revanced-patches/commit/6dc4bf75e09ed6f05534919d7b769b720043abce))
|
||||||
|
|
||||||
|
# [5.13.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.12...v5.13.0-dev.13) (2025-02-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Resolve button flickering when taping seekbar ([#4500](https://github.com/ReVanced/revanced-patches/issues/4500)) ([1f08047](https://github.com/ReVanced/revanced-patches/commit/1f08047b48cc9555a4887d16ec7219a55a77251f))
|
||||||
|
|
||||||
|
# [5.13.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.11...v5.13.0-dev.12) (2025-02-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Return YouTube Dislike:** Use correct number formatting if using a different ReVanced language ([edf66f4](https://github.com/ReVanced/revanced-patches/commit/edf66f4e16d46156cb8b8e31d18cb8dbcb87737e))
|
||||||
|
|
||||||
|
# [5.13.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.10...v5.13.0-dev.11) (2025-02-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok:** Resolve startup app crash ([18c0fc2](https://github.com/ReVanced/revanced-patches/commit/18c0fc2a7f186f50a904fd25dbaa739abdd24993))
|
||||||
|
|
||||||
|
# [5.13.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.9...v5.13.0-dev.10) (2025-02-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Copy video URL:** Use correct button ordering ([5e622cc](https://github.com/ReVanced/revanced-patches/commit/5e622ccf66d34af31c6026fa7f4d332460c6ecb0))
|
||||||
|
|
||||||
|
# [5.13.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.8...v5.13.0-dev.9) (2025-02-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Do not hide player controls when using double tap to skip forward ([#4487](https://github.com/ReVanced/revanced-patches/issues/4487)) ([63fe870](https://github.com/ReVanced/revanced-patches/commit/63fe870d48ca2217327b952bde241b7f16ced850))
|
||||||
|
|
||||||
|
# [5.13.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.7...v5.13.0-dev.8) (2025-02-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof app version:** Force old settings menus if spoofing to older app targets ([#4490](https://github.com/ReVanced/revanced-patches/issues/4490)) ([45e7c46](https://github.com/ReVanced/revanced-patches/commit/45e7c46dd9c70c926b8b1a97ada668f90f5f6f8c))
|
||||||
|
|
||||||
|
# [5.13.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.6...v5.13.0-dev.7) (2025-02-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok:** Resolve startup app crash ([6466398](https://github.com/ReVanced/revanced-patches/commit/64663983b84de1f28636205f61bf0a24c83968d1))
|
||||||
|
|
||||||
|
# [5.13.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.5...v5.13.0-dev.6) (2025-02-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Navigation buttons:** Add 'Hide notifications' setting ([#4485](https://github.com/ReVanced/revanced-patches/issues/4485)) ([506d241](https://github.com/ReVanced/revanced-patches/commit/506d2414bbc760e764e5a514b32926083d6ecb6b))
|
||||||
|
|
||||||
|
# [5.13.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.4...v5.13.0-dev.5) (2025-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok:** Resolve startup app crash ([c14bc24](https://github.com/ReVanced/revanced-patches/commit/c14bc244550de30eca975ca7c09e8eb0c47534b5))
|
||||||
|
|
||||||
|
# [5.13.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.3...v5.13.0-dev.4) (2025-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok:** Resolve startup app crash ([d700076](https://github.com/ReVanced/revanced-patches/commit/d7000768a5e5a688c9f4e48858ac34e352222c1e))
|
||||||
|
|
||||||
|
# [5.13.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.2...v5.13.0-dev.3) (2025-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube:** Fix player button fade out animations ([#4469](https://github.com/ReVanced/revanced-patches/issues/4469)) ([bf8e775](https://github.com/ReVanced/revanced-patches/commit/bf8e7759f9bdbdfef419a879fb3dd7cf0dff0098))
|
||||||
|
|
||||||
# [5.13.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.1...v5.13.0-dev.2) (2025-02-18)
|
# [5.13.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.13.0-dev.1...v5.13.0-dev.2) (2025-02-18)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
4
extensions/nunl/build.gradle.kts
Normal file
4
extensions/nunl/build.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
dependencies {
|
||||||
|
compileOnly(project(":extensions:shared:library"))
|
||||||
|
compileOnly(project(":extensions:nunl:stub"))
|
||||||
|
}
|
||||||
1
extensions/nunl/src/main/AndroidManifest.xml
Normal file
1
extensions/nunl/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package app.revanced.extension.nunl.ads;
|
||||||
|
|
||||||
|
import nl.nu.performance.api.client.interfaces.Block;
|
||||||
|
import nl.nu.performance.api.client.unions.SmallArticleLinkFlavor;
|
||||||
|
import nl.nu.performance.api.client.objects.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class HideAdsPatch {
|
||||||
|
private static final String[] blockedHeaderBlocks = {
|
||||||
|
"Aanbiedingen (Adverteerders)",
|
||||||
|
"Aangeboden door NUshop"
|
||||||
|
};
|
||||||
|
|
||||||
|
// "Rubrieken" menu links to ads.
|
||||||
|
private static final String[] blockedLinkBlocks = {
|
||||||
|
"Van onze adverteerders"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void filterAds(List<Block> blocks) {
|
||||||
|
try {
|
||||||
|
ArrayList<Block> cleanedList = new ArrayList<>();
|
||||||
|
|
||||||
|
boolean skipFullHeader = false;
|
||||||
|
boolean skipUntilDivider = false;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
while (index < blocks.size()) {
|
||||||
|
Block currentBlock = blocks.get(index);
|
||||||
|
|
||||||
|
// Because of pagination, we might not see the Divider in front of it.
|
||||||
|
// Just remove it as is and leave potential extra spacing visible on the screen.
|
||||||
|
if (currentBlock instanceof DpgBannerBlock) {
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index + 1 < blocks.size()) {
|
||||||
|
// Filter Divider -> DpgMediaBanner -> Divider.
|
||||||
|
if (currentBlock instanceof DividerBlock
|
||||||
|
&& blocks.get(index + 1) instanceof DpgBannerBlock) {
|
||||||
|
index += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter Divider -> LinkBlock (... -> LinkBlock -> LinkBlock-> LinkBlock -> Divider).
|
||||||
|
if (currentBlock instanceof DividerBlock
|
||||||
|
&& blocks.get(index + 1) instanceof LinkBlock linkBlock) {
|
||||||
|
Link link = linkBlock.getLink();
|
||||||
|
if (link != null && link.getTitle() != null) {
|
||||||
|
for (String blockedLinkBlock : blockedLinkBlocks) {
|
||||||
|
if (blockedLinkBlock.equals(link.getTitle().getText())) {
|
||||||
|
skipUntilDivider = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skipUntilDivider) {
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip LinkBlocks with a "flavor" claiming to be "isPartner" (sponsored inline ads).
|
||||||
|
if (currentBlock instanceof LinkBlock linkBlock
|
||||||
|
&& linkBlock.getLink() != null
|
||||||
|
&& linkBlock.getLink().getLinkFlavor() instanceof SmallArticleLinkFlavor smallArticleLinkFlavor
|
||||||
|
&& smallArticleLinkFlavor.isPartner() != null
|
||||||
|
&& smallArticleLinkFlavor.isPartner()) {
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentBlock instanceof DividerBlock) {
|
||||||
|
skipUntilDivider = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter HeaderBlock with known ads until next HeaderBlock.
|
||||||
|
if (currentBlock instanceof HeaderBlock headerBlock) {
|
||||||
|
StyledText headerText = headerBlock.component20();
|
||||||
|
if (headerText != null) {
|
||||||
|
skipFullHeader = false;
|
||||||
|
for (String blockedHeaderBlock : blockedHeaderBlocks) {
|
||||||
|
if (blockedHeaderBlock.equals(headerText.getText())) {
|
||||||
|
skipFullHeader = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skipFullHeader) {
|
||||||
|
index++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skipFullHeader && !skipUntilDivider) {
|
||||||
|
cleanedList.add(currentBlock);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace list in-place to not deal with moving the result to the correct register in smali.
|
||||||
|
blocks.clear();
|
||||||
|
blocks.addAll(cleanedList);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "filterAds failure", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
extensions/nunl/stub/build.gradle.kts
Normal file
17
extensions/nunl/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/nunl/stub/src/main/AndroidManifest.xml
Normal file
1
extensions/nunl/stub/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package nl.nu.performance.api.client.interfaces;
|
||||||
|
|
||||||
|
public class Block {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package nl.nu.performance.api.client.objects;
|
||||||
|
|
||||||
|
import nl.nu.performance.api.client.interfaces.Block;
|
||||||
|
|
||||||
|
public class DividerBlock extends Block {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package nl.nu.performance.api.client.objects;
|
||||||
|
|
||||||
|
import nl.nu.performance.api.client.interfaces.Block;
|
||||||
|
|
||||||
|
public class DpgBannerBlock extends Block {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
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() {
|
||||||
|
throw new UnsupportedOperationException("Stub");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package nl.nu.performance.api.client.objects;
|
||||||
|
|
||||||
|
import nl.nu.performance.api.client.unions.LinkFlavor;
|
||||||
|
|
||||||
|
public class Link {
|
||||||
|
public final StyledText getTitle() {
|
||||||
|
throw new UnsupportedOperationException("Stub");
|
||||||
|
}
|
||||||
|
|
||||||
|
public final LinkFlavor getLinkFlavor() {
|
||||||
|
throw new UnsupportedOperationException("Stub");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package nl.nu.performance.api.client.objects;
|
||||||
|
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import nl.nu.performance.api.client.interfaces.Block;
|
||||||
|
|
||||||
|
public abstract class LinkBlock extends Block implements Parcelable {
|
||||||
|
public final Link getLink() {
|
||||||
|
throw new UnsupportedOperationException("Stub");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package nl.nu.performance.api.client.objects;
|
||||||
|
|
||||||
|
public class StyledText {
|
||||||
|
public final String getText() {
|
||||||
|
throw new UnsupportedOperationException("Stub");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package nl.nu.performance.api.client.unions;
|
||||||
|
|
||||||
|
public interface LinkFlavor {
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package nl.nu.performance.api.client.unions;
|
||||||
|
|
||||||
|
public class SmallArticleLinkFlavor implements LinkFlavor {
|
||||||
|
public final Boolean isPartner() {
|
||||||
|
throw new UnsupportedOperationException("Stub");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import android.content.pm.PackageInfo;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -356,33 +357,24 @@ public class Utils {
|
|||||||
|
|
||||||
public static Context getContext() {
|
public static Context getContext() {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
Logger.initializationException(Utils.class, "Context is null, returning null!", null);
|
Logger.initializationException(Utils.class, "Context is not set by extension hook, returning null", null);
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setContext(Context appContext) {
|
public static void setContext(Context appContext) {
|
||||||
// Must initially set context as the language settings needs it.
|
// Must initially set context to check the app language.
|
||||||
context = appContext;
|
context = appContext;
|
||||||
|
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
|
||||||
|
|
||||||
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
|
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
|
||||||
if (language != AppLanguage.DEFAULT) {
|
if (language != AppLanguage.DEFAULT) {
|
||||||
// Create a new context with the desired language.
|
// Create a new context with the desired language.
|
||||||
|
Logger.printDebug(() -> "Using app language: " + language);
|
||||||
Configuration config = appContext.getResources().getConfiguration();
|
Configuration config = appContext.getResources().getConfiguration();
|
||||||
config.setLocale(language.getLocale());
|
config.setLocale(language.getLocale());
|
||||||
context = appContext.createConfigurationContext(config);
|
context = appContext.createConfigurationContext(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies.
|
|
||||||
// Calling the regular printDebug method here can cause a Settings context null pointer exception,
|
|
||||||
// even though the context is already set before the call.
|
|
||||||
//
|
|
||||||
// The initialization logger methods do not directly or indirectly
|
|
||||||
// reference the Context or any Settings and are unaffected by this problem.
|
|
||||||
//
|
|
||||||
// Info level also helps debug if a patch hook is called before
|
|
||||||
// the context is set since debug logging is off by default.
|
|
||||||
Logger.initializationInfo(Utils.class, "Set context: " + appContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setClipboard(@NonNull String text) {
|
public static void setClipboard(@NonNull String text) {
|
||||||
@@ -808,4 +800,14 @@ public class Utils {
|
|||||||
builder.getContext().setTheme(editTextDialogStyle);
|
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,
|
DEFAULT,
|
||||||
|
|
||||||
|
// Languages codes not included with YouTube, but are translated on Crowdin
|
||||||
|
GA,
|
||||||
|
|
||||||
// Language codes found in locale_config.xml
|
// Language codes found in locale_config.xml
|
||||||
// All region specific variants have been removed.
|
// All region specific variants have been removed.
|
||||||
AF,
|
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");
|
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 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 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));
|
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
|
||||||
|
|||||||
@@ -22,12 +22,23 @@ import app.revanced.extension.shared.settings.Setting;
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that if a preference changes,
|
* Indicates that if a preference changes,
|
||||||
* to apply the change from the Setting to the UI component.
|
* to apply the change from the Setting to the UI component.
|
||||||
*/
|
*/
|
||||||
public static boolean settingImportInProgress;
|
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.
|
* Confirm and restart dialog button text and title.
|
||||||
* Set by subclasses if Strings cannot be added as a resource.
|
* Set by subclasses if Strings cannot be added as a resource.
|
||||||
@@ -35,13 +46,13 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
@Nullable
|
@Nullable
|
||||||
protected static String restartDialogButtonText, restartDialogTitle, confirmDialogTitle;
|
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) -> {
|
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
|
||||||
try {
|
try {
|
||||||
|
if (updatingPreference) {
|
||||||
|
Logger.printDebug(() -> "Ignoring preference change as sync is in progress");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str));
|
Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str));
|
||||||
if (setting == null) {
|
if (setting == null) {
|
||||||
return;
|
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'.
|
// 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);
|
updatePreference(pref, setting, true, settingImportInProgress);
|
||||||
// Update any other preference availability that may now be different.
|
// Update any other preference availability that may now be different.
|
||||||
updateUIAvailability();
|
updateUIAvailability();
|
||||||
|
updatingPreference = false;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "OnSharedPreferenceChangeListener failure", ex);
|
Logger.printException(() -> "OnSharedPreferenceChangeListener failure", ex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize this instance, and do any custom behavior.
|
* Initialize this instance, and do any custom behavior.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -81,7 +94,10 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
* so all app specific {@link Setting} instances are loaded before this method returns.
|
* so all app specific {@link Setting} instances are loaded before this method returns.
|
||||||
*/
|
*/
|
||||||
protected void initialize() {
|
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;
|
if (identifier == 0) return;
|
||||||
addPreferencesFromResource(identifier);
|
addPreferencesFromResource(identifier);
|
||||||
|
|
||||||
@@ -97,7 +113,9 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
if (confirmDialogTitle == null) {
|
if (confirmDialogTitle == null) {
|
||||||
confirmDialogTitle = str("revanced_settings_confirm_user_dialog_title");
|
confirmDialogTitle = str("revanced_settings_confirm_user_dialog_title");
|
||||||
}
|
}
|
||||||
|
|
||||||
showingUserDialogMessage = true;
|
showingUserDialogMessage = true;
|
||||||
|
|
||||||
new AlertDialog.Builder(context)
|
new AlertDialog.Builder(context)
|
||||||
.setTitle(confirmDialogTitle)
|
.setTitle(confirmDialogTitle)
|
||||||
.setMessage(Objects.requireNonNull(setting.userDialogMessage).toString())
|
.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.
|
* @return If the preference is currently set to the default value of the Setting.
|
||||||
*/
|
*/
|
||||||
protected boolean prefIsSetToDefault(Preference pref, Setting<?> setting) {
|
protected boolean prefIsSetToDefault(Preference pref, Setting<?> setting) {
|
||||||
|
Object defaultValue = setting.defaultValue;
|
||||||
if (pref instanceof SwitchPreference switchPref) {
|
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) {
|
if (pref instanceof EditTextPreference editPreference) {
|
||||||
return editPreference.getText().equals(setting.defaultValue.toString());
|
return editPreference.getText().equals(defaultValueString);
|
||||||
}
|
}
|
||||||
if (pref instanceof ListPreference listPref) {
|
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 "
|
throw new IllegalStateException("Must override method to handle "
|
||||||
@@ -158,16 +178,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
/**
|
/**
|
||||||
* Syncs all UI Preferences to any {@link Setting} they represent.
|
* Syncs all UI Preferences to any {@link Setting} they represent.
|
||||||
*/
|
*/
|
||||||
private void updatePreferenceScreen(@NonNull PreferenceScreen screen,
|
private void updatePreferenceScreen(@NonNull PreferenceGroup group,
|
||||||
boolean syncSettingValue,
|
boolean syncSettingValue,
|
||||||
boolean applySettingToPreference) {
|
boolean applySettingToPreference) {
|
||||||
// Alternatively this could iterate thru all Settings and check for any matching Preferences,
|
// Alternatively this could iterate thru all Settings and check for any matching Preferences,
|
||||||
// but there are many more Settings than UI preferences so it's more efficient to only check
|
// but there are many more Settings than UI preferences so it's more efficient to only check
|
||||||
// the Preferences.
|
// the Preferences.
|
||||||
for (int i = 0, prefCount = screen.getPreferenceCount(); i < prefCount; i++) {
|
for (int i = 0, prefCount = group.getPreferenceCount(); i < prefCount; i++) {
|
||||||
Preference pref = screen.getPreference(i);
|
Preference pref = group.getPreference(i);
|
||||||
if (pref instanceof PreferenceScreen) {
|
if (pref instanceof PreferenceGroup subGroup) {
|
||||||
updatePreferenceScreen((PreferenceScreen) pref, syncSettingValue, applySettingToPreference);
|
updatePreferenceScreen(subGroup, syncSettingValue, applySettingToPreference);
|
||||||
} else if (pref.hasKey()) {
|
} else if (pref.hasKey()) {
|
||||||
String key = pref.getKey();
|
String key = pref.getKey();
|
||||||
Setting<?> setting = Setting.getSettingFromPath(key);
|
Setting<?> setting = Setting.getSettingFromPath(key);
|
||||||
@@ -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();
|
Utils.verifyOnMainThread();
|
||||||
if (restartDialogTitle == null) {
|
if (restartDialogTitle == null) {
|
||||||
restartDialogTitle = str("revanced_settings_restart_title");
|
restartDialogTitle = str("revanced_settings_restart_title");
|
||||||
@@ -263,6 +283,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
|
|||||||
if (restartDialogButtonText == null) {
|
if (restartDialogButtonText == null) {
|
||||||
restartDialogButtonText = str("revanced_settings_restart");
|
restartDialogButtonText = str("revanced_settings_restart");
|
||||||
}
|
}
|
||||||
|
|
||||||
new AlertDialog.Builder(context)
|
new AlertDialog.Builder(context)
|
||||||
.setMessage(restartDialogTitle)
|
.setMessage(restartDialogTitle)
|
||||||
.setPositiveButton(restartDialogButtonText, (dialog, id)
|
.setPositiveButton(restartDialogButtonText, (dialog, id)
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package app.revanced.extension.shared.settings.preference;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.preference.PreferenceCategory;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty preference category with no title, used to organize and group related preferences together.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
|
public class NoTitlePreferenceCategory extends PreferenceCategory {
|
||||||
|
|
||||||
|
public NoTitlePreferenceCategory(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoTitlePreferenceCategory(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NoTitlePreferenceCategory(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressLint("MissingSuperCall")
|
||||||
|
protected View onCreateView(ViewGroup parent) {
|
||||||
|
// 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;
|
package app.revanced.extension.shared.settings.preference;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -8,17 +10,23 @@ import android.util.AttributeSet;
|
|||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Utils;
|
import androidx.annotation.Nullable;
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
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"})
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
public class ResettableEditTextPreference extends EditTextPreference {
|
public class ResettableEditTextPreference extends EditTextPreference {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting to reset.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private Setting<?> setting;
|
||||||
|
|
||||||
public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public ResettableEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
}
|
}
|
||||||
@@ -32,12 +40,22 @@ public class ResettableEditTextPreference extends EditTextPreference {
|
|||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSetting(@Nullable Setting<?> setting) {
|
||||||
|
this.setting = setting;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
super.onPrepareDialogBuilder(builder);
|
super.onPrepareDialogBuilder(builder);
|
||||||
Utils.setEditTextDialogTheme(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) {
|
if (setting != null) {
|
||||||
builder.setNeutralButton(str("revanced_settings_reset"), null);
|
builder.setNeutralButton(str("revanced_settings_reset"), null);
|
||||||
}
|
}
|
||||||
@@ -54,8 +72,7 @@ public class ResettableEditTextPreference extends EditTextPreference {
|
|||||||
}
|
}
|
||||||
button.setOnClickListener(v -> {
|
button.setOnClickListener(v -> {
|
||||||
try {
|
try {
|
||||||
Setting<?> setting = Objects.requireNonNull(Setting.getSettingFromPath(getKey()));
|
String defaultStringValue = Objects.requireNonNull(setting).defaultValue.toString();
|
||||||
String defaultStringValue = setting.defaultValue.toString();
|
|
||||||
EditText editText = getEditText();
|
EditText editText = getEditText();
|
||||||
editText.setText(defaultStringValue);
|
editText.setText(defaultStringValue);
|
||||||
editText.setSelection(defaultStringValue.length()); // move cursor to end of text
|
editText.setSelection(defaultStringValue.length()); // move cursor to end of text
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public enum ClientType {
|
|||||||
ANDROID_VR_NO_AUTH.clientVersion,
|
ANDROID_VR_NO_AUTH.clientVersion,
|
||||||
ANDROID_VR_NO_AUTH.requiresAuth,
|
ANDROID_VR_NO_AUTH.requiresAuth,
|
||||||
true,
|
true,
|
||||||
"Android VR"
|
"Android VR Auth"
|
||||||
);
|
);
|
||||||
|
|
||||||
private static boolean forceAVC() {
|
private static boolean forceAVC() {
|
||||||
|
|||||||
@@ -107,6 +107,36 @@ public class SpoofVideoStreamsPatch {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
* Turns off a feature flag that interferes with spoofing.
|
||||||
|
*/
|
||||||
|
public static boolean useMediaFetchHotConfigReplacement(boolean original) {
|
||||||
|
if (original) {
|
||||||
|
Logger.printDebug(() -> "useMediaFetchHotConfigReplacement is set on");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SPOOF_STREAMING_DATA) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
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.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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_;
|
||||||
|
}
|
||||||
@@ -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.FeedFilterPreferenceCategory;
|
||||||
import app.revanced.extension.tiktok.settings.preference.categories.ExtensionPreferenceCategory;
|
import app.revanced.extension.tiktok.settings.preference.categories.ExtensionPreferenceCategory;
|
||||||
import app.revanced.extension.tiktok.settings.preference.categories.SimSpoofPreferenceCategory;
|
import app.revanced.extension.tiktok.settings.preference.categories.SimSpoofPreferenceCategory;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preference fragment for ReVanced settings
|
* Preference fragment for ReVanced settings
|
||||||
|
|||||||
@@ -1,37 +1,60 @@
|
|||||||
package app.revanced.extension.tiktok.spoof.sim;
|
package app.revanced.extension.tiktok.spoof.sim;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.tiktok.settings.Settings;
|
import app.revanced.extension.tiktok.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class SpoofSimPatch {
|
public class SpoofSimPatch {
|
||||||
|
|
||||||
private static final boolean ENABLED = Settings.SIM_SPOOF.get();
|
/**
|
||||||
|
* During app startup native code can be called with no obvious way to set the context.
|
||||||
|
* Cannot check if sim spoofing is enabled or the app will crash since no context is set.
|
||||||
|
*/
|
||||||
|
private static boolean isContextNotSet(String fieldSpoofed) {
|
||||||
|
if (Utils.getContext() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.initializationException(SpoofSimPatch.class,
|
||||||
|
"Context is not yet set, cannot spoof: " + fieldSpoofed, null);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getCountryIso(String value) {
|
public static String getCountryIso(String value) {
|
||||||
if (ENABLED) {
|
if (isContextNotSet("countryIso")) return value;
|
||||||
|
|
||||||
|
if (Settings.SIM_SPOOF.get()) {
|
||||||
String iso = Settings.SIM_SPOOF_ISO.get();
|
String iso = Settings.SIM_SPOOF_ISO.get();
|
||||||
Logger.printDebug(() -> "Spoofing sim ISO from: " + value + " to: " + iso);
|
Logger.printDebug(() -> "Spoofing countryIso from: " + value + " to: " + iso);
|
||||||
return iso;
|
return iso;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getOperator(String value) {
|
public static String getOperator(String value) {
|
||||||
if (ENABLED) {
|
if (isContextNotSet("MCC-MNC")) return value;
|
||||||
|
|
||||||
|
if (Settings.SIM_SPOOF.get()) {
|
||||||
String mcc_mnc = Settings.SIMSPOOF_MCCMNC.get();
|
String mcc_mnc = Settings.SIMSPOOF_MCCMNC.get();
|
||||||
Logger.printDebug(() -> "Spoofing sim MCC-MNC from: " + value + " to: " + mcc_mnc);
|
Logger.printDebug(() -> "Spoofing sim MCC-MNC from: " + value + " to: " + mcc_mnc);
|
||||||
return mcc_mnc;
|
return mcc_mnc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getOperatorName(String value) {
|
public static String getOperatorName(String value) {
|
||||||
if (ENABLED) {
|
if (isContextNotSet("operatorName")) return value;
|
||||||
|
|
||||||
|
if (Settings.SIM_SPOOF.get()) {
|
||||||
String operator = Settings.SIMSPOOF_OP_NAME.get();
|
String operator = Settings.SIMSPOOF_OP_NAME.get();
|
||||||
Logger.printDebug(() -> "Spoofing sim operator from: " + value + " to: " + operator);
|
Logger.printDebug(() -> "Spoofing sim operatorName from: " + value + " to: " + operator);
|
||||||
return operator;
|
return operator;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ internal object TwiFucker {
|
|||||||
|
|
||||||
private fun JSONObject.entryIsWhoToFollow(): Boolean =
|
private fun JSONObject.entryIsWhoToFollow(): Boolean =
|
||||||
optString("entryId").let {
|
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 =
|
private fun JSONObject.itemContainsPromotedUser(): Boolean =
|
||||||
|
|||||||
@@ -45,13 +45,24 @@ public class ThemeHelper {
|
|||||||
return "@color/yt_black3";
|
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),
|
* @return The dark theme color as specified by the Theme patch (if included),
|
||||||
* or the dark mode background color unpatched YT uses.
|
* or the dark mode background color unpatched YT uses.
|
||||||
*/
|
*/
|
||||||
public static int getDarkThemeColor() {
|
public static int getDarkThemeColor() {
|
||||||
if (darkThemeColor == null) {
|
if (darkThemeColor == null) {
|
||||||
darkThemeColor = getColorInt(darkThemeResourceName());
|
darkThemeColor = getThemeColor(darkThemeResourceName(), Color.BLACK);
|
||||||
}
|
}
|
||||||
return darkThemeColor;
|
return darkThemeColor;
|
||||||
}
|
}
|
||||||
@@ -71,18 +82,11 @@ public class ThemeHelper {
|
|||||||
*/
|
*/
|
||||||
public static int getLightThemeColor() {
|
public static int getLightThemeColor() {
|
||||||
if (lightThemeColor == null) {
|
if (lightThemeColor == null) {
|
||||||
lightThemeColor = getColorInt(lightThemeResourceName());
|
lightThemeColor = getThemeColor(lightThemeResourceName(), Color.WHITE);
|
||||||
}
|
}
|
||||||
return lightThemeColor;
|
return lightThemeColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getColorInt(String colorString) {
|
|
||||||
if (colorString.startsWith("#")) {
|
|
||||||
return Color.parseColor(colorString);
|
|
||||||
}
|
|
||||||
return Utils.getResourceColor(colorString);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getBackgroundColor() {
|
public static int getBackgroundColor() {
|
||||||
return isDarkTheme() ? getDarkThemeColor() : getLightThemeColor();
|
return isDarkTheme() ? getDarkThemeColor() : getLightThemeColor();
|
||||||
}
|
}
|
||||||
@@ -96,6 +100,6 @@ public class ThemeHelper {
|
|||||||
? "yt_black3"
|
? "yt_black3"
|
||||||
: "yt_white1";
|
: "yt_white1";
|
||||||
|
|
||||||
return getColorInt(colorName);
|
return Utils.getColorFromString(colorName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package app.revanced.extension.youtube.patches;
|
|||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class BackgroundPlaybackPatch {
|
public class BackgroundPlaybackPatch {
|
||||||
@@ -23,16 +24,13 @@ public class BackgroundPlaybackPatch {
|
|||||||
// 7. Close the Short
|
// 7. Close the Short
|
||||||
// 8. Resume playing the regular video
|
// 8. Resume playing the regular video
|
||||||
// 9. Minimize the app (PIP should appear)
|
// 9. Minimize the app (PIP should appear)
|
||||||
if (!VideoInformation.lastVideoIdIsShort()) {
|
if (ShortsPlayerState.isOpen()) {
|
||||||
return true; // Definitely is not a Short.
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add better hook.
|
// Check if the video player is opened and it's not playing in the feed.
|
||||||
// Might be a Shorts, or might be a prior regular video on screen again after a Shorts was closed.
|
PlayerType current = PlayerType.getCurrent();
|
||||||
// This incorrectly prevents PIP if player is in WATCH_WHILE_MINIMIZED after closing a Shorts,
|
return !current.isNoneOrHidden() && current != PlayerType.INLINE_MINIMAL;
|
||||||
// But there's no way around this unless an additional hook is added to definitively detect
|
|
||||||
// the Shorts player is on screen. This use case is unusual anyways so it's not a huge concern.
|
|
||||||
return !PlayerType.getCurrent().isNoneHiddenOrMinimized();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
|
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Utils;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
import app.revanced.extension.youtube.shared.NavigationBar;
|
||||||
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class ChangeFormFactorPatch {
|
public class ChangeFormFactorPatch {
|
||||||
@@ -41,14 +49,57 @@ public class ChangeFormFactorPatch {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static final Integer FORM_FACTOR_TYPE = Settings.CHANGE_FORM_FACTOR.get().formFactorType;
|
private static final Integer FORM_FACTOR_TYPE = Settings.CHANGE_FORM_FACTOR.get().formFactorType;
|
||||||
|
private static final boolean USING_AUTOMOTIVE_TYPE = Objects.requireNonNull(
|
||||||
|
FormFactor.AUTOMOTIVE.formFactorType).equals(FORM_FACTOR_TYPE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static int getFormFactor(int original) {
|
public static int getFormFactor(int original) {
|
||||||
return FORM_FACTOR_TYPE == null
|
if (FORM_FACTOR_TYPE == null) return original;
|
||||||
? original
|
|
||||||
: FORM_FACTOR_TYPE;
|
if (USING_AUTOMOTIVE_TYPE) {
|
||||||
|
// Do not change if the player is opening or is opened,
|
||||||
|
// otherwise the video description cannot be opened.
|
||||||
|
PlayerType current = PlayerType.getCurrent();
|
||||||
|
if (current.isMaximizedOrFullscreen() || current == PlayerType.WATCH_WHILE_SLIDING_MINIMIZED_MAXIMIZED) {
|
||||||
|
Logger.printDebug(() -> "Using original form factor for player");
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NavigationBar.isSearchBarActive()) {
|
||||||
|
// Automotive type shows error 400 when opening a channel page and using some explore tab.
|
||||||
|
// This is a bug in unpatched YouTube that occurs on actual Android Automotive devices.
|
||||||
|
// Work around the issue by using the original form factor if not in search and the
|
||||||
|
// navigation back button is present.
|
||||||
|
if (NavigationBar.isBackButtonVisible()) {
|
||||||
|
Logger.printDebug(() -> "Using original form factor, as back button is visible without search present");
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not change library tab otherwise watch history is hidden.
|
||||||
|
// Do this check last since the current navigation button is required.
|
||||||
|
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.LIBRARY) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FORM_FACTOR_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void navigationTabCreated(NavigationButton button, View tabView) {
|
||||||
|
// On first startup of the app the navigation buttons are fetched and updated.
|
||||||
|
// If the user immediately opens the 'You' or opens a video, then the call to
|
||||||
|
// update the navigtation buttons will use the non automotive form factor
|
||||||
|
// and the explore tab is missing.
|
||||||
|
// Fixing this is not so simple because of the concurrent calls for the player and You tab.
|
||||||
|
// For now, always hide the explore tab.
|
||||||
|
if (USING_AUTOMOTIVE_TYPE && button == NavigationButton.EXPLORE) {
|
||||||
|
tabView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class DisableAutoCaptionsPatch {
|
public class DisableAutoCaptionsPatch {
|
||||||
@@ -14,7 +14,7 @@ public class DisableAutoCaptionsPatch {
|
|||||||
public static boolean autoCaptionsEnabled() {
|
public static boolean autoCaptionsEnabled() {
|
||||||
return Settings.AUTO_CAPTIONS.get()
|
return Settings.AUTO_CAPTIONS.get()
|
||||||
// Do not use auto captions for Shorts.
|
// Do not use auto captions for Shorts.
|
||||||
&& !PlayerType.getCurrent().isNoneHiddenOrSlidingMinimized();
|
&& ShortsPlayerState.isOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package app.revanced.extension.youtube.patches;
|
|||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
/** @noinspection unused*/
|
@SuppressWarnings("unused")
|
||||||
public class DisableResumingStartupShortsPlayerPatch {
|
public class DisableResumingStartupShortsPlayerPatch {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,4 +11,11 @@ public class DisableResumingStartupShortsPlayerPatch {
|
|||||||
public static boolean disableResumingStartupShortsPlayer() {
|
public static boolean disableResumingStartupShortsPlayer() {
|
||||||
return Settings.DISABLE_RESUMING_SHORTS_PLAYER.get();
|
return Settings.DISABLE_RESUMING_SHORTS_PLAYER.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static boolean disableResumingStartupShortsPlayer(boolean original) {
|
||||||
|
return original && !Settings.DISABLE_RESUMING_SHORTS_PLAYER.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
/** @noinspection unused*/
|
|
||||||
public final class DisableSuggestedVideoEndScreenPatch {
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
private static ImageView lastView;
|
|
||||||
|
|
||||||
public static void closeEndScreen(final ImageView imageView) {
|
|
||||||
if (!Settings.DISABLE_SUGGESTED_VIDEO_END_SCREEN.get()) return;
|
|
||||||
|
|
||||||
// Prevent adding the listener multiple times.
|
|
||||||
if (lastView == imageView) return;
|
|
||||||
lastView = imageView;
|
|
||||||
|
|
||||||
imageView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
|
|
||||||
if (imageView.isShown()) imageView.callOnClick();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,7 @@ public final class EnableDebuggingPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean isBooleanFeatureFlagEnabled(boolean value, long flag) {
|
public static boolean isBooleanFeatureFlagEnabled(boolean value, Long flag) {
|
||||||
if (LOG_FEATURE_FLAGS && value) {
|
if (LOG_FEATURE_FLAGS && value) {
|
||||||
if (featureFlags.putIfAbsent(flag, true) == null) {
|
if (featureFlags.putIfAbsent(flag, true) == null) {
|
||||||
Logger.printDebug(() -> "boolean feature is enabled: " + flag);
|
Logger.printDebug(() -> "boolean feature is enabled: " + flag);
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public final class HideEndScreenSuggestedVideoPatch {
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static boolean hideEndScreenSuggestedVideo() {
|
||||||
|
return Settings.HIDE_END_SCREEN_SUGGESTED_VIDEO.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,10 +43,13 @@ public final class MiniplayerPatch {
|
|||||||
MODERN_2(null, 2),
|
MODERN_2(null, 2),
|
||||||
MODERN_3(null, 3),
|
MODERN_3(null, 3),
|
||||||
/**
|
/**
|
||||||
* Half broken miniplayer, that might be work in progress or left over abandoned code.
|
* Works and is functional with 20.03+
|
||||||
* Can force this type by editing the import/export settings.
|
|
||||||
*/
|
*/
|
||||||
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.
|
* Legacy tablet hook value.
|
||||||
@@ -126,12 +129,13 @@ public final class MiniplayerPatch {
|
|||||||
private static final boolean DRAG_AND_DROP_ENABLED =
|
private static final boolean DRAG_AND_DROP_ENABLED =
|
||||||
CURRENT_TYPE.isModern() && Settings.MINIPLAYER_DRAG_AND_DROP.get();
|
CURRENT_TYPE.isModern() && Settings.MINIPLAYER_DRAG_AND_DROP.get();
|
||||||
|
|
||||||
private static final boolean HIDE_EXPAND_CLOSE_ENABLED =
|
private static final boolean HIDE_OVERLAY_BUTTONS_ENABLED =
|
||||||
Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.get()
|
Settings.MINIPLAYER_HIDE_OVERLAY_BUTTONS.get()
|
||||||
&& Settings.MINIPLAYER_HIDE_EXPAND_CLOSE.isAvailable();
|
&& Settings.MINIPLAYER_HIDE_OVERLAY_BUTTONS.isAvailable();
|
||||||
|
|
||||||
private static final boolean HIDE_SUBTEXT_ENABLED =
|
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,
|
// 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.
|
// 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());
|
&& (VersionCheckPatch.IS_19_34_OR_GREATER || Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get());
|
||||||
|
|
||||||
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
|
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 =
|
private static final boolean MINIPLAYER_HORIZONTAL_DRAG_ENABLED =
|
||||||
DRAG_AND_DROP_ENABLED && Settings.MINIPLAYER_HORIZONTAL_DRAG.get();
|
DRAG_AND_DROP_ENABLED && Settings.MINIPLAYER_HORIZONTAL_DRAG.get();
|
||||||
@@ -172,11 +176,12 @@ public final class MiniplayerPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class MiniplayerHideExpandCloseAvailability implements Setting.Availability {
|
public static final class MiniplayerHideOverlayButtonsAvailability implements Setting.Availability {
|
||||||
@Override
|
@Override
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
MiniplayerType type = Settings.MINIPLAYER_TYPE.get();
|
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
|
|| (!IS_19_26_OR_GREATER && type == MODERN_1
|
||||||
&& !Settings.MINIPLAYER_DOUBLE_TAP_ACTION.get() && !Settings.MINIPLAYER_DRAG_AND_DROP.get())
|
&& !Settings.MINIPLAYER_DOUBLE_TAP_ACTION.get() && !Settings.MINIPLAYER_DRAG_AND_DROP.get())
|
||||||
|| (IS_19_29_OR_GREATER && type == MODERN_3);
|
|| (IS_19_29_OR_GREATER && type == MODERN_3);
|
||||||
@@ -227,9 +232,13 @@ public final class MiniplayerPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void adjustMiniplayerOpacity(ImageView view) {
|
public static void adjustMiniplayerOpacity(View view) {
|
||||||
if (CURRENT_TYPE == MODERN_1) {
|
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 +256,7 @@ public final class MiniplayerPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean enableMiniplayerDoubleTapAction(boolean original) {
|
public static boolean getMiniplayerDoubleTapAction(boolean original) {
|
||||||
if (CURRENT_TYPE == DEFAULT) {
|
if (CURRENT_TYPE == DEFAULT) {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
@@ -258,7 +267,7 @@ public final class MiniplayerPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean enableMiniplayerDragAndDrop(boolean original) {
|
public static boolean getMiniplayerDragAndDrop(boolean original) {
|
||||||
if (CURRENT_TYPE == DEFAULT) {
|
if (CURRENT_TYPE == DEFAULT) {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
@@ -266,13 +275,36 @@ public final class MiniplayerPatch {
|
|||||||
return DRAG_AND_DROP_ENABLED;
|
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.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean setRoundedCorners(boolean original) {
|
public static boolean getHorizontalDrag(boolean original) {
|
||||||
if (CURRENT_TYPE.isModern()) {
|
if (CURRENT_TYPE == DEFAULT) {
|
||||||
return MINIPLAYER_ROUNDED_CORNERS_ENABLED;
|
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;
|
return original;
|
||||||
@@ -281,7 +313,7 @@ public final class MiniplayerPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static int setMiniplayerDefaultSize(int original) {
|
public static int getMiniplayerDefaultSize(int original) {
|
||||||
if (CURRENT_TYPE.isModern()) {
|
if (CURRENT_TYPE.isModern()) {
|
||||||
return MINIPLAYER_SIZE;
|
return MINIPLAYER_SIZE;
|
||||||
}
|
}
|
||||||
@@ -289,29 +321,26 @@ public final class MiniplayerPatch {
|
|||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void hideMiniplayerExpandClose(View view) {
|
||||||
|
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_OVERLAY_BUTTONS_ENABLED, view);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static boolean setHorizontalDrag(boolean original) {
|
public static void hideMiniplayerActionButton(View view) {
|
||||||
if (CURRENT_TYPE.isModern()) {
|
if (CURRENT_TYPE == MODERN_4) {
|
||||||
return MINIPLAYER_HORIZONTAL_DRAG_ENABLED;
|
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_OVERLAY_BUTTONS_ENABLED, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
return original;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void hideMiniplayerExpandClose(ImageView view) {
|
public static void hideMiniplayerRewindForward(View view) {
|
||||||
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_EXPAND_CLOSE_ENABLED, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
*/
|
|
||||||
public static void hideMiniplayerRewindForward(ImageView view) {
|
|
||||||
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_REWIND_FORWARD_ENABLED, view);
|
Utils.hideViewByRemovingFromParentUnderCondition(HIDE_REWIND_FORWARD_ENABLED, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public final class NavigationButtonsPatch {
|
|||||||
{
|
{
|
||||||
put(NavigationButton.HOME, Settings.HIDE_HOME_BUTTON.get());
|
put(NavigationButton.HOME, Settings.HIDE_HOME_BUTTON.get());
|
||||||
put(NavigationButton.CREATE, Settings.HIDE_CREATE_BUTTON.get());
|
put(NavigationButton.CREATE, Settings.HIDE_CREATE_BUTTON.get());
|
||||||
|
put(NavigationButton.NOTIFICATIONS, Settings.HIDE_NOTIFICATIONS_BUTTON.get());
|
||||||
put(NavigationButton.SHORTS, Settings.HIDE_SHORTS_BUTTON.get());
|
put(NavigationButton.SHORTS, Settings.HIDE_SHORTS_BUTTON.get());
|
||||||
put(NavigationButton.SUBSCRIPTIONS, Settings.HIDE_SUBSCRIPTIONS_BUTTON.get());
|
put(NavigationButton.SUBSCRIPTIONS, Settings.HIDE_SUBSCRIPTIONS_BUTTON.get());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||||
import app.revanced.extension.youtube.shared.VideoState;
|
import app.revanced.extension.youtube.shared.VideoState;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -24,4 +27,26 @@ public class PlayerTypeHookPatch {
|
|||||||
|
|
||||||
VideoState.setFromString(youTubeVideoState.name());
|
VideoState.setFromString(youTubeVideoState.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*
|
||||||
|
* Add a listener to the shorts player overlay View.
|
||||||
|
* Triggered when a shorts player is attached or detached to Windows.
|
||||||
|
*
|
||||||
|
* @param view shorts player overlay (R.id.reel_watch_player).
|
||||||
|
*/
|
||||||
|
public static void onShortsCreate(View view) {
|
||||||
|
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onViewAttachedToWindow(@Nullable View v) {
|
||||||
|
ShortsPlayerState.setOpen(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewDetachedFromWindow(@Nullable View v) {
|
||||||
|
ShortsPlayerState.setOpen(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,26 +2,21 @@ package app.revanced.extension.youtube.patches;
|
|||||||
|
|
||||||
import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike.Vote;
|
import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike.Vote;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.drawable.ShapeDrawable;
|
import android.graphics.drawable.ShapeDrawable;
|
||||||
import android.text.Spannable;
|
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilterPatch;
|
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.ReturnYouTubeDislike;
|
||||||
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
@@ -47,9 +42,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class ReturnYouTubeDislikePatch {
|
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.
|
* RYD data for the current video on screen.
|
||||||
*/
|
*/
|
||||||
@@ -64,12 +56,12 @@ public class ReturnYouTubeDislikePatch {
|
|||||||
private static volatile ReturnYouTubeDislike lastLithoShortsVideoData;
|
private static volatile ReturnYouTubeDislike lastLithoShortsVideoData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because the litho Shorts spans are created after {@link ReturnYouTubeDislikeFilterPatch}
|
* Because litho Shorts spans are created offscreen after {@link ReturnYouTubeDislikeFilterPatch}
|
||||||
* detects the video ids, after the user votes the litho will update
|
* detects the video ids, but the current Short can arbitrarily reload the same span,
|
||||||
* but {@link #lastLithoShortsVideoData} is not the correct data to use.
|
* then use the {@link #lastLithoShortsVideoData} if this value is greater than zero.
|
||||||
* If this is true, then instead use {@link #currentVideoData}.
|
|
||||||
*/
|
*/
|
||||||
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.
|
* 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() {
|
private static void clearData() {
|
||||||
currentVideoData = null;
|
currentVideoData = null;
|
||||||
lastLithoShortsVideoData = null;
|
lastLithoShortsVideoData = null;
|
||||||
lithoShortsShouldUseCurrentData = false;
|
synchronized (ReturnYouTubeDislike.class) {
|
||||||
|
useLithoShortsVideoDataCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Rolling number text should not be cleared,
|
// Rolling number text should not be cleared,
|
||||||
// as it's used if incognito Short is opened/closed
|
// as it's used if incognito Short is opened/closed
|
||||||
// while a regular video is on screen.
|
// 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.
|
// Litho player for both regular videos and Shorts.
|
||||||
@@ -156,10 +164,13 @@ public class ReturnYouTubeDislikePatch {
|
|||||||
return getShortsSpan(original, true);
|
return getShortsSpan(original, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversionContextString.contains("|shorts_like_button.eml")
|
if (conversionContextString.contains("|shorts_like_button.eml")) {
|
||||||
&& !Utils.containsNumber(original)) {
|
if (!Utils.containsNumber(original)) {
|
||||||
Logger.printDebug(() -> "Replacing hidden likes count");
|
Logger.printDebug(() -> "Replacing hidden likes count");
|
||||||
return getShortsSpan(original, false);
|
return getShortsSpan(original, false);
|
||||||
|
} else {
|
||||||
|
decrementUseLithoDataIfNeeded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onLithoTextLoaded failure", ex);
|
Logger.printException(() -> "onLithoTextLoaded failure", ex);
|
||||||
@@ -174,7 +185,14 @@ public class ReturnYouTubeDislikePatch {
|
|||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnYouTubeDislike videoData = lastLithoShortsVideoData;
|
final ReturnYouTubeDislike videoData;
|
||||||
|
if (decrementUseLithoDataIfNeeded()) {
|
||||||
|
// New Short is loading off screen.
|
||||||
|
videoData = lastLithoShortsVideoData;
|
||||||
|
} else {
|
||||||
|
videoData = currentVideoData;
|
||||||
|
}
|
||||||
|
|
||||||
if (videoData == null) {
|
if (videoData == null) {
|
||||||
// The Shorts litho video id filter did not detect the video id.
|
// The Shorts litho video id filter did not detect the video id.
|
||||||
// This is normal in incognito mode, but otherwise is abnormal.
|
// This is normal in incognito mode, but otherwise is abnormal.
|
||||||
@@ -182,19 +200,6 @@ public class ReturnYouTubeDislikePatch {
|
|||||||
return original;
|
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
|
return isDislikesSpan
|
||||||
? videoData.getDislikeSpanForShort((Spanned) original)
|
? videoData.getDislikeSpanForShort((Spanned) original)
|
||||||
: videoData.getLikeSpanForShort((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).
|
// Video Id and voting hooks (all players).
|
||||||
//
|
//
|
||||||
@@ -503,8 +377,7 @@ public class ReturnYouTubeDislikePatch {
|
|||||||
if (videoIdIsShort && (!isShortAndOpeningOrPlaying || !Settings.RYD_SHORTS.get())) {
|
if (videoIdIsShort && (!isShortAndOpeningOrPlaying || !Settings.RYD_SHORTS.get())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final boolean waitForFetchToComplete = !IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
final boolean waitForFetchToComplete = videoIdIsShort && !lastPlayerResponseWasShort;
|
||||||
&& videoIdIsShort && !lastPlayerResponseWasShort;
|
|
||||||
|
|
||||||
Logger.printDebug(() -> "Prefetching RYD for video: " + videoId);
|
Logger.printDebug(() -> "Prefetching RYD for video: " + videoId);
|
||||||
ReturnYouTubeDislike fetch = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
ReturnYouTubeDislike fetch = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||||
@@ -557,12 +430,6 @@ public class ReturnYouTubeDislikePatch {
|
|||||||
data.setVideoIdIsShort(true);
|
data.setVideoIdIsShort(true);
|
||||||
}
|
}
|
||||||
currentVideoData = data;
|
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) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "newVideoLoaded failure", ex);
|
Logger.printException(() -> "newVideoLoaded failure", ex);
|
||||||
}
|
}
|
||||||
@@ -587,7 +454,10 @@ public class ReturnYouTubeDislikePatch {
|
|||||||
ReturnYouTubeDislike videoData = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
ReturnYouTubeDislike videoData = ReturnYouTubeDislike.getFetchForVideoId(videoId);
|
||||||
videoData.setVideoIdIsShort(true);
|
videoData.setVideoIdIsShort(true);
|
||||||
lastLithoShortsVideoData = videoData;
|
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) {
|
private static boolean videoIdIsSame(@Nullable ReturnYouTubeDislike fetch, @Nullable String videoId) {
|
||||||
@@ -622,13 +492,6 @@ public class ReturnYouTubeDislikePatch {
|
|||||||
for (Vote v : Vote.values()) {
|
for (Vote v : Vote.values()) {
|
||||||
if (v.value == vote) {
|
if (v.value == vote) {
|
||||||
videoData.sendVote(v);
|
videoData.sendVote(v);
|
||||||
|
|
||||||
if (isNoneHiddenOrMinimized) {
|
|
||||||
if (lastLithoShortsVideoData != null) {
|
|
||||||
lithoShortsShouldUseCurrentData = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,15 @@ package app.revanced.extension.youtube.patches;
|
|||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
|
|
||||||
public class VersionCheckPatch {
|
public class VersionCheckPatch {
|
||||||
public static final boolean IS_19_17_OR_GREATER = Utils.getAppVersionName().compareTo("19.17.00") >= 0;
|
private static boolean isVersionOrGreater(String version) {
|
||||||
public static final boolean IS_19_20_OR_GREATER = Utils.getAppVersionName().compareTo("19.20.00") >= 0;
|
return Utils.getAppVersionName().compareTo(version) >= 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_17_OR_GREATER = isVersionOrGreater("19.17.00");
|
||||||
public static final boolean IS_19_34_OR_GREATER = Utils.getAppVersionName().compareTo("19.34.00") >= 0;
|
public static final boolean IS_19_20_OR_GREATER = isVersionOrGreater("19.20.00");
|
||||||
public static final boolean IS_19_46_OR_GREATER = Utils.getAppVersionName().compareTo("19.46.00") >= 0;
|
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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ public final class AdsFilter extends Filter {
|
|||||||
"video_display_button_group_layout",
|
"video_display_button_group_layout",
|
||||||
"landscape_image_wide_button_layout",
|
"landscape_image_wide_button_layout",
|
||||||
"video_display_carousel_button_group_layout",
|
"video_display_carousel_button_group_layout",
|
||||||
|
"video_display_full_buttoned_short_dr_layout",
|
||||||
"compact_landscape_image_layout", // Tablet layout search results.
|
"compact_landscape_image_layout", // Tablet layout search results.
|
||||||
"text_image_no_button_layout" // Tablet layout search results.
|
"text_image_no_button_layout" // Tablet layout search results.
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,20 +2,20 @@ package app.revanced.extension.youtube.patches.components;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.patches.playback.quality.RestoreOldVideoQualityMenuPatch;
|
import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abuse LithoFilter for {@link RestoreOldVideoQualityMenuPatch}.
|
* Abuse LithoFilter for {@link AdvancedVideoQualityMenuPatch}.
|
||||||
*/
|
*/
|
||||||
public final class VideoQualityMenuFilterPatch extends Filter {
|
public final class AdvancedVideoQualityMenuFilter extends Filter {
|
||||||
// Must be volatile or synchronized, as litho filtering runs off main thread
|
// Must be volatile or synchronized, as litho filtering runs off main thread
|
||||||
// and this field is then access from the main thread.
|
// and this field is then access from the main thread.
|
||||||
public static volatile boolean isVideoQualityMenuVisible;
|
public static volatile boolean isVideoQualityMenuVisible;
|
||||||
|
|
||||||
public VideoQualityMenuFilterPatch() {
|
public AdvancedVideoQualityMenuFilter() {
|
||||||
addPathCallbacks(new StringFilterGroup(
|
addPathCallbacks(new StringFilterGroup(
|
||||||
Settings.RESTORE_OLD_VIDEO_QUALITY_MENU,
|
Settings.ADVANCED_VIDEO_QUALITY_MENU,
|
||||||
"quick_quality_sheet_content.eml-js"
|
"quick_quality_sheet_content.eml-js"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -12,10 +12,12 @@ final class CommentsFilter extends Filter {
|
|||||||
|
|
||||||
private final StringFilterGroup commentComposer;
|
private final StringFilterGroup commentComposer;
|
||||||
private final ByteArrayFilterGroup emojiPickerBufferGroup;
|
private final ByteArrayFilterGroup emojiPickerBufferGroup;
|
||||||
|
private final StringFilterGroup filterChipBar;
|
||||||
|
private final ByteArrayFilterGroup aiCommentsSummary;
|
||||||
|
|
||||||
public CommentsFilter() {
|
public CommentsFilter() {
|
||||||
var chatSummary = new StringFilterGroup(
|
var chatSummary = new StringFilterGroup(
|
||||||
Settings.HIDE_COMMENTS_CHAT_SUMMARY,
|
Settings.HIDE_COMMENTS_AI_CHAT_SUMMARY,
|
||||||
"live_chat_summary_banner.eml"
|
"live_chat_summary_banner.eml"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -58,6 +60,16 @@ final class CommentsFilter extends Filter {
|
|||||||
"id.comment.quick_emoji.button"
|
"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(
|
addPathCallbacks(
|
||||||
chatSummary,
|
chatSummary,
|
||||||
commentsByMembers,
|
commentsByMembers,
|
||||||
@@ -65,7 +77,8 @@ final class CommentsFilter extends Filter {
|
|||||||
createAShort,
|
createAShort,
|
||||||
previewComment,
|
previewComment,
|
||||||
thanksButton,
|
thanksButton,
|
||||||
commentComposer
|
commentComposer,
|
||||||
|
filterChipBar
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +97,13 @@ final class CommentsFilter extends Filter {
|
|||||||
return false;
|
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);
|
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ final class DescriptionComponentsFilter extends Filter {
|
|||||||
"metadata"
|
"metadata"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final StringFilterGroup aiGeneratedVideoSummarySection = new StringFilterGroup(
|
||||||
|
Settings.HIDE_AI_GENERATED_VIDEO_SUMMARY_SECTION,
|
||||||
|
"cell_expandable_metadata.eml"
|
||||||
|
);
|
||||||
|
|
||||||
final StringFilterGroup attributesSection = new StringFilterGroup(
|
final StringFilterGroup attributesSection = new StringFilterGroup(
|
||||||
Settings.HIDE_ATTRIBUTES_SECTION,
|
Settings.HIDE_ATTRIBUTES_SECTION,
|
||||||
"gaming_section",
|
"gaming_section",
|
||||||
@@ -67,6 +72,7 @@ final class DescriptionComponentsFilter extends Filter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
addPathCallbacks(
|
addPathCallbacks(
|
||||||
|
aiGeneratedVideoSummarySection,
|
||||||
attributesSection,
|
attributesSection,
|
||||||
infoCardsSection,
|
infoCardsSection,
|
||||||
howThisWasMadeSection,
|
howThisWasMadeSection,
|
||||||
|
|||||||
@@ -98,6 +98,11 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
"compact_banner"
|
"compact_banner"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final var subscriptionsChipBar = new StringFilterGroup(
|
||||||
|
Settings.HIDE_FILTER_BAR_FEED_IN_FEED,
|
||||||
|
"subscriptions_chip_bar"
|
||||||
|
);
|
||||||
|
|
||||||
inFeedSurvey = new StringFilterGroup(
|
inFeedSurvey = new StringFilterGroup(
|
||||||
Settings.HIDE_FEED_SURVEY,
|
Settings.HIDE_FEED_SURVEY,
|
||||||
"in_feed_survey",
|
"in_feed_survey",
|
||||||
@@ -264,6 +269,7 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
singleItemInformationPanel,
|
singleItemInformationPanel,
|
||||||
emergencyBox,
|
emergencyBox,
|
||||||
subscribersCommunityGuidelines,
|
subscribersCommunityGuidelines,
|
||||||
|
subscriptionsChipBar,
|
||||||
channelGuidelines,
|
channelGuidelines,
|
||||||
audioTrackButton,
|
audioTrackButton,
|
||||||
artistCard,
|
artistCard,
|
||||||
@@ -448,14 +454,24 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hideShelves() {
|
private static boolean hideShelves() {
|
||||||
// If the player is opened while library is selected,
|
// Horizontal shelves are used for music/game links in video descriptions,
|
||||||
// then filter any recommendations below the player.
|
// such as https://youtube.com/watch?v=W8kI1na3S2M
|
||||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()
|
if (PlayerType.getCurrent().isMaximizedOrFullscreen()) {
|
||||||
// Or if the search is active while library is selected, then also filter.
|
return false;
|
||||||
|| NavigationBar.isSearchBarActive()) {
|
}
|
||||||
|
|
||||||
|
// Must check search bar after player type, since search results
|
||||||
|
// can be in the background behind an open player.
|
||||||
|
if (NavigationBar.isSearchBarActive()) {
|
||||||
return true;
|
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.
|
// Check navigation button last.
|
||||||
// Only filter if the library tab is not selected.
|
// Only filter if the library tab is not selected.
|
||||||
// This check is important as the shelf layout is used for the library tab playlists.
|
// This check is important as the shelf layout is used for the library tab playlists.
|
||||||
|
|||||||
@@ -2,12 +2,25 @@ package app.revanced.extension.youtube.patches.components;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
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.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class PlayerFlyoutMenuItemsFilter extends Filter {
|
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 ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList();
|
||||||
|
|
||||||
private final ByteArrayFilterGroup exception;
|
private final ByteArrayFilterGroup exception;
|
||||||
|
|||||||
@@ -8,30 +8,30 @@ import android.widget.ListView;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.patches.components.VideoQualityMenuFilterPatch;
|
import app.revanced.extension.youtube.patches.components.AdvancedVideoQualityMenuFilter;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This patch contains the logic to show the old video quality menu.
|
* This patch contains the logic to always open the advanced video quality menu.
|
||||||
* Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
|
* Two methods are required, because the quality menu is a RecyclerView in the new YouTube version
|
||||||
* and a ListView in the old one.
|
* and a ListView in the old one.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class RestoreOldVideoQualityMenuPatch {
|
public final class AdvancedVideoQualityMenuPatch {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
|
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
|
||||||
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return;
|
if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
|
||||||
|
|
||||||
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
|
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
|
||||||
try {
|
try {
|
||||||
// Check if the current view is the quality menu.
|
// Check if the current view is the quality menu.
|
||||||
if (!VideoQualityMenuFilterPatch.isVideoQualityMenuVisible || recyclerView.getChildCount() == 0) {
|
if (!AdvancedVideoQualityMenuFilter.isVideoQualityMenuVisible || recyclerView.getChildCount() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
VideoQualityMenuFilterPatch.isVideoQualityMenuVisible = false;
|
AdvancedVideoQualityMenuFilter.isVideoQualityMenuVisible = false;
|
||||||
|
|
||||||
ViewParent quickQualityViewParent = Utils.getParentView(recyclerView, 3);
|
ViewParent quickQualityViewParent = Utils.getParentView(recyclerView, 3);
|
||||||
if (!(quickQualityViewParent instanceof ViewGroup)) {
|
if (!(quickQualityViewParent instanceof ViewGroup)) {
|
||||||
@@ -39,16 +39,15 @@ public final class RestoreOldVideoQualityMenuPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
View firstChild = recyclerView.getChildAt(0);
|
View firstChild = recyclerView.getChildAt(0);
|
||||||
if (!(firstChild instanceof ViewGroup)) {
|
if (!(firstChild instanceof ViewGroup firstChildGroup)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewGroup advancedQualityParentView = (ViewGroup) firstChild;
|
if (firstChildGroup.getChildCount() < 4) {
|
||||||
if (advancedQualityParentView.getChildCount() < 4) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
View advancedQualityView = advancedQualityParentView.getChildAt(3);
|
View advancedQualityView = firstChildGroup.getChildAt(3);
|
||||||
if (advancedQualityView == null) {
|
if (advancedQualityView == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -71,7 +70,7 @@ public final class RestoreOldVideoQualityMenuPatch {
|
|||||||
* Used to force the creation of the advanced menu item for the Shorts quality flyout.
|
* Used to force the creation of the advanced menu item for the Shorts quality flyout.
|
||||||
*/
|
*/
|
||||||
public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) {
|
public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) {
|
||||||
return Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get() || original;
|
return Settings.ADVANCED_VIDEO_QUALITY_MENU.get() || original;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,8 +78,8 @@ public final class RestoreOldVideoQualityMenuPatch {
|
|||||||
*
|
*
|
||||||
* Used if spoofing to an old app version, and also used for the Shorts video quality flyout.
|
* Used if spoofing to an old app version, and also used for the Shorts video quality flyout.
|
||||||
*/
|
*/
|
||||||
public static void showOldVideoQualityMenu(final ListView listView) {
|
public static void showAdvancedVideoQualityMenu(ListView listView) {
|
||||||
if (!Settings.RESTORE_OLD_VIDEO_QUALITY_MENU.get()) return;
|
if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
|
||||||
|
|
||||||
listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
|
listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -12,15 +12,19 @@ import java.util.List;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
import app.revanced.extension.shared.settings.IntegerSetting;
|
import app.revanced.extension.shared.settings.IntegerSetting;
|
||||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class RememberVideoQualityPatch {
|
public class RememberVideoQualityPatch {
|
||||||
private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
|
private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
|
||||||
private static final IntegerSetting wifiQualitySetting = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
|
private static final IntegerSetting videoQualityWifi = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
|
||||||
private static final IntegerSetting mobileQualitySetting = Settings.VIDEO_QUALITY_DEFAULT_MOBILE;
|
private static final IntegerSetting videoQualityMobile = Settings.VIDEO_QUALITY_DEFAULT_MOBILE;
|
||||||
|
private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI;
|
||||||
|
private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE;
|
||||||
|
|
||||||
private static boolean qualityNeedsUpdating;
|
private static boolean qualityNeedsUpdating;
|
||||||
|
|
||||||
@@ -41,17 +45,29 @@ public class RememberVideoQualityPatch {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static List<Integer> videoQualities;
|
private static List<Integer> videoQualities;
|
||||||
|
|
||||||
|
private static boolean shouldRememberVideoQuality() {
|
||||||
|
BooleanSetting preference = ShortsPlayerState.isOpen() ?
|
||||||
|
Settings.REMEMBER_SHORTS_QUALITY_LAST_SELECTED
|
||||||
|
: Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED;
|
||||||
|
return preference.get();
|
||||||
|
}
|
||||||
|
|
||||||
private static void changeDefaultQuality(int defaultQuality) {
|
private static void changeDefaultQuality(int defaultQuality) {
|
||||||
String networkTypeMessage;
|
String networkTypeMessage;
|
||||||
|
boolean useShortsPreference = ShortsPlayerState.isOpen();
|
||||||
if (Utils.getNetworkType() == NetworkType.MOBILE) {
|
if (Utils.getNetworkType() == NetworkType.MOBILE) {
|
||||||
mobileQualitySetting.save(defaultQuality);
|
if (useShortsPreference) shortsQualityMobile.save(defaultQuality);
|
||||||
|
else videoQualityMobile.save(defaultQuality);
|
||||||
networkTypeMessage = str("revanced_remember_video_quality_mobile");
|
networkTypeMessage = str("revanced_remember_video_quality_mobile");
|
||||||
} else {
|
} else {
|
||||||
wifiQualitySetting.save(defaultQuality);
|
if (useShortsPreference) shortsQualityWifi.save(defaultQuality);
|
||||||
|
else videoQualityWifi.save(defaultQuality);
|
||||||
networkTypeMessage = str("revanced_remember_video_quality_wifi");
|
networkTypeMessage = str("revanced_remember_video_quality_wifi");
|
||||||
}
|
}
|
||||||
Utils.showToastShort(
|
Utils.showToastShort(str(
|
||||||
str("revanced_remember_video_quality_toast", networkTypeMessage, (defaultQuality + "p")));
|
useShortsPreference ? "revanced_remember_video_quality_toast_shorts" : "revanced_remember_video_quality_toast",
|
||||||
|
networkTypeMessage, (defaultQuality + "p")
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,9 +78,10 @@ public class RememberVideoQualityPatch {
|
|||||||
*/
|
*/
|
||||||
public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) {
|
public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) {
|
||||||
try {
|
try {
|
||||||
|
boolean useShortsPreference = ShortsPlayerState.isOpen();
|
||||||
final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE
|
final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE
|
||||||
? mobileQualitySetting.get()
|
? (useShortsPreference ? shortsQualityMobile : videoQualityMobile).get()
|
||||||
: wifiQualitySetting.get();
|
: (useShortsPreference ? shortsQualityWifi : videoQualityWifi).get();
|
||||||
|
|
||||||
if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
|
if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
|
||||||
return originalQualityIndex; // Nothing to do.
|
return originalQualityIndex; // Nothing to do.
|
||||||
@@ -141,17 +158,17 @@ public class RememberVideoQualityPatch {
|
|||||||
* Injection point. Old quality menu.
|
* Injection point. Old quality menu.
|
||||||
*/
|
*/
|
||||||
public static void userChangedQuality(int selectedQualityIndex) {
|
public static void userChangedQuality(int selectedQualityIndex) {
|
||||||
if (!Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED.get()) return;
|
if (shouldRememberVideoQuality()) {
|
||||||
|
userSelectedQualityIndex = selectedQualityIndex;
|
||||||
userSelectedQualityIndex = selectedQualityIndex;
|
userChangedDefaultQuality = true;
|
||||||
userChangedDefaultQuality = true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point. New quality menu.
|
* Injection point. New quality menu.
|
||||||
*/
|
*/
|
||||||
public static void userChangedQualityInNewFlyout(int selectedQuality) {
|
public static void userChangedQualityInNewFlyout(int selectedQuality) {
|
||||||
if (!Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED.get()) return;
|
if (!shouldRememberVideoQuality()) return;
|
||||||
|
|
||||||
changeDefaultQuality(selectedQuality); // Quality is human readable resolution (ie: 1080).
|
changeDefaultQuality(selectedQuality); // Quality is human readable resolution (ie: 1080).
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ public final class SeekbarColorPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String loadRawResourceAsString(int resourceId) {
|
private static String loadRawResourceAsString(int resourceId) {
|
||||||
|
//noinspection CharsetObjectCanBeUsed
|
||||||
try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId);
|
try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId);
|
||||||
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
|
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
|
||||||
return scanner.next();
|
return scanner.next();
|
||||||
@@ -281,6 +282,20 @@ public final class SeekbarColorPatch {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* 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) {
|
public static int[] getPlayerLinearGradient(int[] original) {
|
||||||
return SEEKBAR_CUSTOM_COLOR_ENABLED
|
return SEEKBAR_CUSTOM_COLOR_ENABLED
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import java.util.concurrent.*;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
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.RYDVoteData;
|
||||||
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
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 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.
|
* Cached lookup of all video ids.
|
||||||
*/
|
*/
|
||||||
@@ -118,7 +114,7 @@ public class ReturnYouTubeDislike {
|
|||||||
private static final Rect middleSeparatorBounds;
|
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;
|
public static final int leftSeparatorShapePaddingPixels;
|
||||||
private static final ShapeDrawable leftSeparatorShape;
|
private static final ShapeDrawable leftSeparatorShape;
|
||||||
@@ -133,7 +129,7 @@ public class ReturnYouTubeDislike {
|
|||||||
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3.7f, dp);
|
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3.7f, dp);
|
||||||
middleSeparatorBounds = new Rect(0, 0, middleSeparatorSize, middleSeparatorSize);
|
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 = new ShapeDrawable(new RectShape());
|
||||||
leftSeparatorShape.setBounds(leftSeparatorBounds);
|
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.
|
* 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.
|
* 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.
|
* 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() {
|
private static int getSeparatorColor() {
|
||||||
if (IS_SPOOFING_TO_OLD_SEPARATOR_COLOR) {
|
|
||||||
return ThemeHelper.isDarkTheme()
|
|
||||||
? 0x29AAAAAA // transparent dark gray
|
|
||||||
: 0xFFD9D9D9; // light gray
|
|
||||||
}
|
|
||||||
return ThemeHelper.isDarkTheme()
|
return ThemeHelper.isDarkTheme()
|
||||||
? 0x33FFFFFF
|
? 0x33FFFFFF
|
||||||
: 0xFFD9D9D9;
|
: 0xFFD9D9D9;
|
||||||
@@ -251,7 +238,7 @@ public class ReturnYouTubeDislike {
|
|||||||
String leftSeparatorString = getTextDirectionString();
|
String leftSeparatorString = getTextDirectionString();
|
||||||
final Spannable leftSeparatorSpan;
|
final Spannable leftSeparatorSpan;
|
||||||
if (isRollingNumber) {
|
if (isRollingNumber) {
|
||||||
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
||||||
} else {
|
} else {
|
||||||
leftSeparatorString += " ";
|
leftSeparatorString += " ";
|
||||||
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
leftSeparatorSpan = new SpannableString(leftSeparatorString);
|
||||||
@@ -352,13 +339,16 @@ public class ReturnYouTubeDislike {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String formatDislikeCount(long dislikeCount) {
|
private static String formatDislikeCount(long dislikeCount) {
|
||||||
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
|
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe.
|
||||||
if (dislikeCountFormatter == null) {
|
if (dislikeCountFormatter == null) {
|
||||||
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
|
// Must use default locale and not Utils context locale,
|
||||||
|
// otherwise if using a different settings language then the
|
||||||
|
// formatting will use that of the different language.
|
||||||
|
Locale locale = Locale.getDefault();
|
||||||
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
|
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
|
||||||
|
|
||||||
// YouTube disregards locale specific number characters
|
// YouTube disregards locale specific number characters
|
||||||
// and instead shows english number characters everywhere.
|
// and instead shows English number characters everywhere.
|
||||||
// To use the same behavior, override the digit characters to use English
|
// To use the same behavior, override the digit characters to use English
|
||||||
// so languages such as Arabic will show "1.234" instead of the native "۱,۲۳٤"
|
// so languages such as Arabic will show "1.234" instead of the native "۱,۲۳٤"
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
@@ -375,15 +365,15 @@ public class ReturnYouTubeDislike {
|
|||||||
private static String formatDislikePercentage(float dislikePercentage) {
|
private static String formatDislikePercentage(float dislikePercentage) {
|
||||||
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe, must synchronize.
|
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe, must synchronize.
|
||||||
if (dislikePercentageFormatter == null) {
|
if (dislikePercentageFormatter == null) {
|
||||||
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
|
Locale locale = Locale.getDefault();
|
||||||
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
|
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
|
||||||
|
|
||||||
// Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns.
|
// Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns.
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||||
&& dislikePercentageFormatter instanceof DecimalFormat) {
|
&& dislikePercentageFormatter instanceof DecimalFormat decimalFormatter) {
|
||||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||||
((DecimalFormat) dislikePercentageFormatter).setDecimalFormatSymbols(symbols);
|
decimalFormatter.setDecimalFormatSymbols(symbols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -633,7 +623,7 @@ public class ReturnYouTubeDislike {
|
|||||||
userVote = vote;
|
userVote = vote;
|
||||||
clearUICache();
|
clearUICache();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
// Update the fetched vote data.
|
// Update the fetched vote data.
|
||||||
RYDVoteData voteData = getFetchData(MAX_MILLISECONDS_TO_BLOCK_UI_WAITING_FOR_FETCH);
|
RYDVoteData voteData = getFetchData(MAX_MILLISECONDS_TO_BLOCK_UI_WAITING_FOR_FETCH);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import app.revanced.extension.shared.settings.AppLanguage;
|
|||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.youtube.ThemeHelper;
|
import app.revanced.extension.youtube.ThemeHelper;
|
||||||
import app.revanced.extension.youtube.patches.VersionCheckPatch;
|
import app.revanced.extension.youtube.patches.VersionCheckPatch;
|
||||||
|
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
|
||||||
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
|
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
|
||||||
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
|
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
|
||||||
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
|
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
|
||||||
@@ -63,6 +64,10 @@ public class LicenseActivityHook {
|
|||||||
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
|
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Spoofing can cause half broken settings menus of old and new settings.
|
||||||
|
if (SpoofAppVersionPatch.isSpoofingToLessThan("19.35.36")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// On the first launch of a clean install, forcing the cairo menu can give a
|
// On the first launch of a clean install, forcing the cairo menu can give a
|
||||||
// half broken appearance because all the preference icons may not be available yet.
|
// half broken appearance because all the preference icons may not be available yet.
|
||||||
@@ -121,8 +126,7 @@ public class LicenseActivityHook {
|
|||||||
// This is required to fix submenu title alignment issue with Android ASOP 15+
|
// This is required to fix submenu title alignment issue with Android ASOP 15+
|
||||||
ViewGroup toolBarParent = activity.findViewById(
|
ViewGroup toolBarParent = activity.findViewById(
|
||||||
getResourceIdentifier("revanced_toolbar_parent", "id"));
|
getResourceIdentifier("revanced_toolbar_parent", "id"));
|
||||||
ViewGroup dummyToolbar = toolBarParent.findViewById(getResourceIdentifier(
|
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
|
||||||
"revanced_toolbar", "id"));
|
|
||||||
toolbarLayoutParams = dummyToolbar.getLayoutParams();
|
toolbarLayoutParams = dummyToolbar.getLayoutParams();
|
||||||
toolBarParent.removeView(dummyToolbar);
|
toolBarParent.removeView(dummyToolbar);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package app.revanced.extension.youtube.settings;
|
|||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static app.revanced.extension.shared.settings.Setting.Availability;
|
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.migrateOldSettingToNew;
|
||||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||||
import static app.revanced.extension.shared.settings.Setting.parentsAny;
|
import static app.revanced.extension.shared.settings.Setting.parentsAny;
|
||||||
@@ -11,7 +10,6 @@ import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormF
|
|||||||
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
||||||
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
|
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
|
||||||
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
|
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
|
||||||
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.MiniplayerHorizontalDragAvailability;
|
||||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
|
||||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MINIMAL;
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MINIMAL;
|
||||||
@@ -21,7 +19,7 @@ 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.MiniplayerPatch.MiniplayerType.MODERN_4;
|
||||||
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
|
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.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.IGNORE;
|
||||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
|
||||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
|
||||||
@@ -38,20 +36,23 @@ import app.revanced.extension.shared.settings.IntegerSetting;
|
|||||||
import app.revanced.extension.shared.settings.LongSetting;
|
import app.revanced.extension.shared.settings.LongSetting;
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
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.DeArrowAvailability;
|
||||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
|
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
|
||||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
|
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
|
||||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
|
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
|
||||||
|
import app.revanced.extension.youtube.patches.MiniplayerPatch;
|
||||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||||
|
|
||||||
public class Settings extends BaseSettings {
|
public class Settings extends BaseSettings {
|
||||||
// Video
|
// Video
|
||||||
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
|
|
||||||
public static final BooleanSetting RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
|
|
||||||
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
|
|
||||||
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
|
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
|
||||||
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
|
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
|
||||||
|
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
|
||||||
|
public static final IntegerSetting SHORTS_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_shorts_quality_default_wifi", -2, true);
|
||||||
|
public static final IntegerSetting SHORTS_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_shorts_quality_default_mobile", -2, true);
|
||||||
|
public static final BooleanSetting REMEMBER_SHORTS_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_shorts_quality_last_selected", FALSE);
|
||||||
|
public static final BooleanSetting ADVANCED_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_advanced_video_quality_menu", TRUE);
|
||||||
|
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
|
||||||
// Speed
|
// Speed
|
||||||
public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true);
|
public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true);
|
||||||
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
|
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
|
||||||
@@ -125,7 +126,6 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
||||||
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, 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 BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
||||||
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
|
|
||||||
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
|
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
|
||||||
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
|
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
|
||||||
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
|
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
|
||||||
@@ -135,6 +135,7 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE);
|
public static final BooleanSetting HIDE_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_community_guidelines", TRUE);
|
||||||
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
|
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_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_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", 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_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
|
||||||
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
|
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
|
||||||
@@ -156,7 +157,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_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_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_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_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_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);
|
public static final BooleanSetting MINIPLAYER_ROUNDED_CORNERS = new BooleanSetting("revanced_miniplayer_rounded_corners", TRUE, true, MINIPLAYER_ANY_MODERN);
|
||||||
@@ -168,14 +169,16 @@ public class Settings extends BaseSettings {
|
|||||||
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name",
|
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));
|
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
|
||||||
// Comments
|
// 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_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_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);
|
||||||
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
|
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
|
||||||
public static final BooleanSetting HIDE_COMMENTS_SECTION = new BooleanSetting("revanced_hide_comments_section", FALSE);
|
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);
|
public static final BooleanSetting HIDE_COMMENTS_THANKS_BUTTON = new BooleanSetting("revanced_hide_comments_thanks_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_COMMENTS_TIMESTAMP_AND_EMOJI_BUTTONS = new BooleanSetting("revanced_hide_comments_timestamp_and_emoji_buttons", TRUE);
|
|
||||||
// Description
|
// 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_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_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);
|
public static final BooleanSetting HIDE_HOW_THIS_WAS_MADE_SECTION = new BooleanSetting("revanced_hide_how_this_was_made_section", FALSE);
|
||||||
@@ -196,7 +199,7 @@ public class Settings extends BaseSettings {
|
|||||||
// Player flyout menu items
|
// Player flyout menu items
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
||||||
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_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_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);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
||||||
@@ -218,7 +221,7 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
||||||
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
||||||
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
|
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
|
||||||
public static final 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 StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
|
||||||
// Custom filter
|
// Custom filter
|
||||||
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
||||||
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
|
||||||
@@ -228,8 +231,11 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting HIDE_SHORTS_BUTTON = new BooleanSetting("revanced_hide_shorts_button", TRUE, true);
|
public static final BooleanSetting HIDE_SHORTS_BUTTON = new BooleanSetting("revanced_hide_shorts_button", TRUE, true);
|
||||||
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
|
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
|
||||||
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
|
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
|
||||||
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
|
public static final BooleanSetting HIDE_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_hide_notifications_button", FALSE, true);
|
||||||
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", 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,
|
||||||
|
"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_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);
|
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true);
|
||||||
|
|
||||||
@@ -282,7 +288,6 @@ public class Settings extends BaseSettings {
|
|||||||
"revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability());
|
"revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability());
|
||||||
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
|
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
|
||||||
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
|
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
|
||||||
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
|
|
||||||
public static final StringSetting SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_primary", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
|
public static final StringSetting SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_primary", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
|
||||||
public static final StringSetting SEEKBAR_CUSTOM_COLOR_ACCENT = new StringSetting("revanced_seekbar_custom_color_accent", "#FF2791", true, parent(SEEKBAR_CUSTOM_COLOR));
|
public static final StringSetting SEEKBAR_CUSTOM_COLOR_ACCENT = new StringSetting("revanced_seekbar_custom_color_accent", "#FF2791", true, parent(SEEKBAR_CUSTOM_COLOR));
|
||||||
|
|
||||||
@@ -314,13 +319,13 @@ public class Settings extends BaseSettings {
|
|||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
|
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
|
||||||
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
|
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
|
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
|
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
|
|
||||||
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
|
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
|
||||||
@@ -362,52 +367,53 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting SB_SEEN_GUIDELINES = new BooleanSetting("sb_seen_guidelines", FALSE, false, false);
|
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 = 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 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 = 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 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 = new StringSetting("sb_interaction", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_INTERACTION_COLOR = new StringSetting("sb_interaction_color", "#CC00FF");
|
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 = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684");
|
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 = new StringSetting("sb_intro", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF");
|
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 = new StringSetting("sb_outro", MANUAL_SKIP.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_OUTRO_COLOR = new StringSetting("sb_outro_color", "#0202ED");
|
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 = new StringSetting("sb_preview", IGNORE.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_PREVIEW_COLOR = new StringSetting("sb_preview_color", "#008FD6");
|
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 = new StringSetting("sb_filler", IGNORE.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_FILLER_COLOR = new StringSetting("sb_filler_color", "#7300FF");
|
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 = 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 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 = new StringSetting("sb_unsubmitted", SKIP_AUTOMATICALLY.reVancedKeyValue);
|
||||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFF");
|
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
|
// Deprecated migrations
|
||||||
public 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_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 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);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// region Migration
|
// 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_BUTTONS, HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS);
|
||||||
|
|
||||||
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
|
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
|
||||||
|
|
||||||
// Old spoof versions that no longer work reliably.
|
migrateOldSettingToNew(DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN, HIDE_END_SCREEN_SUGGESTED_VIDEO);
|
||||||
if (SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0) {
|
|
||||||
Logger.printInfo(() -> "Resetting spoof app version target");
|
migrateOldSettingToNew(DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU, ADVANCED_VIDEO_QUALITY_MENU);
|
||||||
SPOOF_APP_VERSION_TARGET.resetToDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate renamed enum.
|
// Migrate renamed enum.
|
||||||
//noinspection deprecation
|
//noinspection deprecation
|
||||||
@@ -444,6 +450,12 @@ public class Settings extends BaseSettings {
|
|||||||
DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.resetToDefault();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region SB import/export callbacks
|
// region SB import/export callbacks
|
||||||
|
|||||||
@@ -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 android.widget.Toolbar;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
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[] sortedEntries = new CharSequence[entrySize];
|
||||||
CharSequence[] sortedEntryValues = new CharSequence[entrySize];
|
CharSequence[] sortedEntryValues = new CharSequence[entrySize];
|
||||||
@@ -109,6 +109,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
|
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sortPreferenceListMenu(Settings.CHANGE_START_PAGE);
|
||||||
sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE);
|
sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE);
|
||||||
sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE);
|
sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@@ -137,11 +138,13 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
.findViewById(android.R.id.content)
|
.findViewById(android.R.id.content)
|
||||||
.getParent();
|
.getParent();
|
||||||
|
|
||||||
// Fix required for Android 15 and YT 19.45+
|
// Fix edge-to-edge screen with Android 15 and YT 19.45+
|
||||||
|
// https://developer.android.com/develop/ui/views/layout/edge-to-edge#system-bars-insets
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
rootView.setOnApplyWindowInsetsListener((v, insets) -> {
|
rootView.setOnApplyWindowInsetsListener((v, insets) -> {
|
||||||
Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars());
|
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;
|
return insets;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import android.preference.PreferenceScreen;
|
|||||||
import android.preference.SwitchPreference;
|
import android.preference.SwitchPreference;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
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.Setting;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch;
|
import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch;
|
||||||
@@ -85,9 +86,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
|||||||
shortsPreference = new SwitchPreference(context);
|
shortsPreference = new SwitchPreference(context);
|
||||||
shortsPreference.setChecked(Settings.RYD_SHORTS.get());
|
shortsPreference.setChecked(Settings.RYD_SHORTS.get());
|
||||||
shortsPreference.setTitle(str("revanced_ryd_shorts_title"));
|
shortsPreference.setTitle(str("revanced_ryd_shorts_title"));
|
||||||
String shortsSummary = ReturnYouTubeDislikePatch.IS_SPOOFING_TO_NON_LITHO_SHORTS_PLAYER
|
String shortsSummary = str("revanced_ryd_shorts_summary_on_disclaimer");
|
||||||
? str("revanced_ryd_shorts_summary_on")
|
|
||||||
: str("revanced_ryd_shorts_summary_on_disclaimer");
|
|
||||||
shortsPreference.setSummaryOn(shortsSummary);
|
shortsPreference.setSummaryOn(shortsSummary);
|
||||||
shortsPreference.setSummaryOff(str("revanced_ryd_shorts_summary_off"));
|
shortsPreference.setSummaryOff(str("revanced_ryd_shorts_summary_off"));
|
||||||
shortsPreference.setOnPreferenceChangeListener((pref, newValue) -> {
|
shortsPreference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||||
@@ -237,6 +236,8 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
|||||||
"revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_non_zero_summary"));
|
"revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_non_zero_summary"));
|
||||||
preferenceScreen.addPreference(statisticPreference);
|
preferenceScreen.addPreference(statisticPreference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Utils.setPreferenceTitlesToMultiLineIfNeeded(preferenceScreen);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onCreate failure", 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.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
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.settings.Settings;
|
||||||
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
|
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
|
||||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||||
@@ -44,8 +45,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
|||||||
private SwitchPreference showTimeWithoutSegments;
|
private SwitchPreference showTimeWithoutSegments;
|
||||||
private SwitchPreference toastOnConnectionError;
|
private SwitchPreference toastOnConnectionError;
|
||||||
|
|
||||||
private EditTextPreference newSegmentStep;
|
private ResettableEditTextPreference newSegmentStep;
|
||||||
private EditTextPreference minSegmentDuration;
|
private ResettableEditTextPreference minSegmentDuration;
|
||||||
private EditTextPreference privateUserId;
|
private EditTextPreference privateUserId;
|
||||||
private EditTextPreference importExport;
|
private EditTextPreference importExport;
|
||||||
private Preference apiUrl;
|
private Preference apiUrl;
|
||||||
@@ -159,6 +160,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
|||||||
|
|
||||||
addAboutCategory(context, preferenceScreen);
|
addAboutCategory(context, preferenceScreen);
|
||||||
|
|
||||||
|
Utils.setPreferenceTitlesToMultiLineIfNeeded(preferenceScreen);
|
||||||
|
|
||||||
updateUI();
|
updateUI();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onCreate failure", ex);
|
Logger.printException(() -> "onCreate failure", ex);
|
||||||
@@ -268,7 +271,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
|||||||
return true;
|
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.setTitle(str("revanced_sb_general_adjusting"));
|
||||||
newSegmentStep.setSummary(str("revanced_sb_general_adjusting_sum"));
|
newSegmentStep.setSummary(str("revanced_sb_general_adjusting_sum"));
|
||||||
newSegmentStep.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
|
newSegmentStep.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
@@ -326,7 +330,8 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
|||||||
});
|
});
|
||||||
category.addPreference(trackSkips);
|
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.setTitle(str("revanced_sb_general_min_duration"));
|
||||||
minSegmentDuration.setSummary(str("revanced_sb_general_min_duration_sum"));
|
minSegmentDuration.setSummary(str("revanced_sb_general_min_duration_sum"));
|
||||||
minSegmentDuration.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
minSegmentDuration.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
||||||
@@ -345,7 +350,15 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
|||||||
});
|
});
|
||||||
category.addPreference(minSegmentDuration);
|
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.setTitle(str("revanced_sb_general_uuid"));
|
||||||
privateUserId.setSummary(str("revanced_sb_general_uuid_sum"));
|
privateUserId.setSummary(str("revanced_sb_general_uuid_sum"));
|
||||||
privateUserId.setOnPreferenceChangeListener((preference1, newValue) -> {
|
privateUserId.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||||
@@ -504,7 +517,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
|||||||
|
|
||||||
if (stats.totalSegmentCountIncludingIgnored > 0) {
|
if (stats.totalSegmentCountIncludingIgnored > 0) {
|
||||||
// If user has not created any segments, there's no reason to set a username.
|
// 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);
|
statsCategory.addPreference(preference);
|
||||||
String userName = stats.userName;
|
String userName = stats.userName;
|
||||||
preference.setTitle(fromHtml(str("revanced_sb_stats_username", userName)));
|
preference.setTitle(fromHtml(str("revanced_sb_stats_username", userName)));
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
|||||||
if (currentClientType == clientType) {
|
if (currentClientType == clientType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
currentClientType = clientType;
|
||||||
|
|
||||||
Logger.printDebug(() -> "Updating spoof stream side effects preference");
|
Logger.printDebug(() -> "Updating spoof stream side effects preference");
|
||||||
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
|
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package app.revanced.extension.youtube.shared;
|
|||||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
|
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@@ -24,12 +26,22 @@ import app.revanced.extension.youtube.settings.Settings;
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class NavigationBar {
|
public final class NavigationBar {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to call obfuscated methods in AppCompat Toolbar class.
|
||||||
|
*/
|
||||||
|
public interface AppCompatToolbarPatchInterface {
|
||||||
|
Drawable patch_getNavigationIcon();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Search bar
|
// Search and toolbar.
|
||||||
//
|
//
|
||||||
|
|
||||||
private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null);
|
private static volatile WeakReference<View> searchBarResultsRef = new WeakReference<>(null);
|
||||||
|
|
||||||
|
private static volatile WeakReference<AppCompatToolbarPatchInterface> toolbarResultsRef
|
||||||
|
= new WeakReference<>(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
@@ -37,6 +49,22 @@ public final class NavigationBar {
|
|||||||
searchBarResultsRef = new WeakReference<>(searchbarResults);
|
searchBarResultsRef = new WeakReference<>(searchbarResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void setToolbar(FrameLayout layout) {
|
||||||
|
AppCompatToolbarPatchInterface toolbar = Utils.getChildView(layout, false, (view) ->
|
||||||
|
view instanceof AppCompatToolbarPatchInterface
|
||||||
|
);
|
||||||
|
|
||||||
|
if (toolbar == null) {
|
||||||
|
Logger.printException(() -> "Could not find navigation toolbar");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbarResultsRef = new WeakReference<>(toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return If the search bar is on screen. This includes if the player
|
* @return If the search bar is on screen. This includes if the player
|
||||||
* is on screen and the search results are behind the player (and not visible).
|
* is on screen and the search results are behind the player (and not visible).
|
||||||
@@ -47,8 +75,13 @@ public final class NavigationBar {
|
|||||||
return searchbarResults != null && searchbarResults.getParent() != null;
|
return searchbarResults != null && searchbarResults.getParent() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isBackButtonVisible() {
|
||||||
|
AppCompatToolbarPatchInterface toolbar = toolbarResultsRef.get();
|
||||||
|
return toolbar != null && toolbar.patch_getNavigationIcon() != null;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Navigation bar buttons
|
// Navigation bar buttons.
|
||||||
//
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import app.revanced.extension.youtube.Event
|
|||||||
import app.revanced.extension.youtube.patches.VideoInformation
|
import app.revanced.extension.youtube.patches.VideoInformation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main player type.
|
* Regular player type.
|
||||||
*/
|
*/
|
||||||
enum class PlayerType {
|
enum class PlayerType {
|
||||||
/**
|
/**
|
||||||
@@ -21,9 +21,6 @@ enum class PlayerType {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A regular video is minimized.
|
* A regular video is minimized.
|
||||||
*
|
|
||||||
* When spoofing to 16.x YouTube and watching a short with a regular video in the background,
|
|
||||||
* the type can be this (and not [HIDDEN]).
|
|
||||||
*/
|
*/
|
||||||
WATCH_WHILE_MINIMIZED,
|
WATCH_WHILE_MINIMIZED,
|
||||||
WATCH_WHILE_MAXIMIZED,
|
WATCH_WHILE_MAXIMIZED,
|
||||||
@@ -56,8 +53,7 @@ enum class PlayerType {
|
|||||||
val newType = nameToPlayerType[enumName]
|
val newType = nameToPlayerType[enumName]
|
||||||
if (newType == null) {
|
if (newType == null) {
|
||||||
Logger.printException { "Unknown PlayerType encountered: $enumName" }
|
Logger.printException { "Unknown PlayerType encountered: $enumName" }
|
||||||
} else if (current != newType) {
|
} else {
|
||||||
Logger.printDebug { "PlayerType changed to: $newType" }
|
|
||||||
current = newType
|
current = newType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,9 +64,13 @@ enum class PlayerType {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
var current
|
var current
|
||||||
get() = currentPlayerType
|
get() = currentPlayerType
|
||||||
private set(value) {
|
private set(type) {
|
||||||
currentPlayerType = value
|
if (currentPlayerType != type) {
|
||||||
onChange(currentPlayerType)
|
Logger.printDebug { "Changed to: $type" }
|
||||||
|
|
||||||
|
currentPlayerType = type
|
||||||
|
onChange(type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Volatile // Read/write from different threads.
|
@Volatile // Read/write from different threads.
|
||||||
@@ -90,8 +90,6 @@ enum class PlayerType {
|
|||||||
* Does not include the first moment after a short is opened when a regular video is minimized on screen,
|
* Does not include the first moment after a short is opened when a regular video is minimized on screen,
|
||||||
* or while watching a short with a regular video present on a spoofed 16.x version of YouTube.
|
* or while watching a short with a regular video present on a spoofed 16.x version of YouTube.
|
||||||
* To include those situations instead use [isNoneHiddenOrMinimized].
|
* To include those situations instead use [isNoneHiddenOrMinimized].
|
||||||
*
|
|
||||||
* @see VideoInformation
|
|
||||||
*/
|
*/
|
||||||
fun isNoneOrHidden(): Boolean {
|
fun isNoneOrHidden(): Boolean {
|
||||||
return this == NONE || this == HIDDEN
|
return this == NONE || this == HIDDEN
|
||||||
@@ -107,8 +105,11 @@ enum class PlayerType {
|
|||||||
* when spoofing to an old version this will return false even
|
* when spoofing to an old version this will return false even
|
||||||
* though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]).
|
* though a Short is being opened or is on screen (see [isNoneHiddenOrMinimized]).
|
||||||
*
|
*
|
||||||
|
* Instead of this method, consider using {@link ShortsPlayerState}
|
||||||
|
* which may work better for some situations.
|
||||||
|
*
|
||||||
* @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state.
|
* @return If nothing, a Short, or a regular video is sliding off screen to a dismissed or hidden state.
|
||||||
* @see VideoInformation
|
* @see ShortsPlayerState
|
||||||
*/
|
*/
|
||||||
fun isNoneHiddenOrSlidingMinimized(): Boolean {
|
fun isNoneHiddenOrSlidingMinimized(): Boolean {
|
||||||
return isNoneOrHidden() || this == WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED
|
return isNoneOrHidden() || this == WATCH_WHILE_SLIDING_MINIMIZED_DISMISSED
|
||||||
@@ -125,9 +126,12 @@ enum class PlayerType {
|
|||||||
* Typically used to detect if a Short is playing when the player cannot be in a minimized state,
|
* Typically used to detect if a Short is playing when the player cannot be in a minimized state,
|
||||||
* such as the user interacting with a button or element of the player.
|
* such as the user interacting with a button or element of the player.
|
||||||
*
|
*
|
||||||
|
* Instead of this method, consider using {@link ShortsPlayerState}
|
||||||
|
* which may work better for some situations.
|
||||||
|
*
|
||||||
* @return If nothing, a Short, a regular video is sliding off screen to a dismissed or hidden state,
|
* @return If nothing, a Short, a regular video is sliding off screen to a dismissed or hidden state,
|
||||||
* a regular video is minimized (and a new video is not being opened).
|
* a regular video is minimized (and a new video is not being opened).
|
||||||
* @see VideoInformation
|
* @see ShortsPlayerState
|
||||||
*/
|
*/
|
||||||
fun isNoneHiddenOrMinimized(): Boolean {
|
fun isNoneHiddenOrMinimized(): Boolean {
|
||||||
return isNoneHiddenOrSlidingMinimized() || this == WATCH_WHILE_MINIMIZED
|
return isNoneHiddenOrSlidingMinimized() || this == WATCH_WHILE_MINIMIZED
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package app.revanced.extension.youtube.shared
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger
|
||||||
|
import app.revanced.extension.youtube.Event
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorts player state.
|
||||||
|
*/
|
||||||
|
class ShortsPlayerState {
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setOpen(open: Boolean) {
|
||||||
|
if (isOpen != open) {
|
||||||
|
Logger.printDebug { "ShortsPlayerState open changed to: $isOpen" }
|
||||||
|
isOpen = open
|
||||||
|
onChange(open)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var isOpen = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shorts player state change listener.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
val onChange = Event<Boolean>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the Shorts player is currently open.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun isOpen(): Boolean {
|
||||||
|
return isOpen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -136,7 +136,7 @@ public class SponsorBlockSettings {
|
|||||||
for (SegmentCategory category : categories) {
|
for (SegmentCategory category : categories) {
|
||||||
JSONObject categoryObject = new JSONObject();
|
JSONObject categoryObject = new JSONObject();
|
||||||
String categoryKey = category.keyValue;
|
String categoryKey = category.keyValue;
|
||||||
categoryObject.put("color", category.colorString());
|
categoryObject.put("color", category.getColorString());
|
||||||
barTypesObject.put(categoryKey, categoryObject);
|
barTypesObject.put(categoryKey, categoryObject);
|
||||||
|
|
||||||
if (category.behaviour != CategoryBehaviour.IGNORE) {
|
if (category.behaviour != CategoryBehaviour.IGNORE) {
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ import static app.revanced.extension.shared.StringRef.str;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
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 android.widget.EditText;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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.
|
* Not thread safe. All fields/methods must be accessed from the main thread.
|
||||||
*/
|
*/
|
||||||
public class SponsorBlockUtils {
|
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 String MANUAL_EDIT_TIME_TEXT_HINT = "hh:mm:ss.sss";
|
||||||
private static final Pattern manualEditTimePattern
|
private static final Pattern manualEditTimePattern
|
||||||
= Pattern.compile("((\\d{1,2}):)?(\\d{1,2}):(\\d{2})(\\.(\\d{1,3}))?");
|
= 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[] voteOptions = (segment.category == SegmentCategory.HIGHLIGHT)
|
||||||
? SegmentVote.voteTypesWithoutCategoryChange // highlight segments cannot change category
|
? SegmentVote.voteTypesWithoutCategoryChange // highlight segments cannot change category
|
||||||
: SegmentVote.values();
|
: 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];
|
SegmentVote voteOption = voteOptions[i];
|
||||||
String title = voteOption.title.toString();
|
CharSequence title = voteOption.title.toString();
|
||||||
if (Settings.SB_USER_IS_VIP.get() && segment.isLocked && voteOption.shouldHighlight) {
|
if (userIsVip && segment.isLocked && voteOption.highlightIfVipAndVideoIsLocked) {
|
||||||
items[i] = Html.fromHtml(String.format("<font color=\"%s\">%s</font>", LOCKED_COLOR, title));
|
SpannableString coloredTitle = new SpannableString(title);
|
||||||
} else {
|
coloredTitle.setSpan(new ForegroundColorSpan(LOCKED_COLOR),
|
||||||
items[i] = title;
|
0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
title = coloredTitle;
|
||||||
}
|
}
|
||||||
|
items[i] = title;
|
||||||
}
|
}
|
||||||
|
|
||||||
new AlertDialog.Builder(context)
|
new AlertDialog.Builder(context).setItems(items, (dialog1, which1) -> {
|
||||||
.setItems(items, (dialog1, which1) -> {
|
SegmentVote voteOption = voteOptions[which1];
|
||||||
SegmentVote voteOption = voteOptions[which1];
|
switch (voteOption) {
|
||||||
switch (voteOption) {
|
case UPVOTE:
|
||||||
case UPVOTE:
|
case DOWNVOTE:
|
||||||
case DOWNVOTE:
|
SBRequester.voteForSegmentOnBackgroundThread(segment, voteOption);
|
||||||
SBRequester.voteForSegmentOnBackgroundThread(segment, voteOption);
|
break;
|
||||||
break;
|
case CATEGORY_CHANGE:
|
||||||
case CATEGORY_CHANGE:
|
onNewCategorySelect(segment, context);
|
||||||
onNewCategorySelect(segment, context);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}).show();
|
||||||
})
|
|
||||||
.show();
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "segmentVoteClickListener failure", ex);
|
Logger.printException(() -> "segmentVoteClickListener failure", ex);
|
||||||
}
|
}
|
||||||
@@ -282,7 +289,6 @@ public class SponsorBlockUtils {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final int numberOfSegments = segments.length;
|
final int numberOfSegments = segments.length;
|
||||||
CharSequence[] titles = new CharSequence[numberOfSegments];
|
CharSequence[] titles = new CharSequence[numberOfSegments];
|
||||||
for (int i = 0; i < numberOfSegments; i++) {
|
for (int i = 0; i < numberOfSegments; i++) {
|
||||||
@@ -290,22 +296,33 @@ public class SponsorBlockUtils {
|
|||||||
if (segment.category == SegmentCategory.UNSUBMITTED) {
|
if (segment.category == SegmentCategory.UNSUBMITTED) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
StringBuilder htmlBuilder = new StringBuilder();
|
|
||||||
htmlBuilder.append(String.format("<b><font color=\"#%06X\">⬤</font> %s<br>",
|
SpannableStringBuilder spannableBuilder = new SpannableStringBuilder();
|
||||||
segment.category.color, segment.category.title));
|
|
||||||
htmlBuilder.append(formatSegmentTime(segment.start));
|
spannableBuilder.append(segment.category.getTitleWithColorDot());
|
||||||
if (segment.category != SegmentCategory.HIGHLIGHT) {
|
spannableBuilder.append('\n');
|
||||||
htmlBuilder.append(" to ").append(formatSegmentTime(segment.end));
|
|
||||||
|
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
|
if (i + 1 != numberOfSegments) {
|
||||||
htmlBuilder.append("<br>");
|
// prevents trailing new line after last segment
|
||||||
titles[i] = Html.fromHtml(htmlBuilder.toString());
|
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)
|
new AlertDialog.Builder(context).setItems(titles, segmentVoteClickListener).show();
|
||||||
.setItems(titles, segmentVoteClickListener)
|
|
||||||
.show();
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onVotingClicked failure", ex);
|
Logger.printException(() -> "onVotingClicked failure", ex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package app.revanced.extension.youtube.sponsorblock.objects;
|
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.shared.StringRef.sf;
|
||||||
|
import static app.revanced.extension.youtube.settings.Settings.*;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.text.Html;
|
import android.text.Spannable;
|
||||||
import android.text.Spanned;
|
import android.text.SpannableString;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -15,43 +16,45 @@ import androidx.annotation.Nullable;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
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.Logger;
|
||||||
import app.revanced.extension.shared.StringRef;
|
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 {
|
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"),
|
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"),
|
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"),
|
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.
|
* 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"),
|
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"),
|
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_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"),
|
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"),
|
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"),
|
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_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"),
|
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"),
|
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"),
|
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"),
|
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 skipSponsorTextCompact = sf("revanced_sb_skip_button_compact");
|
||||||
private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight");
|
private static final StringRef skipSponsorTextCompactHighlight = sf("revanced_sb_skip_button_compact_highlight");
|
||||||
@@ -90,12 +93,10 @@ public enum SegmentCategory {
|
|||||||
mValuesMap.put(value.keyValue, value);
|
mValuesMap.put(value.keyValue, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static SegmentCategory[] categoriesWithoutUnsubmitted() {
|
public static SegmentCategory[] categoriesWithoutUnsubmitted() {
|
||||||
return categoriesWithoutUnsubmitted;
|
return categoriesWithoutUnsubmitted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public static SegmentCategory[] categoriesWithoutHighlights() {
|
public static SegmentCategory[] categoriesWithoutHighlights() {
|
||||||
return 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() {
|
public static void updateEnabledCategories() {
|
||||||
Utils.verifyOnMainThread();
|
Utils.verifyOnMainThread();
|
||||||
@@ -133,32 +134,33 @@ public enum SegmentCategory {
|
|||||||
updateEnabledCategories();
|
updateEnabledCategories();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
public static int applyOpacityToColor(int color, float opacity) {
|
||||||
public final String keyValue;
|
if (opacity < 0 || opacity > 1.0f) {
|
||||||
@NonNull
|
throw new IllegalArgumentException("Invalid opacity: " + opacity);
|
||||||
public final StringSetting behaviorSetting;
|
}
|
||||||
@NonNull
|
final int opacityInt = (int) (255 * opacity);
|
||||||
private final StringSetting colorSetting;
|
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;
|
public final StringRef title;
|
||||||
@NonNull
|
|
||||||
public final StringRef description;
|
public final StringRef description;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skip button text, if the skip occurs in the first quarter of the video
|
* Skip button text, if the skip occurs in the first quarter of the video
|
||||||
*/
|
*/
|
||||||
@NonNull
|
|
||||||
public final StringRef skipButtonTextBeginning;
|
public final StringRef skipButtonTextBeginning;
|
||||||
/**
|
/**
|
||||||
* Skip button text, if the skip occurs in the middle half of the video
|
* Skip button text, if the skip occurs in the middle half of the video
|
||||||
*/
|
*/
|
||||||
@NonNull
|
|
||||||
public final StringRef skipButtonTextMiddle;
|
public final StringRef skipButtonTextMiddle;
|
||||||
/**
|
/**
|
||||||
* Skip button text, if the skip occurs in the last quarter of the video
|
* Skip button text, if the skip occurs in the last quarter of the video
|
||||||
*/
|
*/
|
||||||
@NonNull
|
|
||||||
public final StringRef skipButtonTextEnd;
|
public final StringRef skipButtonTextEnd;
|
||||||
/**
|
/**
|
||||||
* Skipped segment toast, if the skip occurred in the first quarter of the video
|
* Skipped segment toast, if the skip occurred in the first quarter of the video
|
||||||
@@ -179,10 +181,7 @@ public enum SegmentCategory {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public final Paint paint;
|
public final Paint paint;
|
||||||
|
|
||||||
/**
|
private int color;
|
||||||
* Value must be changed using {@link #setColor(String)}.
|
|
||||||
*/
|
|
||||||
public int color;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value must be changed using {@link #setBehaviour(CategoryBehaviour)}.
|
* Value must be changed using {@link #setBehaviour(CategoryBehaviour)}.
|
||||||
@@ -194,17 +193,20 @@ public enum SegmentCategory {
|
|||||||
SegmentCategory(String keyValue, StringRef title, StringRef description,
|
SegmentCategory(String keyValue, StringRef title, StringRef description,
|
||||||
StringRef skipButtonText,
|
StringRef skipButtonText,
|
||||||
StringRef skippedToastText,
|
StringRef skippedToastText,
|
||||||
StringSetting behavior, StringSetting color) {
|
StringSetting behavior,
|
||||||
|
StringSetting color, FloatSetting opacity) {
|
||||||
this(keyValue, title, description,
|
this(keyValue, title, description,
|
||||||
skipButtonText, skipButtonText, skipButtonText,
|
skipButtonText, skipButtonText, skipButtonText,
|
||||||
skippedToastText, skippedToastText, skippedToastText,
|
skippedToastText, skippedToastText, skippedToastText,
|
||||||
behavior, color);
|
behavior,
|
||||||
|
color, opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
SegmentCategory(String keyValue, StringRef title, StringRef description,
|
SegmentCategory(String keyValue, StringRef title, StringRef description,
|
||||||
StringRef skipButtonTextBeginning, StringRef skipButtonTextMiddle, StringRef skipButtonTextEnd,
|
StringRef skipButtonTextBeginning, StringRef skipButtonTextMiddle, StringRef skipButtonTextEnd,
|
||||||
StringRef skippedToastBeginning, StringRef skippedToastMiddle, StringRef skippedToastEnd,
|
StringRef skippedToastBeginning, StringRef skippedToastMiddle, StringRef skippedToastEnd,
|
||||||
StringSetting behavior, StringSetting color) {
|
StringSetting behavior,
|
||||||
|
StringSetting color, FloatSetting opacity) {
|
||||||
this.keyValue = Objects.requireNonNull(keyValue);
|
this.keyValue = Objects.requireNonNull(keyValue);
|
||||||
this.title = Objects.requireNonNull(title);
|
this.title = Objects.requireNonNull(title);
|
||||||
this.description = Objects.requireNonNull(description);
|
this.description = Objects.requireNonNull(description);
|
||||||
@@ -216,6 +218,7 @@ public enum SegmentCategory {
|
|||||||
this.skippedToastEnd = Objects.requireNonNull(skippedToastEnd);
|
this.skippedToastEnd = Objects.requireNonNull(skippedToastEnd);
|
||||||
this.behaviorSetting = Objects.requireNonNull(behavior);
|
this.behaviorSetting = Objects.requireNonNull(behavior);
|
||||||
this.colorSetting = Objects.requireNonNull(color);
|
this.colorSetting = Objects.requireNonNull(color);
|
||||||
|
this.opacitySetting = Objects.requireNonNull(opacity);
|
||||||
this.paint = new Paint();
|
this.paint = new Paint();
|
||||||
loadFromSettings();
|
loadFromSettings();
|
||||||
}
|
}
|
||||||
@@ -232,11 +235,14 @@ public enum SegmentCategory {
|
|||||||
this.behaviour = savedBehavior;
|
this.behaviour = savedBehavior;
|
||||||
|
|
||||||
String colorString = colorSetting.get();
|
String colorString = colorSetting.get();
|
||||||
|
final float opacity = opacitySetting.get();
|
||||||
try {
|
try {
|
||||||
setColor(colorString);
|
setColor(colorString);
|
||||||
|
setOpacity(opacity);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "Invalid color: " + colorString, ex);
|
Logger.printException(() -> "Invalid color: " + colorString + " opacity: " + opacity, ex);
|
||||||
colorSetting.resetToDefault();
|
colorSetting.resetToDefault();
|
||||||
|
opacitySetting.resetToDefault();
|
||||||
loadFromSettings();
|
loadFromSettings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -245,45 +251,78 @@ public enum SegmentCategory {
|
|||||||
this.behaviour = Objects.requireNonNull(behaviour);
|
this.behaviour = Objects.requireNonNull(behaviour);
|
||||||
this.behaviorSetting.save(behaviour.reVancedKeyValue);
|
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 {
|
private void updateColor() {
|
||||||
final int color = Color.parseColor(colorString) & 0xFFFFFF;
|
color = applyOpacityToColor(color, opacitySetting.get());
|
||||||
this.color = color;
|
|
||||||
paint.setColor(color);
|
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);
|
setColor(colorSetting.defaultValue);
|
||||||
|
setOpacity(opacitySetting.defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
/**
|
||||||
private static String getCategoryColorDotHTML(int color) {
|
* @param colorString Segment color with #RRGGBB format.
|
||||||
color &= 0xFFFFFF;
|
*/
|
||||||
return String.format("<font color=\"#%06X\">⬤</font>", color);
|
public void setColor(String colorString) throws IllegalArgumentException {
|
||||||
|
color = Color.parseColor(colorString);
|
||||||
|
colorSetting.save(colorString);
|
||||||
|
|
||||||
|
updateColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
/**
|
||||||
public static Spanned getCategoryColorDot(int color) {
|
* @return Integer color of #RRGGBB format.
|
||||||
return Html.fromHtml(getCategoryColorDotHTML(color));
|
*/
|
||||||
|
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);
|
return getCategoryColorDot(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
public SpannableString getTitleWithColorDot(int categoryColor) {
|
||||||
public Spanned getTitleWithColorDot() {
|
return getCategoryColorDotSpan(" " + title, categoryColor);
|
||||||
return Html.fromHtml(getCategoryColorDotHTML(color) + " " + title);
|
}
|
||||||
|
|
||||||
|
public SpannableString getTitleWithColorDot() {
|
||||||
|
return getTitleWithColorDot(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -291,7 +330,6 @@ public enum SegmentCategory {
|
|||||||
* @param videoLength length of the video
|
* @param videoLength length of the video
|
||||||
* @return the skip button text
|
* @return the skip button text
|
||||||
*/
|
*/
|
||||||
@NonNull
|
|
||||||
StringRef getSkipButtonText(long segmentStartTime, long videoLength) {
|
StringRef getSkipButtonText(long segmentStartTime, long videoLength) {
|
||||||
if (Settings.SB_COMPACT_SKIP_BUTTON.get()) {
|
if (Settings.SB_COMPACT_SKIP_BUTTON.get()) {
|
||||||
return (this == SegmentCategory.HIGHLIGHT)
|
return (this == SegmentCategory.HIGHLIGHT)
|
||||||
@@ -300,7 +338,7 @@ public enum SegmentCategory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (videoLength == 0) {
|
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;
|
final float position = segmentStartTime / (float) videoLength;
|
||||||
if (position < 0.25f) {
|
if (position < 0.25f) {
|
||||||
@@ -316,10 +354,9 @@ public enum SegmentCategory {
|
|||||||
* @param videoLength length of the video
|
* @param videoLength length of the video
|
||||||
* @return 'skipped segment' toast message
|
* @return 'skipped segment' toast message
|
||||||
*/
|
*/
|
||||||
@NonNull
|
|
||||||
StringRef getSkippedToastText(long segmentStartTime, long videoLength) {
|
StringRef getSkippedToastText(long segmentStartTime, long videoLength) {
|
||||||
if (videoLength == 0) {
|
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;
|
final float position = segmentStartTime / (float) videoLength;
|
||||||
if (position < 0.25f) {
|
if (position < 0.25f) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package app.revanced.extension.youtube.sponsorblock.objects;
|
package app.revanced.extension.youtube.sponsorblock.objects;
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
import static app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory.applyOpacityToColor;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -11,11 +12,10 @@ import android.text.Editable;
|
|||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.GridLayout;
|
||||||
import android.widget.TableLayout;
|
|
||||||
import android.widget.TableRow;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
@@ -24,27 +24,38 @@ import app.revanced.extension.shared.Utils;
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class SegmentCategoryListPreference extends ListPreference {
|
public class SegmentCategoryListPreference extends ListPreference {
|
||||||
private final SegmentCategory category;
|
private final SegmentCategory category;
|
||||||
private EditText mEditText;
|
private TextView colorDotView;
|
||||||
private int mClickedDialogEntryIndex;
|
private EditText colorEditText;
|
||||||
|
private EditText opacityEditText;
|
||||||
|
/**
|
||||||
|
* #RRGGBB
|
||||||
|
*/
|
||||||
|
private int categoryColor;
|
||||||
|
/**
|
||||||
|
* [0, 1]
|
||||||
|
*/
|
||||||
|
private float categoryOpacity;
|
||||||
|
private int selectedDialogEntryIndex;
|
||||||
|
|
||||||
public SegmentCategoryListPreference(Context context, SegmentCategory category) {
|
public SegmentCategoryListPreference(Context context, SegmentCategory category) {
|
||||||
super(context);
|
super(context);
|
||||||
final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
|
|
||||||
this.category = Objects.requireNonNull(category);
|
this.category = Objects.requireNonNull(category);
|
||||||
|
|
||||||
// Edit: Using preferences to sync together multiple pieces
|
// 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);
|
setKey(category.behaviorSetting.key);
|
||||||
setDefaultValue(category.behaviorSetting.defaultValue);
|
setDefaultValue(category.behaviorSetting.defaultValue);
|
||||||
|
|
||||||
|
final boolean isHighlightCategory = category == SegmentCategory.HIGHLIGHT;
|
||||||
setEntries(isHighlightCategory
|
setEntries(isHighlightCategory
|
||||||
? CategoryBehaviour.getBehaviorDescriptionsWithoutSkipOnce()
|
? CategoryBehaviour.getBehaviorDescriptionsWithoutSkipOnce()
|
||||||
: CategoryBehaviour.getBehaviorDescriptions());
|
: CategoryBehaviour.getBehaviorDescriptions());
|
||||||
setEntryValues(isHighlightCategory
|
setEntryValues(isHighlightCategory
|
||||||
? CategoryBehaviour.getBehaviorKeyValuesWithoutSkipOnce()
|
? CategoryBehaviour.getBehaviorKeyValuesWithoutSkipOnce()
|
||||||
: CategoryBehaviour.getBehaviorKeyValues());
|
: CategoryBehaviour.getBehaviorKeyValues());
|
||||||
|
|
||||||
setSummary(category.description.toString());
|
setSummary(category.description.toString());
|
||||||
updateTitle();
|
|
||||||
|
updateTitleFromCategory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -52,26 +63,40 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
try {
|
try {
|
||||||
Utils.setEditTextDialogTheme(builder);
|
Utils.setEditTextDialogTheme(builder);
|
||||||
|
|
||||||
|
categoryColor = category.getColorNoOpacity();
|
||||||
|
categoryOpacity = category.getOpacity();
|
||||||
|
|
||||||
Context context = builder.getContext();
|
Context context = builder.getContext();
|
||||||
TableLayout table = new TableLayout(context);
|
GridLayout gridLayout = new GridLayout(context);
|
||||||
table.setOrientation(LinearLayout.HORIZONTAL);
|
gridLayout.setPadding(70, 0, 150, 0); // Padding for the entire layout.
|
||||||
table.setPadding(70, 0, 150, 0);
|
gridLayout.setColumnCount(3);
|
||||||
|
gridLayout.setRowCount(2);
|
||||||
TableRow row = new TableRow(context);
|
|
||||||
|
|
||||||
|
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);
|
TextView colorTextLabel = new TextView(context);
|
||||||
colorTextLabel.setText(str("revanced_sb_color_dot_label"));
|
colorTextLabel.setText(str("revanced_sb_color_dot_label"));
|
||||||
row.addView(colorTextLabel);
|
colorTextLabel.setLayoutParams(gridParams);
|
||||||
|
gridLayout.addView(colorTextLabel);
|
||||||
|
|
||||||
TextView colorDotView = new TextView(context);
|
gridParams = new GridLayout.LayoutParams();
|
||||||
colorDotView.setText(category.getCategoryColorDot());
|
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
||||||
colorDotView.setPadding(30, 0, 30, 0);
|
gridParams.columnSpec = GridLayout.spec(1); // Second column.
|
||||||
row.addView(colorDotView);
|
gridParams.setMargins(0, 0, 10, 0);
|
||||||
|
colorDotView = new TextView(context);
|
||||||
|
colorDotView.setLayoutParams(gridParams);
|
||||||
|
gridLayout.addView(colorDotView);
|
||||||
|
updateCategoryColorDot();
|
||||||
|
|
||||||
mEditText = new EditText(context);
|
gridParams = new GridLayout.LayoutParams();
|
||||||
mEditText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS);
|
gridParams.rowSpec = GridLayout.spec(0); // First row.
|
||||||
mEditText.setText(category.colorString());
|
gridParams.columnSpec = GridLayout.spec(2); // Third column.
|
||||||
mEditText.addTextChangedListener(new TextWatcher() {
|
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
|
@Override
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
}
|
}
|
||||||
@@ -81,29 +106,94 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable s) {
|
public void afterTextChanged(Editable edit) {
|
||||||
try {
|
try {
|
||||||
String colorString = s.toString();
|
String colorString = edit.toString();
|
||||||
|
final int colorStringLength = colorString.length();
|
||||||
|
|
||||||
if (!colorString.startsWith("#")) {
|
if (!colorString.startsWith("#")) {
|
||||||
s.insert(0, "#"); // recursively calls back into this method
|
edit.insert(0, "#"); // Recursively calls back into this method.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (colorString.length() > 7) {
|
|
||||||
s.delete(7, colorString.length());
|
final int maxColorStringLength = 7; // #RRGGBB
|
||||||
|
if (colorStringLength > maxColorStringLength) {
|
||||||
|
edit.delete(maxColorStringLength, colorStringLength);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final int color = Color.parseColor(colorString);
|
|
||||||
colorDotView.setText(SegmentCategory.getCategoryColorDot(color));
|
categoryColor = Color.parseColor(colorString);
|
||||||
|
updateCategoryColorDot();
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
// ignore
|
// Ignore.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mEditText.setLayoutParams(new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 1f));
|
colorEditText.setLayoutParams(gridParams);
|
||||||
row.addView(mEditText);
|
gridLayout.addView(colorEditText);
|
||||||
|
|
||||||
table.addView(row);
|
gridParams = new GridLayout.LayoutParams();
|
||||||
builder.setView(table);
|
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.setTitle(category.title.toString());
|
||||||
|
|
||||||
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
|
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) -> {
|
builder.setNeutralButton(str("revanced_sb_reset_color"), (dialog, which) -> {
|
||||||
try {
|
try {
|
||||||
category.resetColor();
|
category.resetColorAndOpacity();
|
||||||
updateTitle();
|
updateTitleFromCategory();
|
||||||
Utils.showToastShort(str("revanced_sb_color_reset"));
|
Utils.showToastShort(str("revanced_sb_color_reset"));
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "setNeutralButton failure", ex);
|
Logger.printException(() -> "setNeutralButton failure", ex);
|
||||||
@@ -120,8 +210,9 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
});
|
});
|
||||||
builder.setNegativeButton(android.R.string.cancel, null);
|
builder.setNegativeButton(android.R.string.cancel, null);
|
||||||
|
|
||||||
mClickedDialogEntryIndex = findIndexOfValue(getValue());
|
selectedDialogEntryIndex = findIndexOfValue(getValue());
|
||||||
builder.setSingleChoiceItems(getEntries(), mClickedDialogEntryIndex, (dialog, which) -> mClickedDialogEntryIndex = which);
|
builder.setSingleChoiceItems(getEntries(), selectedDialogEntryIndex,
|
||||||
|
(dialog, which) -> selectedDialogEntryIndex = which);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
|
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
|
||||||
}
|
}
|
||||||
@@ -130,30 +221,51 @@ public class SegmentCategoryListPreference extends ListPreference {
|
|||||||
@Override
|
@Override
|
||||||
protected void onDialogClosed(boolean positiveResult) {
|
protected void onDialogClosed(boolean positiveResult) {
|
||||||
try {
|
try {
|
||||||
if (positiveResult && mClickedDialogEntryIndex >= 0 && getEntryValues() != null) {
|
if (positiveResult && selectedDialogEntryIndex >= 0 && getEntryValues() != null) {
|
||||||
String value = getEntryValues()[mClickedDialogEntryIndex].toString();
|
String value = getEntryValues()[selectedDialogEntryIndex].toString();
|
||||||
if (callChangeListener(value)) {
|
if (callChangeListener(value)) {
|
||||||
setValue(value);
|
setValue(value);
|
||||||
category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
|
category.setBehaviour(Objects.requireNonNull(CategoryBehaviour.byReVancedKeyValue(value)));
|
||||||
SegmentCategory.updateEnabledCategories();
|
SegmentCategory.updateEnabledCategories();
|
||||||
}
|
}
|
||||||
String colorString = mEditText.getText().toString();
|
|
||||||
try {
|
try {
|
||||||
if (!colorString.equals(category.colorString())) {
|
String colorString = colorEditText.getText().toString();
|
||||||
|
if (!colorString.equals(category.getColorString()) || categoryOpacity != category.getOpacity()) {
|
||||||
category.setColor(colorString);
|
category.setColor(colorString);
|
||||||
|
category.setOpacity(categoryOpacity);
|
||||||
Utils.showToastShort(str("revanced_sb_color_changed"));
|
Utils.showToastShort(str("revanced_sb_color_changed"));
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
Utils.showToastShort(str("revanced_sb_color_invalid"));
|
Utils.showToastShort(str("revanced_sb_color_invalid"));
|
||||||
}
|
}
|
||||||
updateTitle();
|
|
||||||
|
updateTitleFromCategory();
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onDialogClosed failure", ex);
|
Logger.printException(() -> "onDialogClosed failure", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTitle() {
|
private void applyOpacityToCategoryColor() {
|
||||||
setTitle(category.getTitleWithColorDot());
|
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
|
@NonNull
|
||||||
public final StringRef title;
|
public final StringRef title;
|
||||||
public final int apiVoteType;
|
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.title = title;
|
||||||
this.apiVoteType = apiVoteType;
|
this.apiVoteType = apiVoteType;
|
||||||
this.shouldHighlight = shouldHighlight;
|
this.highlightIfVipAndVideoIsLocked = highlightIfVipAndVideoIsLocked;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package app.revanced.extension.youtube.sponsorblock.ui;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
|
||||||
|
|
||||||
|
public class CreateSegmentButton {
|
||||||
|
@Nullable
|
||||||
|
private static PlayerControlButton instance;
|
||||||
|
|
||||||
|
public static void hideControls() {
|
||||||
|
if (instance != null) instance.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* injection point
|
||||||
|
*/
|
||||||
|
public static void initialize(View controlsView) {
|
||||||
|
try {
|
||||||
|
instance = new PlayerControlButton(
|
||||||
|
controlsView,
|
||||||
|
"revanced_sb_create_segment_button",
|
||||||
|
null,
|
||||||
|
CreateSegmentButton::shouldBeShown,
|
||||||
|
v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "initialize failure", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point
|
||||||
|
*/
|
||||||
|
public static void setVisibilityImmediate(boolean visible) {
|
||||||
|
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point
|
||||||
|
*/
|
||||||
|
public static void setVisibility(boolean visible, boolean animated) {
|
||||||
|
if (instance != null) instance.setVisibility(visible, animated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean shouldBeShown() {
|
||||||
|
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get()
|
||||||
|
&& !VideoInformation.isAtEndOfVideo();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.sponsorblock.ui;
|
|
||||||
|
|
||||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.shared.Utils;
|
|
||||||
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
|
|
||||||
|
|
||||||
// Edit: This should be a subclass of PlayerControlButton
|
|
||||||
public class CreateSegmentButtonController {
|
|
||||||
private static WeakReference<ImageView> buttonReference = new WeakReference<>(null);
|
|
||||||
private static boolean isShowing;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* injection point
|
|
||||||
*/
|
|
||||||
public static void initialize(View youtubeControlsLayout) {
|
|
||||||
try {
|
|
||||||
Logger.printDebug(() -> "initializing new segment button");
|
|
||||||
ImageView imageView = Objects.requireNonNull(Utils.getChildViewByResourceName(
|
|
||||||
youtubeControlsLayout, "revanced_sb_create_segment_button"));
|
|
||||||
imageView.setVisibility(View.GONE);
|
|
||||||
imageView.setOnClickListener(v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility());
|
|
||||||
|
|
||||||
buttonReference = new WeakReference<>(imageView);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "initialize failure", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* injection point
|
|
||||||
*/
|
|
||||||
public static void changeVisibilityImmediate(boolean visible) {
|
|
||||||
if (visible) {
|
|
||||||
// Fix button flickering, by pushing this call to the back of
|
|
||||||
// the main thread and letting other layout code run first.
|
|
||||||
Utils.runOnMainThread(() -> setVisibility(true, false));
|
|
||||||
} else {
|
|
||||||
setVisibility(false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* injection point
|
|
||||||
*/
|
|
||||||
public static void changeVisibility(boolean visible, boolean animated) {
|
|
||||||
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
|
|
||||||
if (visible && !animated) return;
|
|
||||||
|
|
||||||
setVisibility(visible, animated);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void setVisibility(boolean visible, boolean animated) {
|
|
||||||
try {
|
|
||||||
if (isShowing == visible) return;
|
|
||||||
isShowing = visible;
|
|
||||||
|
|
||||||
ImageView iView = buttonReference.get();
|
|
||||||
if (iView == null) return;
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
iView.clearAnimation();
|
|
||||||
if (!shouldBeShown()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (animated) {
|
|
||||||
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
|
|
||||||
}
|
|
||||||
iView.setVisibility(View.VISIBLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iView.getVisibility() == View.VISIBLE) {
|
|
||||||
iView.clearAnimation();
|
|
||||||
if (animated) {
|
|
||||||
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
|
|
||||||
}
|
|
||||||
iView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "changeVisibility failure", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean shouldBeShown() {
|
|
||||||
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get()
|
|
||||||
&& !VideoInformation.isAtEndOfVideo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void hide() {
|
|
||||||
if (!isShowing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Utils.verifyOnMainThread();
|
|
||||||
View v = buttonReference.get();
|
|
||||||
if (v == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
v.setVisibility(View.GONE);
|
|
||||||
isShowing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -238,8 +238,8 @@ public class SponsorBlockViewController {
|
|||||||
// but if buttons are showing when the end of the video is reached then they need
|
// but if buttons are showing when the end of the video is reached then they need
|
||||||
// to be forcefully hidden
|
// to be forcefully hidden
|
||||||
if (!Settings.AUTO_REPEAT.get()) {
|
if (!Settings.AUTO_REPEAT.get()) {
|
||||||
CreateSegmentButtonController.hide();
|
CreateSegmentButton.hideControls();
|
||||||
VotingButtonController.hide();
|
VotingButton.hideControls();
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "endOfVideoReached failure", ex);
|
Logger.printException(() -> "endOfVideoReached failure", ex);
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package app.revanced.extension.youtube.sponsorblock.ui;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.youtube.patches.VideoInformation;
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
|
||||||
|
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
|
||||||
|
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
|
||||||
|
|
||||||
|
public class VotingButton {
|
||||||
|
@Nullable
|
||||||
|
private static PlayerControlButton instance;
|
||||||
|
|
||||||
|
public static void hideControls() {
|
||||||
|
if (instance != null) instance.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* injection point
|
||||||
|
*/
|
||||||
|
public static void initialize(View controlsView) {
|
||||||
|
try {
|
||||||
|
instance = new PlayerControlButton(
|
||||||
|
controlsView,
|
||||||
|
"revanced_sb_voting_button",
|
||||||
|
null,
|
||||||
|
VotingButton::shouldBeShown,
|
||||||
|
v -> SponsorBlockUtils.onVotingClicked(v.getContext()),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "initialize failure", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point
|
||||||
|
*/
|
||||||
|
public static void setVisibilityImmediate(boolean visible) {
|
||||||
|
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point
|
||||||
|
*/
|
||||||
|
public static void setVisibility(boolean visible, boolean animated) {
|
||||||
|
if (instance != null) instance.setVisibility(visible, animated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean shouldBeShown() {
|
||||||
|
return Settings.SB_ENABLED.get() && Settings.SB_VOTING_BUTTON.get()
|
||||||
|
&& SegmentPlaybackController.videoHasSegments() && !VideoInformation.isAtEndOfVideo();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.sponsorblock.ui;
|
|
||||||
|
|
||||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import app.revanced.extension.youtube.patches.VideoInformation;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
|
|
||||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.shared.Utils;
|
|
||||||
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
|
|
||||||
|
|
||||||
// Edit: This should be a subclass of PlayerControlButton
|
|
||||||
public class VotingButtonController {
|
|
||||||
private static WeakReference<ImageView> buttonReference = new WeakReference<>(null);
|
|
||||||
private static boolean isShowing;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* injection point
|
|
||||||
*/
|
|
||||||
public static void initialize(View youtubeControlsLayout) {
|
|
||||||
try {
|
|
||||||
Logger.printDebug(() -> "initializing voting button");
|
|
||||||
ImageView imageView = Objects.requireNonNull(Utils.getChildViewByResourceName(
|
|
||||||
youtubeControlsLayout, "revanced_sb_voting_button"));
|
|
||||||
imageView.setVisibility(View.GONE);
|
|
||||||
imageView.setOnClickListener(v -> SponsorBlockUtils.onVotingClicked(v.getContext()));
|
|
||||||
|
|
||||||
buttonReference = new WeakReference<>(imageView);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "initialize failure", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* injection point
|
|
||||||
*/
|
|
||||||
public static void changeVisibilityImmediate(boolean visible) {
|
|
||||||
if (visible) {
|
|
||||||
// Fix button flickering, by pushing this call to the back of
|
|
||||||
// the main thread and letting other layout code run first.
|
|
||||||
Utils.runOnMainThread(() -> setVisibility(true, false));
|
|
||||||
} else {
|
|
||||||
setVisibility(false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* injection point
|
|
||||||
*/
|
|
||||||
public static void changeVisibility(boolean visible, boolean animated) {
|
|
||||||
// Ignore this call, otherwise with full screen thumbnails the buttons are visible while seeking.
|
|
||||||
if (visible && !animated) return;
|
|
||||||
|
|
||||||
setVisibility(visible, animated);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* injection point
|
|
||||||
*/
|
|
||||||
private static void setVisibility(boolean visible, boolean animated) {
|
|
||||||
try {
|
|
||||||
if (isShowing == visible) return;
|
|
||||||
isShowing = visible;
|
|
||||||
|
|
||||||
ImageView iView = buttonReference.get();
|
|
||||||
if (iView == null) return;
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
iView.clearAnimation();
|
|
||||||
if (!shouldBeShown()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (animated) {
|
|
||||||
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
|
|
||||||
}
|
|
||||||
iView.setVisibility(View.VISIBLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iView.getVisibility() == View.VISIBLE) {
|
|
||||||
iView.clearAnimation();
|
|
||||||
if (animated) {
|
|
||||||
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
|
|
||||||
}
|
|
||||||
iView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "changeVisibility failure", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean shouldBeShown() {
|
|
||||||
return Settings.SB_ENABLED.get() && Settings.SB_VOTING_BUTTON.get()
|
|
||||||
&& SegmentPlaybackController.videoHasSegments() && !VideoInformation.isAtEndOfVideo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void hide() {
|
|
||||||
if (!isShowing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Utils.verifyOnMainThread();
|
|
||||||
View v = buttonReference.get();
|
|
||||||
if (v == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
v.setVisibility(View.GONE);
|
|
||||||
isShowing = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package app.revanced.extension.youtube.swipecontrols
|
package app.revanced.extension.youtube.swipecontrols
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import app.revanced.extension.shared.StringRef.str
|
import app.revanced.extension.shared.StringRef.str
|
||||||
import app.revanced.extension.shared.Utils
|
import app.revanced.extension.shared.Utils
|
||||||
@@ -9,12 +8,8 @@ import app.revanced.extension.youtube.shared.PlayerType
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* provider for configuration for volume and brightness swipe controls
|
* provider for configuration for volume and brightness swipe controls
|
||||||
*
|
|
||||||
* @param context the context to create in
|
|
||||||
*/
|
*/
|
||||||
class SwipeControlsConfigurationProvider(
|
class SwipeControlsConfigurationProvider {
|
||||||
private val context: Context,
|
|
||||||
) {
|
|
||||||
//region swipe enable
|
//region swipe enable
|
||||||
/**
|
/**
|
||||||
* should swipe controls be enabled? (global setting)
|
* should swipe controls be enabled? (global setting)
|
||||||
@@ -60,6 +55,23 @@ class SwipeControlsConfigurationProvider(
|
|||||||
*/
|
*/
|
||||||
val swipeMagnitudeThreshold: Int
|
val swipeMagnitudeThreshold: Int
|
||||||
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
|
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much volume will change by single swipe.
|
||||||
|
* If it is set to 0, it will reset to the default value because 0 would disable swiping.
|
||||||
|
* */
|
||||||
|
val volumeSwipeSensitivity: Int
|
||||||
|
get() {
|
||||||
|
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
|
||||||
|
|
||||||
|
if (sensitivity < 1) {
|
||||||
|
Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
|
||||||
|
|
||||||
|
return Settings.SWIPE_VOLUME_SENSITIVITY.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
return sensitivity
|
||||||
|
}
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region overlay adjustments
|
//region overlay adjustments
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class SwipeControlsHostActivity : Activity() {
|
|||||||
private fun initialize() {
|
private fun initialize() {
|
||||||
// create controllers
|
// create controllers
|
||||||
printDebug { "initializing swipe controls controllers" }
|
printDebug { "initializing swipe controls controllers" }
|
||||||
config = SwipeControlsConfigurationProvider(this)
|
config = SwipeControlsConfigurationProvider()
|
||||||
keys = VolumeKeysController(this)
|
keys = VolumeKeysController(this)
|
||||||
audio = createAudioController()
|
audio = createAudioController()
|
||||||
screen = createScreenController()
|
screen = createScreenController()
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class VolumeKeysController(
|
|||||||
private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean {
|
private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean {
|
||||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||||
controller.audio?.apply {
|
controller.audio?.apply {
|
||||||
volume += if (volumeUp) 1 else -1
|
volume += controller.config.volumeSwipeSensitivity * if (volumeUp) 1 else -1
|
||||||
controller.overlay.onVolumeChanged(volume, maxVolume)
|
controller.overlay.onVolumeChanged(volume, maxVolume)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ abstract class BaseGestureController(
|
|||||||
controller.overlay,
|
controller.overlay,
|
||||||
10,
|
10,
|
||||||
1,
|
1,
|
||||||
|
controller.config.volumeSwipeSensitivity,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ interface VolumeAndBrightnessScroller {
|
|||||||
* @param overlayController overlay controller instance
|
* @param overlayController overlay controller instance
|
||||||
* @param volumeDistance unit distance for volume scrolling, in dp
|
* @param volumeDistance unit distance for volume scrolling, in dp
|
||||||
* @param brightnessDistance unit distance for brightness scrolling, in dp
|
* @param brightnessDistance unit distance for brightness scrolling, in dp
|
||||||
|
* @param volumeSwipeSensitivity how much volume will change by single swipe
|
||||||
*/
|
*/
|
||||||
class VolumeAndBrightnessScrollerImpl(
|
class VolumeAndBrightnessScrollerImpl(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -49,6 +50,7 @@ class VolumeAndBrightnessScrollerImpl(
|
|||||||
private val overlayController: SwipeControlsOverlay,
|
private val overlayController: SwipeControlsOverlay,
|
||||||
volumeDistance: Int = 10,
|
volumeDistance: Int = 10,
|
||||||
brightnessDistance: Int = 1,
|
brightnessDistance: Int = 1,
|
||||||
|
private val volumeSwipeSensitivity: Int,
|
||||||
) : VolumeAndBrightnessScroller {
|
) : VolumeAndBrightnessScroller {
|
||||||
|
|
||||||
// region volume
|
// region volume
|
||||||
@@ -60,7 +62,7 @@ class VolumeAndBrightnessScrollerImpl(
|
|||||||
),
|
),
|
||||||
) { _, _, direction ->
|
) { _, _, direction ->
|
||||||
volumeController?.run {
|
volumeController?.run {
|
||||||
volume += direction
|
volume += direction * volumeSwipeSensitivity
|
||||||
overlayController.onVolumeChanged(volume, maxVolume)
|
overlayController.onVolumeChanged(volume, maxVolume)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ import kotlin.math.min
|
|||||||
import kotlin.math.round
|
import kotlin.math.round
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main overlay layout for displaying volume and brightness level with both circular and rectangular progress bars.
|
* Main overlay layout for displaying volume and brightness level with both circular and horizontal progress bars.
|
||||||
*/
|
*/
|
||||||
class SwipeControlsOverlayLayout(
|
class SwipeControlsOverlayLayout(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val config: SwipeControlsConfigurationProvider,
|
private val config: SwipeControlsConfigurationProvider,
|
||||||
) : RelativeLayout(context), SwipeControlsOverlay {
|
) : RelativeLayout(context), SwipeControlsOverlay {
|
||||||
|
|
||||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
|
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider())
|
||||||
|
|
||||||
// Drawable icons for brightness and volume
|
// Drawable icons for brightness and volume
|
||||||
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
||||||
@@ -69,7 +69,7 @@ class SwipeControlsOverlayLayout(
|
|||||||
}
|
}
|
||||||
addView(circularProgressView)
|
addView(circularProgressView)
|
||||||
|
|
||||||
// Initialize rectangular progress bar
|
// Initialize horizontal progress bar
|
||||||
val screenWidth = resources.displayMetrics.widthPixels
|
val screenWidth = resources.displayMetrics.widthPixels
|
||||||
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
|
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
|
||||||
horizontalProgressView = HorizontalProgressView(
|
horizontalProgressView = HorizontalProgressView(
|
||||||
@@ -152,10 +152,7 @@ class SwipeControlsOverlayLayout(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for progress views to reduce code duplication.
|
* Abstract base class for progress views.
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Abstract base class for progress views to reduce code duplication.
|
|
||||||
*/
|
*/
|
||||||
abstract class AbstractProgressView(
|
abstract class AbstractProgressView(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -183,10 +180,9 @@ abstract class AbstractProgressView(
|
|||||||
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
color = overlayTextColor
|
color = overlayTextColor
|
||||||
textAlign = Paint.Align.CENTER
|
textAlign = Paint.Align.CENTER
|
||||||
textSize = 30f // Can adjust based on need
|
textSize = 40f // Can adjust based on need
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected var progress = 0
|
protected var progress = 0
|
||||||
protected var maxProgress = 100
|
protected var maxProgress = 100
|
||||||
protected var displayText: String = "0"
|
protected var displayText: String = "0"
|
||||||
@@ -211,7 +207,7 @@ abstract class AbstractProgressView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom view for rendering a circular progress indicator with text and icon.
|
* Custom view for rendering a circular progress indicator with icons and text.
|
||||||
*/
|
*/
|
||||||
class CircularProgressView(
|
class CircularProgressView(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -235,7 +231,7 @@ class CircularProgressView(
|
|||||||
private val rectF = RectF()
|
private val rectF = RectF()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
textPaint.textSize = 40f // Override default text size for horizontal view
|
textPaint.textSize = 40f // Override default text size for circular view
|
||||||
progressPaint.strokeWidth = 20f
|
progressPaint.strokeWidth = 20f
|
||||||
fillBackgroundPaint.strokeWidth = 20f
|
fillBackgroundPaint.strokeWidth = 20f
|
||||||
progressPaint.strokeCap = Paint.Cap.ROUND
|
progressPaint.strokeCap = Paint.Cap.ROUND
|
||||||
@@ -266,7 +262,7 @@ class CircularProgressView(
|
|||||||
it.draw(canvas)
|
it.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not in icon-only mode, draw the text inside the ring.
|
// If not a minimal style mode, draw the text inside the ring.
|
||||||
if (!overlayShowOverlayMinimalStyle) {
|
if (!overlayShowOverlayMinimalStyle) {
|
||||||
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
|
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
|
||||||
}
|
}
|
||||||
@@ -300,7 +296,7 @@ class HorizontalProgressView(
|
|||||||
private val padding = 40f
|
private val padding = 40f
|
||||||
|
|
||||||
init {
|
init {
|
||||||
textPaint.textSize = 30f // Override default text size for horizontal view
|
textPaint.textSize = 36f // Override default text size for horizontal view
|
||||||
progressPaint.strokeWidth = 0f
|
progressPaint.strokeWidth = 0f
|
||||||
progressPaint.strokeCap = Paint.Cap.BUTT
|
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||||
progressPaint.style = Paint.Style.FILL
|
progressPaint.style = Paint.Style.FILL
|
||||||
|
|||||||
@@ -1,38 +1,35 @@
|
|||||||
package app.revanced.extension.youtube.videoplayer;
|
package app.revanced.extension.youtube.videoplayer;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
|
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class CopyVideoUrlButton extends PlayerControlButton {
|
public class CopyVideoUrlButton {
|
||||||
@Nullable
|
@Nullable
|
||||||
private static CopyVideoUrlButton instance;
|
private static PlayerControlButton instance;
|
||||||
|
|
||||||
public CopyVideoUrlButton(ViewGroup viewGroup) {
|
|
||||||
super(
|
|
||||||
viewGroup,
|
|
||||||
"revanced_copy_video_url_button",
|
|
||||||
Settings.COPY_VIDEO_URL,
|
|
||||||
view -> CopyVideoUrlPatch.copyUrl(false),
|
|
||||||
view -> {
|
|
||||||
CopyVideoUrlPatch.copyUrl(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void initializeButton(View view) {
|
public static void initializeButton(View controlsView) {
|
||||||
try {
|
try {
|
||||||
instance = new CopyVideoUrlButton((ViewGroup) view);
|
instance = new PlayerControlButton(
|
||||||
|
controlsView,
|
||||||
|
"revanced_copy_video_url_button",
|
||||||
|
"revanced_copy_video_url_button_placeholder",
|
||||||
|
Settings.COPY_VIDEO_URL::get,
|
||||||
|
view -> CopyVideoUrlPatch.copyUrl(false),
|
||||||
|
view -> {
|
||||||
|
CopyVideoUrlPatch.copyUrl(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "initializeButton failure", ex);
|
Logger.printException(() -> "initializeButton failure", ex);
|
||||||
}
|
}
|
||||||
@@ -41,14 +38,14 @@ public class CopyVideoUrlButton extends PlayerControlButton {
|
|||||||
/**
|
/**
|
||||||
* injection point
|
* injection point
|
||||||
*/
|
*/
|
||||||
public static void changeVisibilityImmediate(boolean visible) {
|
public static void setVisibilityImmediate(boolean visible) {
|
||||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* injection point
|
* injection point
|
||||||
*/
|
*/
|
||||||
public static void changeVisibility(boolean visible, boolean animated) {
|
public static void setVisibility(boolean visible, boolean animated) {
|
||||||
if (instance != null) instance.setVisibility(visible, animated);
|
if (instance != null) instance.setVisibility(visible, animated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,38 +1,35 @@
|
|||||||
package app.revanced.extension.youtube.videoplayer;
|
package app.revanced.extension.youtube.videoplayer;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
|
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class CopyVideoUrlTimestampButton extends PlayerControlButton {
|
public class CopyVideoUrlTimestampButton {
|
||||||
@Nullable
|
@Nullable
|
||||||
private static CopyVideoUrlTimestampButton instance;
|
private static PlayerControlButton instance;
|
||||||
|
|
||||||
public CopyVideoUrlTimestampButton(ViewGroup bottomControlsViewGroup) {
|
|
||||||
super(
|
|
||||||
bottomControlsViewGroup,
|
|
||||||
"revanced_copy_video_url_timestamp_button",
|
|
||||||
Settings.COPY_VIDEO_URL_TIMESTAMP,
|
|
||||||
view -> CopyVideoUrlPatch.copyUrl(true),
|
|
||||||
view -> {
|
|
||||||
CopyVideoUrlPatch.copyUrl(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void initializeButton(View bottomControlsViewGroup) {
|
public static void initializeButton(View controlsView) {
|
||||||
try {
|
try {
|
||||||
instance = new CopyVideoUrlTimestampButton((ViewGroup) bottomControlsViewGroup);
|
instance = new PlayerControlButton(
|
||||||
|
controlsView,
|
||||||
|
"revanced_copy_video_url_timestamp_button",
|
||||||
|
"revanced_copy_video_url_timestamp_button_placeholder",
|
||||||
|
Settings.COPY_VIDEO_URL_TIMESTAMP::get,
|
||||||
|
view -> CopyVideoUrlPatch.copyUrl(true),
|
||||||
|
view -> {
|
||||||
|
CopyVideoUrlPatch.copyUrl(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "initializeButton failure", ex);
|
Logger.printException(() -> "initializeButton failure", ex);
|
||||||
}
|
}
|
||||||
@@ -41,14 +38,14 @@ public class CopyVideoUrlTimestampButton extends PlayerControlButton {
|
|||||||
/**
|
/**
|
||||||
* injection point
|
* injection point
|
||||||
*/
|
*/
|
||||||
public static void changeVisibilityImmediate(boolean visible) {
|
public static void setVisibilityImmediate(boolean visible) {
|
||||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* injection point
|
* injection point
|
||||||
*/
|
*/
|
||||||
public static void changeVisibility(boolean visible, boolean animated) {
|
public static void setVisibility(boolean visible, boolean animated) {
|
||||||
if (instance != null) instance.setVisibility(visible, animated);
|
if (instance != null) instance.setVisibility(visible, animated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package app.revanced.extension.youtube.videoplayer;
|
package app.revanced.extension.youtube.videoplayer;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@@ -11,26 +10,23 @@ import app.revanced.extension.youtube.patches.VideoInformation;
|
|||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class ExternalDownloadButton extends PlayerControlButton {
|
public class ExternalDownloadButton {
|
||||||
@Nullable
|
@Nullable
|
||||||
private static ExternalDownloadButton instance;
|
private static PlayerControlButton instance;
|
||||||
|
|
||||||
public ExternalDownloadButton(ViewGroup viewGroup) {
|
|
||||||
super(
|
|
||||||
viewGroup,
|
|
||||||
"revanced_external_download_button",
|
|
||||||
Settings.EXTERNAL_DOWNLOADER,
|
|
||||||
ExternalDownloadButton::onDownloadClick,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void initializeButton(View view) {
|
public static void initializeButton(View controlsView) {
|
||||||
try {
|
try {
|
||||||
instance = new ExternalDownloadButton((ViewGroup) view);
|
instance = new PlayerControlButton(
|
||||||
|
controlsView,
|
||||||
|
"revanced_external_download_button",
|
||||||
|
"revanced_external_download_button_placeholder",
|
||||||
|
Settings.EXTERNAL_DOWNLOADER::get,
|
||||||
|
ExternalDownloadButton::onDownloadClick,
|
||||||
|
null
|
||||||
|
);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "initializeButton failure", ex);
|
Logger.printException(() -> "initializeButton failure", ex);
|
||||||
}
|
}
|
||||||
@@ -39,14 +35,14 @@ public class ExternalDownloadButton extends PlayerControlButton {
|
|||||||
/**
|
/**
|
||||||
* injection point
|
* injection point
|
||||||
*/
|
*/
|
||||||
public static void changeVisibilityImmediate(boolean visible) {
|
public static void setVisibilityImmediate(boolean visible) {
|
||||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* injection point
|
* Injection point
|
||||||
*/
|
*/
|
||||||
public static void changeVisibility(boolean visible, boolean animated) {
|
public static void setVisibility(boolean visible, boolean animated) {
|
||||||
if (instance != null) instance.setVisibility(visible, animated);
|
if (instance != null) instance.setVisibility(visible, animated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,31 @@
|
|||||||
package app.revanced.extension.youtube.videoplayer;
|
package app.revanced.extension.youtube.videoplayer;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class PlaybackSpeedDialogButton extends PlayerControlButton {
|
public class PlaybackSpeedDialogButton {
|
||||||
@Nullable
|
@Nullable
|
||||||
private static PlaybackSpeedDialogButton instance;
|
private static PlayerControlButton instance;
|
||||||
|
|
||||||
public PlaybackSpeedDialogButton(ViewGroup viewGroup) {
|
|
||||||
super(
|
|
||||||
viewGroup,
|
|
||||||
"revanced_playback_speed_dialog_button",
|
|
||||||
Settings.PLAYBACK_SPEED_DIALOG_BUTTON,
|
|
||||||
view -> CustomPlaybackSpeedPatch.showOldPlaybackSpeedMenu(),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
public static void initializeButton(View view) {
|
public static void initializeButton(View controlsView) {
|
||||||
try {
|
try {
|
||||||
instance = new PlaybackSpeedDialogButton((ViewGroup) view);
|
instance = new PlayerControlButton(
|
||||||
|
controlsView,
|
||||||
|
"revanced_playback_speed_dialog_button",
|
||||||
|
"revanced_playback_speed_dialog_button_placeholder",
|
||||||
|
Settings.PLAYBACK_SPEED_DIALOG_BUTTON::get,
|
||||||
|
view -> CustomPlaybackSpeedPatch.showOldPlaybackSpeedMenu(),
|
||||||
|
null
|
||||||
|
);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "initializeButton failure", ex);
|
Logger.printException(() -> "initializeButton failure", ex);
|
||||||
}
|
}
|
||||||
@@ -38,14 +34,14 @@ public class PlaybackSpeedDialogButton extends PlayerControlButton {
|
|||||||
/**
|
/**
|
||||||
* injection point
|
* injection point
|
||||||
*/
|
*/
|
||||||
public static void changeVisibilityImmediate(boolean visible) {
|
public static void setVisibilityImmediate(boolean visible) {
|
||||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* injection point
|
* injection point
|
||||||
*/
|
*/
|
||||||
public static void changeVisibility(boolean visible, boolean animated) {
|
public static void setVisibility(boolean visible, boolean animated) {
|
||||||
if (instance != null) instance.setVisibility(visible, animated);
|
if (instance != null) instance.setVisibility(visible, animated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,73 +1,92 @@
|
|||||||
package app.revanced.extension.youtube.videoplayer;
|
package app.revanced.extension.youtube.videoplayer;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
import kotlin.Unit;
|
||||||
|
|
||||||
public abstract class PlayerControlButton {
|
public class PlayerControlButton {
|
||||||
private static final Animation fadeIn;
|
public interface PlayerControlButtonVisibility {
|
||||||
private static final Animation fadeOut;
|
/**
|
||||||
|
* @return If the button should be shown when the player overlay is visible.
|
||||||
|
*/
|
||||||
|
boolean shouldBeShown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int fadeInDuration;
|
||||||
|
private static final int fadeOutDuration;
|
||||||
|
|
||||||
|
private static final Animation fadeInAnimation;
|
||||||
|
private static final Animation fadeOutAnimation;
|
||||||
private static final Animation fadeOutImmediate;
|
private static final Animation fadeOutImmediate;
|
||||||
|
|
||||||
private final WeakReference<ImageView> buttonRef;
|
|
||||||
protected final BooleanSetting setting;
|
|
||||||
protected boolean isVisible;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// TODO: check if these durations are correct.
|
fadeInDuration = Utils.getResourceInteger("fade_duration_fast");
|
||||||
fadeIn = Utils.getResourceAnimation("fade_in");
|
fadeOutDuration = Utils.getResourceInteger("fade_duration_scheduled");
|
||||||
fadeIn.setDuration(Utils.getResourceInteger("fade_duration_fast"));
|
|
||||||
|
|
||||||
fadeOut = Utils.getResourceAnimation("fade_out");
|
fadeInAnimation = Utils.getResourceAnimation("fade_in");
|
||||||
fadeOut.setDuration(Utils.getResourceInteger("fade_duration_scheduled"));
|
fadeInAnimation.setDuration(fadeInDuration);
|
||||||
|
|
||||||
|
fadeOutAnimation = Utils.getResourceAnimation("fade_out");
|
||||||
|
fadeOutAnimation.setDuration(fadeOutDuration);
|
||||||
|
|
||||||
|
// Animation for the fast fade out after tapping the overlay.
|
||||||
|
// Currently not used but should be.
|
||||||
fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out");
|
fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out");
|
||||||
fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast"));
|
fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
private final WeakReference<View> buttonRef;
|
||||||
public static Animation getButtonFadeIn() {
|
/**
|
||||||
return fadeIn;
|
* Empty view with the same layout size as the button. Used to fill empty space while the
|
||||||
}
|
* fade out animation runs. Without this the chapter titles overlapping the button when fading out.
|
||||||
|
*/
|
||||||
|
private final WeakReference<View> placeHolderRef;
|
||||||
|
private final PlayerControlButtonVisibility visibilityCheck;
|
||||||
|
private boolean isVisible;
|
||||||
|
|
||||||
@NonNull
|
public PlayerControlButton(View controlsViewGroup,
|
||||||
public static Animation getButtonFadeOut() {
|
String imageViewButtonId,
|
||||||
return fadeOut;
|
@Nullable String placeholderId,
|
||||||
}
|
PlayerControlButtonVisibility buttonVisibility,
|
||||||
|
View.OnClickListener onClickListener,
|
||||||
@NonNull
|
|
||||||
public static Animation getButtonFadeOutImmediately() {
|
|
||||||
return fadeOutImmediate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerControlButton(@NonNull ViewGroup bottomControlsViewGroup, @NonNull String imageViewButtonId,
|
|
||||||
@NonNull BooleanSetting booleanSetting, @NonNull View.OnClickListener onClickListener,
|
|
||||||
@Nullable View.OnLongClickListener longClickListener) {
|
@Nullable View.OnLongClickListener longClickListener) {
|
||||||
Logger.printDebug(() -> "Initializing button: " + imageViewButtonId);
|
ImageView imageView = Utils.getChildViewByResourceName(controlsViewGroup, imageViewButtonId);
|
||||||
|
|
||||||
ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById(
|
|
||||||
Utils.getResourceIdentifier(imageViewButtonId, "id")
|
|
||||||
));
|
|
||||||
imageView.setVisibility(View.GONE);
|
imageView.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
View tempPlaceholder = null;
|
||||||
|
if (placeholderId != null) {
|
||||||
|
tempPlaceholder = Utils.getChildViewByResourceName(controlsViewGroup, placeholderId);
|
||||||
|
tempPlaceholder.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
placeHolderRef = new WeakReference<>(tempPlaceholder);
|
||||||
|
|
||||||
imageView.setOnClickListener(onClickListener);
|
imageView.setOnClickListener(onClickListener);
|
||||||
if (longClickListener != null) {
|
if (longClickListener != null) {
|
||||||
imageView.setOnLongClickListener(longClickListener);
|
imageView.setOnLongClickListener(longClickListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
setting = booleanSetting;
|
visibilityCheck = buttonVisibility;
|
||||||
buttonRef = new WeakReference<>(imageView);
|
buttonRef = new WeakReference<>(imageView);
|
||||||
|
isVisible = false;
|
||||||
|
|
||||||
|
// Update the visibility after the player type changes.
|
||||||
|
// This ensures that button animations are cleared and their states are updated correctly
|
||||||
|
// when switching between states like minimized, maximized, or fullscreen, preventing
|
||||||
|
// "stuck" animations or incorrect visibility. Without this fix the issue is most noticable
|
||||||
|
// when maximizing type 3 miniplayer.
|
||||||
|
PlayerType.getOnChange().addObserver((PlayerType type) -> {
|
||||||
|
playerTypeChanged(type);
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVisibilityImmediate(boolean visible) {
|
public void setVisibilityImmediate(boolean visible) {
|
||||||
@@ -92,26 +111,80 @@ public abstract class PlayerControlButton {
|
|||||||
if (isVisible == visible) return;
|
if (isVisible == visible) return;
|
||||||
isVisible = visible;
|
isVisible = visible;
|
||||||
|
|
||||||
ImageView iView = buttonRef.get();
|
View button = buttonRef.get();
|
||||||
if (iView == null) {
|
if (button == null) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visible && setting.get()) {
|
View placeholder = placeHolderRef.get();
|
||||||
iView.clearAnimation();
|
final boolean shouldBeShown = visibilityCheck.shouldBeShown();
|
||||||
|
|
||||||
|
if (visible && shouldBeShown) {
|
||||||
|
button.clearAnimation();
|
||||||
if (animated) {
|
if (animated) {
|
||||||
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
|
button.startAnimation(PlayerControlButton.fadeInAnimation);
|
||||||
}
|
}
|
||||||
iView.setVisibility(View.VISIBLE);
|
button.setVisibility(View.VISIBLE);
|
||||||
} else if (iView.getVisibility() == View.VISIBLE) {
|
|
||||||
iView.clearAnimation();
|
if (placeholder != null) {
|
||||||
if (animated) {
|
placeholder.setVisibility(View.GONE);
|
||||||
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
|
}
|
||||||
|
} else {
|
||||||
|
if (button.getVisibility() == View.VISIBLE) {
|
||||||
|
button.clearAnimation();
|
||||||
|
if (animated) {
|
||||||
|
button.startAnimation(PlayerControlButton.fadeOutAnimation);
|
||||||
|
}
|
||||||
|
button.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (placeholder != null) {
|
||||||
|
placeholder.setVisibility(shouldBeShown
|
||||||
|
? View.VISIBLE
|
||||||
|
: View.GONE);
|
||||||
}
|
}
|
||||||
iView.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "setVisibility failure", ex);
|
Logger.printException(() -> "private_setVisibility failure", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Synchronizes the button state after the player state changes.
|
||||||
|
*/
|
||||||
|
private void playerTypeChanged(PlayerType newType) {
|
||||||
|
if (newType != PlayerType.WATCH_WHILE_MINIMIZED && !newType.isMaximizedOrFullscreen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
View button = buttonRef.get();
|
||||||
|
if (button == null) return;
|
||||||
|
|
||||||
|
button.clearAnimation();
|
||||||
|
View placeholder = placeHolderRef.get();
|
||||||
|
|
||||||
|
if (visibilityCheck.shouldBeShown()) {
|
||||||
|
if (isVisible) {
|
||||||
|
button.setVisibility(View.VISIBLE);
|
||||||
|
if (placeholder != null) placeholder.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
button.setVisibility(View.GONE);
|
||||||
|
if (placeholder != null) placeholder.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
button.setVisibility(View.GONE);
|
||||||
|
if (placeholder != null) placeholder.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hide() {
|
||||||
|
if (!isVisible) return;
|
||||||
|
|
||||||
|
Utils.verifyOnMainThread();
|
||||||
|
View view = buttonRef.get();
|
||||||
|
if (view == null) return;
|
||||||
|
view.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
view = placeHolderRef.get();
|
||||||
|
if (view != null) view.setVisibility(View.GONE);
|
||||||
|
isVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.13.0-dev.2
|
version = 5.20.0-dev.5
|
||||||
|
|||||||
@@ -108,6 +108,10 @@ public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveSha
|
|||||||
public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
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 interface class app/revanced/patches/all/misc/transformation/IMethodCall {
|
||||||
public abstract fun getDefinedClassName ()Ljava/lang/String;
|
public abstract fun getDefinedClassName ()Ljava/lang/String;
|
||||||
public abstract fun getMethodName ()Ljava/lang/String;
|
public abstract fun getMethodName ()Ljava/lang/String;
|
||||||
@@ -132,6 +136,10 @@ public final class app/revanced/patches/amazon/DeepLinkingPatchKt {
|
|||||||
public static final fun getDeepLinkingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
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 final class app/revanced/patches/backdrops/misc/pro/ProUnlockPatchKt {
|
||||||
public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getProUnlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -268,6 +276,10 @@ public final class app/revanced/patches/messenger/inputfield/DisableTypingIndica
|
|||||||
public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
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 final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt {
|
||||||
public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -348,6 +360,14 @@ public final class app/revanced/patches/nfctoolsse/misc/pro/UnlockProPatchKt {
|
|||||||
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/nunl/ads/HideAdsPatchKt {
|
||||||
|
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/nunl/firebase/SpoofCertificatePatchKt {
|
||||||
|
public static final fun getSpoofCertificatePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/nyx/misc/pro/UnlockProPatchKt {
|
public final class app/revanced/patches/nyx/misc/pro/UnlockProPatchKt {
|
||||||
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -388,6 +408,10 @@ public final class app/revanced/patches/pixiv/ads/HideAdsPatchKt {
|
|||||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/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 final class app/revanced/patches/rar/misc/annoyances/purchasereminder/HidePurchaseReminderPatchKt {
|
||||||
public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHidePurchaseReminderPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -548,7 +572,9 @@ public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt {
|
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 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 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 (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;
|
public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
@@ -768,8 +794,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/TextPref
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt {
|
public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt {
|
||||||
public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)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;ILjava/lang/Object;)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 {
|
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
|
||||||
@@ -808,6 +834,18 @@ public final class app/revanced/patches/spotify/lite/ondemand/OnDemandPatchKt {
|
|||||||
public static final fun getOnDemandPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
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/SpoofSignaturePatchKt {
|
||||||
|
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt {
|
public final class app/revanced/patches/spotify/navbar/PremiumNavbarTabPatchKt {
|
||||||
public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getPremiumNavbarTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1076,6 +1114,14 @@ public final class app/revanced/patches/youtube/interaction/seekbar/EnableSlideT
|
|||||||
public static final fun getEnableSlideToSeekPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
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 final class app/revanced/patches/youtube/interaction/seekbar/SeekbarThumbnailsPatchKt {
|
||||||
public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getSeekbarThumbnailsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1116,6 +1162,10 @@ public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideE
|
|||||||
public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/layout/hide/endscreensuggestion/HideEndScreenSuggestedVideoPatchKt {
|
||||||
|
public static final fun getHideEndScreenSuggestedVideoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatchKt {
|
public final class app/revanced/patches/youtube/layout/hide/fullscreenambientmode/DisableFullscreenAmbientModePatchKt {
|
||||||
public static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getDisableFullscreenAmbientModePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1161,17 +1211,7 @@ public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPa
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatchKt {
|
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 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 {
|
public final class app/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatchKt {
|
||||||
@@ -1366,6 +1406,12 @@ 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_46_or_greater ()Z
|
||||||
public static final fun is_19_47_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_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 final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
|
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
|
||||||
@@ -1451,6 +1497,10 @@ public final class app/revanced/patches/youtube/video/quality/RememberVideoQuali
|
|||||||
public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getRememberVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/video/quality/VideoQualityPatchKt {
|
||||||
|
public static final fun getVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt {
|
public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt {
|
||||||
public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1475,7 +1525,11 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/util/BytecodeUtilsKt {
|
public final class app/revanced/util/BytecodeUtilsKt {
|
||||||
|
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||||
|
public static final fun 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 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;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 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;
|
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||||
@@ -1502,9 +1556,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 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;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 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 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 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 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 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 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
|
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import app.revanced.patches.all.misc.transformation.IMethodCall
|
|||||||
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
|
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
|
||||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||||
"Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch"
|
"Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch"
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val spoofWifiPatch = bytecodePatch(
|
val spoofWifiPatch = bytecodePatch(
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package app.revanced.patches.all.misc.targetSdk
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
|
import app.revanced.util.getNode
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val setTargetSdkVersion34 = resourcePatch(
|
||||||
|
name = "Set target SDK version 34",
|
||||||
|
description = "Changes the target SDK to version 34 (Android 14). " +
|
||||||
|
"For devices running Android 15+, this will disable edge-to-edge display.",
|
||||||
|
use = false,
|
||||||
|
) {
|
||||||
|
execute {
|
||||||
|
val targetSdkOverride = 34 // Android 14.
|
||||||
|
|
||||||
|
document("AndroidManifest.xml").use { document ->
|
||||||
|
fun getLogger() = Logger.getLogger(this::class.java.name)
|
||||||
|
|
||||||
|
// Ideally, the override should only be applied if the existing target is higher.
|
||||||
|
// But since ApkTool does not add targetSdkVersion to the decompiled AndroidManifest,
|
||||||
|
// there is no way to check targetSdkVersion. Instead, check compileSdkVersion and print a warning.
|
||||||
|
try {
|
||||||
|
val manifestElement = document.getNode("manifest") as Element
|
||||||
|
val compileSdkVersion = Integer.parseInt(
|
||||||
|
manifestElement.getAttribute("android:compileSdkVersion")
|
||||||
|
)
|
||||||
|
if (compileSdkVersion <= targetSdkOverride) {
|
||||||
|
getLogger().warning(
|
||||||
|
"This app does not appear to use a target SDK above $targetSdkOverride: " +
|
||||||
|
"(compileSdkVersion: $compileSdkVersion)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
getLogger().warning("Could not check compileSdkVersion")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change targetSdkVersion to override value.
|
||||||
|
document.getElementsByTagName("manifest").item(0).let {
|
||||||
|
var element = it.ownerDocument.createElement("uses-sdk")
|
||||||
|
element.setAttribute("android:targetSdkVersion", targetSdkOverride.toString())
|
||||||
|
|
||||||
|
it.appendChild(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,8 @@ import org.w3c.dom.Element
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val changeVersionCodePatch = resourcePatch(
|
val changeVersionCodePatch = resourcePatch(
|
||||||
name = "Change version code",
|
name = "Change version code",
|
||||||
description = "Changes the version code of the app. By default the highest version code is set. " +
|
description = "Changes the version code of the app. This will turn off app store updates " +
|
||||||
"This allows older versions of an app to be installed " +
|
"and allows downgrading an existing app install to an older app version.",
|
||||||
"if their version code is set to the same or a higher value and can stop app stores to update the app.",
|
|
||||||
use = false,
|
use = false,
|
||||||
) {
|
) {
|
||||||
val versionCode by intOption(
|
val versionCode by intOption(
|
||||||
@@ -21,7 +20,8 @@ val changeVersionCodePatch = resourcePatch(
|
|||||||
"Highest" to Int.MAX_VALUE,
|
"Highest" to Int.MAX_VALUE,
|
||||||
),
|
),
|
||||||
title = "Version code",
|
title = "Version code",
|
||||||
description = "The version code to use",
|
description = "The version code to use. Using the highest value turns off app store " +
|
||||||
|
"updates and allows downgrading an existing app install to an older app version.",
|
||||||
required = true,
|
required = true,
|
||||||
) { versionCode -> versionCode!! >= 1 }
|
) { versionCode -> versionCode!! >= 1 }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package app.revanced.patches.angulus.ads
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
// Keywords to search for in case the method name changes:
|
||||||
|
// dailyMeasurementCount
|
||||||
|
// lastMeasurementDate
|
||||||
|
// dailyAdResetCount
|
||||||
|
// MeasurementPrefs
|
||||||
|
|
||||||
|
// This fingerprint targets a method that returns the daily measurement count.
|
||||||
|
// This method is used to determine if the user has reached the daily limit of measurements.
|
||||||
|
internal val getDailyMeasurementCountFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PRIVATE)
|
||||||
|
returns("I")
|
||||||
|
strings("dailyMeasurementCount")
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.angulus.ads
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val angulusPatch = bytecodePatch(name = "Hide ads") {
|
||||||
|
compatibleWith("com.drinkplusplus.angulus")
|
||||||
|
|
||||||
|
execute {
|
||||||
|
// Always return 0 as the daily measurement count.
|
||||||
|
getDailyMeasurementCountFingerprint.method.returnEarly()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
|||||||
val hideSponsoredStoriesPatch = bytecodePatch(
|
val hideSponsoredStoriesPatch = bytecodePatch(
|
||||||
name = "Hide 'Sponsored Stories'",
|
name = "Hide 'Sponsored Stories'",
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.facebook.katana")
|
compatibleWith("com.facebook.katana"("490.0.0.63.82"))
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
val sponsoredDataModelTemplateMethod = getSponsoredDataModelTemplateFingerprint.originalMethod
|
val sponsoredDataModelTemplateMethod = getSponsoredDataModelTemplateFingerprint.originalMethod
|
||||||
|
|||||||
@@ -27,4 +27,5 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
|
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
|
||||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
|
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
|
||||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||||
|
addStringResources = false,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
|
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
|
||||||
fromPackageName = PHOTOS_PACKAGE_NAME,
|
fromPackageName = PHOTOS_PACKAGE_NAME,
|
||||||
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
|
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
|
||||||
|
addStringResources = false,
|
||||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
||||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,23 +3,28 @@ package app.revanced.patches.googlephotos.misc.preferences
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
@Deprecated("This patch no longer works and this code will soon be deleted")
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch(
|
val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch(
|
||||||
name = "Restore hidden 'Back up while charging' toggle",
|
description = "Restores a hidden toggle to only run backups when the device is charging."
|
||||||
description = "Restores a hidden toggle to only run backups when the device is charging.",
|
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.google.android.apps.photos")
|
compatibleWith("com.google.android.apps.photos"("7.11.0.705590205"))
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
|
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
|
||||||
val chargingPrefStringIndex = backupPreferencesFingerprint.stringMatches!!.first().index
|
backupPreferencesFingerprint.let {
|
||||||
backupPreferencesFingerprint.method.apply {
|
it.method.apply {
|
||||||
// Get the register of move-result.
|
val index = indexOfFirstInstructionOrThrow(
|
||||||
val resultRegister = getInstruction<OneRegisterInstruction>(chargingPrefStringIndex + 2).registerA
|
it.stringMatches!!.first().index,
|
||||||
// Insert const after move-result to override register as true.
|
Opcode.MOVE_RESULT
|
||||||
addInstruction(chargingPrefStringIndex + 3, "const/4 v$resultRegister, 0x1")
|
)
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
addInstruction(index + 1, "const/4 v$register, 0x1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user