mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-18 07:02:28 +01:00
Compare commits
85 Commits
v5.12.0-de
...
v5.14.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
4b77d27c77 | ||
|
|
7991c80129 | ||
|
|
6baf4ea2ac | ||
|
|
c89538c8f5 | ||
|
|
94fb367618 | ||
|
|
354835966d | ||
|
|
168f9b769e | ||
|
|
e4c4b3a73a | ||
|
|
fce98b4960 | ||
|
|
839aa81e9c |
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 */8 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
249
CHANGELOG.md
249
CHANGELOG.md
@@ -1,3 +1,252 @@
|
|||||||
|
# [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)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **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))
|
||||||
|
|
||||||
|
# [5.13.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.12.0...v5.13.0-dev.1) (2025-02-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide layout components:** Do not hide 'Show anyway' button in search results ([4ac8854](https://github.com/ReVanced/revanced-patches/commit/4ac8854b99808a8957f3b0b7438e1e0cdedffbaf))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **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.12.0](https://github.com/ReVanced/revanced-patches/compare/v5.11.0...v5.12.0) (2025-02-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Allow changing default settings for existing app installs ([#4464](https://github.com/ReVanced/revanced-patches/issues/4464)) ([1bd7986](https://github.com/ReVanced/revanced-patches/commit/1bd7986823e774a929c8a9102a7cc96e245d5274))
|
||||||
|
* **Windy.app:** Remove obsolete `Unlock pro` patch ([#4428](https://github.com/ReVanced/revanced-patches/issues/4428)) ([83d116e](https://github.com/ReVanced/revanced-patches/commit/83d116e8fd3935ee431cfdf0b8e095d04ee77259))
|
||||||
|
* **YouTube - Spoof video streams:** Change default client to `Android TV` ([#4465](https://github.com/ReVanced/revanced-patches/issues/4465)) ([0412c79](https://github.com/ReVanced/revanced-patches/commit/0412c7901dc8599b6079d9c3ba26452f88af642b))
|
||||||
|
* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([a006758](https://github.com/ReVanced/revanced-patches/commit/a0067581d0f877e1b4eb1f888a25786f09676b2e))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Return YouTube Dislike:** add `Show estimated likes` setting ([#4443](https://github.com/ReVanced/revanced-patches/issues/4443)) ([9a88b42](https://github.com/ReVanced/revanced-patches/commit/9a88b4239fd63d5f91105fec8e7d59d318a5d09a))
|
||||||
|
* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([8f4883f](https://github.com/ReVanced/revanced-patches/commit/8f4883fc002420bfb4056401e23445c99e1d3fce))
|
||||||
|
* **YouTube Music:** Support version `8.05.50` ([#4439](https://github.com/ReVanced/revanced-patches/issues/4439)) ([b31fed9](https://github.com/ReVanced/revanced-patches/commit/b31fed98901fcda1bce6f05eb0de63280c689fa0))
|
||||||
|
* **YouTube Music:** Support version `8.05.51` ([128441e](https://github.com/ReVanced/revanced-patches/commit/128441e78bc0d096c3fc2f57782ab90c39c3ae4b))
|
||||||
|
|
||||||
# [5.12.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.6...v5.12.0-dev.7) (2025-02-16)
|
# [5.12.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.6...v5.12.0-dev.7) (2025-02-16)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -356,33 +356,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) {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -158,16 +158,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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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,21 @@ 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.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk = 33 // TODO: Update Swipe controls code to allow updating this to the latest sdk.
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 BackgroundPlaybackPatch {
|
public class BackgroundPlaybackPatch {
|
||||||
@@ -23,16 +23,7 @@ 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()) {
|
return !ShortsPlayerState.isOpen();
|
||||||
return true; // Definitely is not a Short.
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add better hook.
|
|
||||||
// Might be a Shorts, or might be a prior regular video on screen again after a Shorts was closed.
|
|
||||||
// This incorrectly prevents PIP if player is in WATCH_WHILE_MINIMIZED after closing a Shorts,
|
|
||||||
// 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -58,7 +55,6 @@ public class ShortsAutoplayPatch {
|
|||||||
/**
|
/**
|
||||||
* @return If the app is currently in background PiP mode.
|
* @return If the app is currently in background PiP mode.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
private static boolean isAppInBackgroundPiPMode() {
|
private static boolean isAppInBackgroundPiPMode() {
|
||||||
Activity activity = mainActivityRef.get();
|
Activity activity = mainActivityRef.get();
|
||||||
return activity != null && activity.isInPictureInPictureMode();
|
return activity != null && activity.isInPictureInPictureMode();
|
||||||
@@ -80,7 +76,6 @@ public class ShortsAutoplayPatch {
|
|||||||
/**
|
/**
|
||||||
* Injection point.
|
* Injection point.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
|
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
|
||||||
try {
|
try {
|
||||||
final boolean autoplay;
|
final boolean autoplay;
|
||||||
|
|||||||
@@ -7,13 +7,10 @@ import static app.revanced.extension.youtube.patches.announcements.requests.Anno
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.os.Build;
|
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -31,7 +28,6 @@ public final class AnnouncementsPatch {
|
|||||||
private AnnouncementsPatch() {
|
private AnnouncementsPatch() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
||||||
private static boolean isLatestAlready() throws IOException {
|
private static boolean isLatestAlready() throws IOException {
|
||||||
HttpURLConnection connection =
|
HttpURLConnection connection =
|
||||||
AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS);
|
AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS);
|
||||||
@@ -70,7 +66,6 @@ public final class AnnouncementsPatch {
|
|||||||
return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
|
return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
||||||
public static void showAnnouncement(final Activity context) {
|
public static void showAnnouncement(final Activity context) {
|
||||||
if (!Settings.ANNOUNCEMENTS.get()) return;
|
if (!Settings.ANNOUNCEMENTS.get()) return;
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -6,8 +6,12 @@ import app.revanced.extension.youtube.settings.Settings;
|
|||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
final class ButtonsFilter extends Filter {
|
final class ButtonsFilter extends Filter {
|
||||||
|
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
|
||||||
|
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
|
||||||
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml";
|
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml";
|
||||||
|
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
|
||||||
|
|
||||||
|
private final StringFilterGroup likeSubscribeGlow;
|
||||||
private final StringFilterGroup actionBarGroup;
|
private final StringFilterGroup actionBarGroup;
|
||||||
private final StringFilterGroup bufferFilterPathGroup;
|
private final StringFilterGroup bufferFilterPathGroup;
|
||||||
private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList();
|
private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList();
|
||||||
@@ -20,11 +24,19 @@ final class ButtonsFilter extends Filter {
|
|||||||
addIdentifierCallbacks(actionBarGroup);
|
addIdentifierCallbacks(actionBarGroup);
|
||||||
|
|
||||||
|
|
||||||
|
likeSubscribeGlow = new StringFilterGroup(
|
||||||
|
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
|
||||||
|
"animated_button_border.eml"
|
||||||
|
);
|
||||||
|
|
||||||
bufferFilterPathGroup = new StringFilterGroup(
|
bufferFilterPathGroup = new StringFilterGroup(
|
||||||
null,
|
null,
|
||||||
"|ContainerType|button.eml|"
|
"|ContainerType|button.eml|"
|
||||||
);
|
);
|
||||||
|
|
||||||
addPathCallbacks(
|
addPathCallbacks(
|
||||||
|
likeSubscribeGlow,
|
||||||
|
bufferFilterPathGroup,
|
||||||
new StringFilterGroup(
|
new StringFilterGroup(
|
||||||
Settings.HIDE_LIKE_DISLIKE_BUTTON,
|
Settings.HIDE_LIKE_DISLIKE_BUTTON,
|
||||||
"|segmented_like_dislike_button"
|
"|segmented_like_dislike_button"
|
||||||
@@ -40,8 +52,7 @@ final class ButtonsFilter extends Filter {
|
|||||||
new StringFilterGroup(
|
new StringFilterGroup(
|
||||||
Settings.HIDE_CLIP_BUTTON,
|
Settings.HIDE_CLIP_BUTTON,
|
||||||
"|clip_button.eml|"
|
"|clip_button.eml|"
|
||||||
),
|
)
|
||||||
bufferFilterPathGroup
|
|
||||||
);
|
);
|
||||||
|
|
||||||
bufferButtonsGroupList.addAll(
|
bufferButtonsGroupList.addAll(
|
||||||
@@ -83,6 +94,15 @@ final class ButtonsFilter extends Filter {
|
|||||||
@Override
|
@Override
|
||||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
|
if (matchedGroup == likeSubscribeGlow) {
|
||||||
|
if ((path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
|
||||||
|
&& path.contains(ANIMATED_VECTOR_TYPE_PATH)) {
|
||||||
|
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If the current matched group is the action bar group,
|
// If the current matched group is the action bar group,
|
||||||
// in case every filter group is enabled, hide the action bar.
|
// in case every filter group is enabled, hide the action bar.
|
||||||
if (matchedGroup == actionBarGroup) {
|
if (matchedGroup == actionBarGroup) {
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -44,13 +41,11 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
|
|||||||
return filterGroups.iterator();
|
return filterGroups.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
@Override
|
@Override
|
||||||
public void forEach(@NonNull Consumer<? super T> action) {
|
public void forEach(@NonNull Consumer<? super T> action) {
|
||||||
filterGroups.forEach(action);
|
filterGroups.forEach(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Spliterator<T> spliterator() {
|
public Spliterator<T> spliterator() {
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ import static app.revanced.extension.shared.StringRef.str;
|
|||||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||||
import static java.lang.Character.UnicodeBlock.*;
|
import static java.lang.Character.UnicodeBlock.*;
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -44,7 +41,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
|||||||
* - When using whole word syntax, some keywords may need additional pluralized variations.
|
* - When using whole word syntax, some keywords may need additional pluralized variations.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
final class KeywordContentFilter extends Filter {
|
final class KeywordContentFilter extends Filter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ package app.revanced.extension.youtube.patches.components;
|
|||||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
@@ -18,10 +16,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
|||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class LayoutComponentsFilter extends Filter {
|
public final class LayoutComponentsFilter extends Filter {
|
||||||
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
|
|
||||||
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
|
|
||||||
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
|
|
||||||
|
|
||||||
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch(
|
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch(
|
||||||
"V.ED", // Playlist browse id.
|
"V.ED", // Playlist browse id.
|
||||||
"java.lang.ref.WeakReference"
|
"java.lang.ref.WeakReference"
|
||||||
@@ -38,16 +32,15 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||||
private final StringFilterGroup inFeedSurvey;
|
private final StringFilterGroup inFeedSurvey;
|
||||||
private final StringFilterGroup notifyMe;
|
private final StringFilterGroup notifyMe;
|
||||||
|
private final StringFilterGroup singleItemInformationPanel;
|
||||||
private final StringFilterGroup expandableMetadata;
|
private final StringFilterGroup expandableMetadata;
|
||||||
private final ByteArrayFilterGroup searchResultRecommendations;
|
private final ByteArrayFilterGroup searchResultRecommendations;
|
||||||
private final StringFilterGroup searchResultVideo;
|
private final StringFilterGroup searchResultVideo;
|
||||||
private final StringFilterGroup compactChannelBarInner;
|
private final StringFilterGroup compactChannelBarInner;
|
||||||
private final StringFilterGroup compactChannelBarInnerButton;
|
private final StringFilterGroup compactChannelBarInnerButton;
|
||||||
private final ByteArrayFilterGroup joinMembershipButton;
|
private final ByteArrayFilterGroup joinMembershipButton;
|
||||||
private final StringFilterGroup likeSubscribeGlow;
|
|
||||||
private final StringFilterGroup horizontalShelves;
|
private final StringFilterGroup horizontalShelves;
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
public LayoutComponentsFilter() {
|
public LayoutComponentsFilter() {
|
||||||
exceptions.addPatterns(
|
exceptions.addPatterns(
|
||||||
"home_video_with_context",
|
"home_video_with_context",
|
||||||
@@ -105,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",
|
||||||
@@ -123,8 +121,12 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final var infoPanel = new StringFilterGroup(
|
final var infoPanel = new StringFilterGroup(
|
||||||
Settings.HIDE_HIDE_INFO_PANELS,
|
Settings.HIDE_INFO_PANELS,
|
||||||
"publisher_transparency_panel",
|
"publisher_transparency_panel"
|
||||||
|
);
|
||||||
|
|
||||||
|
singleItemInformationPanel = new StringFilterGroup(
|
||||||
|
Settings.HIDE_INFO_PANELS,
|
||||||
"single_item_information_panel"
|
"single_item_information_panel"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -217,10 +219,6 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
"sponsorships"
|
"sponsorships"
|
||||||
);
|
);
|
||||||
|
|
||||||
likeSubscribeGlow = new StringFilterGroup(
|
|
||||||
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
|
|
||||||
"animated_button_border.eml"
|
|
||||||
);
|
|
||||||
|
|
||||||
final var channelWatermark = new StringFilterGroup(
|
final var channelWatermark = new StringFilterGroup(
|
||||||
Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
|
Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
|
||||||
@@ -254,7 +252,6 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
expandableMetadata,
|
expandableMetadata,
|
||||||
inFeedSurvey,
|
inFeedSurvey,
|
||||||
notifyMe,
|
notifyMe,
|
||||||
likeSubscribeGlow,
|
|
||||||
compactChannelBar,
|
compactChannelBar,
|
||||||
communityPosts,
|
communityPosts,
|
||||||
paidPromotion,
|
paidPromotion,
|
||||||
@@ -269,8 +266,10 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
compactChannelBarInner,
|
compactChannelBarInner,
|
||||||
medicalPanel,
|
medicalPanel,
|
||||||
infoPanel,
|
infoPanel,
|
||||||
|
singleItemInformationPanel,
|
||||||
emergencyBox,
|
emergencyBox,
|
||||||
subscribersCommunityGuidelines,
|
subscribersCommunityGuidelines,
|
||||||
|
subscriptionsChipBar,
|
||||||
channelGuidelines,
|
channelGuidelines,
|
||||||
audioTrackButton,
|
audioTrackButton,
|
||||||
artistCard,
|
artistCard,
|
||||||
@@ -285,6 +284,19 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
@Override
|
@Override
|
||||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
|
// This identifier is used not only in players but also in search results:
|
||||||
|
// https://github.com/ReVanced/revanced-patches/issues/3245
|
||||||
|
// Until 2024, medical information panels such as Covid 19 also used this identifier and were shown in the search results.
|
||||||
|
// From 2025, the medical information panel is no longer shown in the search results.
|
||||||
|
// Therefore, this identifier does not filter when the search bar is activated.
|
||||||
|
if (matchedGroup == singleItemInformationPanel) {
|
||||||
|
if (PlayerType.getCurrent().isMaximizedOrFullscreen() || !NavigationBar.isSearchBarActive()) {
|
||||||
|
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (matchedGroup == searchResultVideo) {
|
if (matchedGroup == searchResultVideo) {
|
||||||
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
|
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
|
||||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||||
@@ -292,15 +304,6 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchedGroup == likeSubscribeGlow) {
|
|
||||||
if ((path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
|
|
||||||
&& path.contains(ANIMATED_VECTOR_TYPE_PATH)) {
|
|
||||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The groups are excluded from the filter due to the exceptions list below.
|
// The groups are excluded from the filter due to the exceptions list below.
|
||||||
// Filter them separately here.
|
// Filter them separately here.
|
||||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
||||||
@@ -459,6 +462,12 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not hide if the navigation back button is visible,
|
||||||
|
// otherwise the content shelves in the YouTube Movie/Courses pages is hidden.
|
||||||
|
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.
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package app.revanced.extension.youtube.patches.components;
|
package app.revanced.extension.youtube.patches.components;
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
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;
|
||||||
@@ -16,7 +13,6 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
|
|||||||
private final ByteArrayFilterGroup exception;
|
private final ByteArrayFilterGroup exception;
|
||||||
private final StringFilterGroup videoQualityMenuFooter;
|
private final StringFilterGroup videoQualityMenuFooter;
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
public PlayerFlyoutMenuItemsFilter() {
|
public PlayerFlyoutMenuItemsFilter() {
|
||||||
exception = new ByteArrayFilterGroup(
|
exception = new ByteArrayFilterGroup(
|
||||||
// Whitelist Quality menu item when "Hide Additional settings menu" is enabled
|
// Whitelist Quality menu item when "Hide Additional settings menu" is enabled
|
||||||
|
|||||||
@@ -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).
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,13 +352,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 +378,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,12 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
@@ -22,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;
|
||||||
@@ -66,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.
|
||||||
@@ -79,7 +81,6 @@ public class LicenseActivityHook {
|
|||||||
* <p>
|
* <p>
|
||||||
* Hooks LicenseActivity#onCreate in order to inject our own fragment.
|
* Hooks LicenseActivity#onCreate in order to inject our own fragment.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
public static void initialize(Activity licenseActivity) {
|
public static void initialize(Activity licenseActivity) {
|
||||||
try {
|
try {
|
||||||
ThemeHelper.setActivityTheme(licenseActivity);
|
ThemeHelper.setActivityTheme(licenseActivity);
|
||||||
@@ -119,15 +120,13 @@ public class LicenseActivityHook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
@SuppressLint("UseCompatLoadingForDrawables")
|
@SuppressLint("UseCompatLoadingForDrawables")
|
||||||
private static void createToolbar(Activity activity, String toolbarTitleResourceName) {
|
private static void createToolbar(Activity activity, String toolbarTitleResourceName) {
|
||||||
// Replace dummy placeholder toolbar.
|
// Replace dummy placeholder toolbar.
|
||||||
// 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);
|
||||||
|
|
||||||
|
|||||||
@@ -47,11 +47,14 @@ 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);
|
||||||
@@ -124,9 +127,7 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
||||||
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
public static final BooleanSetting 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_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
|
|
||||||
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);
|
||||||
@@ -136,8 +137,9 @@ 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_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);
|
||||||
public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE);
|
public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE);
|
public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE);
|
||||||
@@ -172,10 +174,10 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_summary", FALSE);
|
public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_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_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);
|
||||||
@@ -185,6 +187,7 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
|
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
|
||||||
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
|
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
|
||||||
// Action buttons
|
// Action buttons
|
||||||
|
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
|
||||||
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
|
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
|
||||||
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
|
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
|
||||||
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
|
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
|
||||||
@@ -228,7 +231,9 @@ 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 SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true,
|
||||||
|
"revanced_switch_create_with_notifications_button_user_dialog_message");
|
||||||
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true);
|
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true);
|
||||||
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 +287,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));
|
||||||
|
|
||||||
@@ -306,20 +310,19 @@ public class Settings extends BaseSettings {
|
|||||||
|
|
||||||
// Swipe controls
|
// Swipe controls
|
||||||
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
|
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
|
||||||
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE);
|
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE, true);
|
||||||
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE);
|
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE, true);
|
||||||
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
|
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
|
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
|
||||||
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_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 50, 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));
|
||||||
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
|
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
|
||||||
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
// Debugging
|
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
|
||||||
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true,
|
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
@@ -382,9 +385,13 @@ public class Settings extends BaseSettings {
|
|||||||
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");
|
||||||
|
|
||||||
// Deprecated migrations
|
// Deprecated migrations
|
||||||
public static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024
|
private static final StringSetting DEPRECATED_SB_UUID_OLD_MIGRATION_SETTING = new StringSetting("uuid", ""); // Delete sometime in 2024
|
||||||
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true);
|
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_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
|
||||||
@@ -403,11 +410,9 @@ public class Settings extends BaseSettings {
|
|||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -3,18 +3,14 @@ package app.revanced.extension.youtube.settings.preference;
|
|||||||
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows using basic html for the summary text.
|
* Allows using basic html for the summary text.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unused", "deprecation"})
|
@SuppressWarnings({"unused", "deprecation"})
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
||||||
public class HtmlPreference extends Preference {
|
public class HtmlPreference extends Preference {
|
||||||
{
|
{
|
||||||
setSummary(Html.fromHtml(getSummary().toString(), FROM_HTML_MODE_COMPACT));
|
setSummary(Html.fromHtml(getSummary().toString(), FROM_HTML_MODE_COMPACT));
|
||||||
|
|||||||
@@ -17,11 +17,9 @@ import android.view.WindowInsets;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
@@ -76,7 +74,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];
|
||||||
@@ -98,7 +97,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
|||||||
listPreference.setEntryValues(sortedEntryValues);
|
listPreference.setEntryValues(sortedEntryValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
|
||||||
@Override
|
@Override
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
super.initialize();
|
super.initialize();
|
||||||
@@ -112,6 +110,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) {
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ 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.os.Build;
|
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;
|
||||||
import androidx.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -26,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.
|
||||||
*/
|
*/
|
||||||
@@ -39,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).
|
||||||
@@ -49,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.
|
||||||
//
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -257,7 +288,6 @@ public final class NavigationBar {
|
|||||||
* Injection point.
|
* Injection point.
|
||||||
* Fixes missing drawable.
|
* Fixes missing drawable.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
|
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
|
||||||
if (fillBellCairoBlack != 0) {
|
if (fillBellCairoBlack != 0) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,19 +20,17 @@ class SwipeControlsConfigurationProvider(
|
|||||||
* should swipe controls be enabled? (global setting)
|
* should swipe controls be enabled? (global setting)
|
||||||
*/
|
*/
|
||||||
val enableSwipeControls: Boolean
|
val enableSwipeControls: Boolean
|
||||||
get() = isFullscreenVideo && (enableVolumeControls || enableBrightnessControl)
|
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should swipe controls for volume be enabled?
|
* should swipe controls for volume be enabled?
|
||||||
*/
|
*/
|
||||||
val enableVolumeControls: Boolean
|
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
|
||||||
get() = Settings.SWIPE_VOLUME.get()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should swipe controls for volume be enabled?
|
* should swipe controls for volume be enabled?
|
||||||
*/
|
*/
|
||||||
val enableBrightnessControl: Boolean
|
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
|
||||||
get() = Settings.SWIPE_BRIGHTNESS.get()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is the video player currently in fullscreen mode?
|
* is the video player currently in fullscreen mode?
|
||||||
@@ -46,7 +44,7 @@ class SwipeControlsConfigurationProvider(
|
|||||||
* should volume key controls be overwritten? (global setting)
|
* should volume key controls be overwritten? (global setting)
|
||||||
*/
|
*/
|
||||||
val overwriteVolumeKeyControls: Boolean
|
val overwriteVolumeKeyControls: Boolean
|
||||||
get() = isFullscreenVideo && enableVolumeControls
|
get() = enableVolumeControls && isFullscreenVideo
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region gesture adjustments
|
//region gesture adjustments
|
||||||
@@ -65,7 +63,6 @@ class SwipeControlsConfigurationProvider(
|
|||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region overlay adjustments
|
//region overlay adjustments
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should the overlay enable haptic feedback?
|
* should the overlay enable haptic feedback?
|
||||||
*/
|
*/
|
||||||
@@ -79,15 +76,10 @@ class SwipeControlsConfigurationProvider(
|
|||||||
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
|
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* text size for the overlay, in sp
|
* Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency.
|
||||||
|
* If the opacity value is out of range, it resets to the default and displays a warning message.
|
||||||
*/
|
*/
|
||||||
val overlayTextSize: Int
|
val overlayBackgroundOpacity: Int
|
||||||
get() = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the background color for text on the overlay, as a color int
|
|
||||||
*/
|
|
||||||
val overlayTextBackgroundColor: Int
|
|
||||||
get() {
|
get() {
|
||||||
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
|
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
|
||||||
|
|
||||||
@@ -102,11 +94,34 @@ class SwipeControlsConfigurationProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the foreground color for text on the overlay, as a color int
|
* The color of the progress overlay.
|
||||||
*/
|
*/
|
||||||
val overlayForegroundColor: Int
|
val overlayProgressColor: Int
|
||||||
|
get() = 0xBFFFFFFF.toInt()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for the background of the progress overlay fill.
|
||||||
|
*/
|
||||||
|
val overlayFillBackgroundPaint: Int
|
||||||
|
get() = 0x80D3D3D3.toInt()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used for the text and icons in the overlay.
|
||||||
|
*/
|
||||||
|
val overlayTextColor: Int
|
||||||
get() = Color.WHITE
|
get() = Color.WHITE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag that determines if the overlay should only show the icon.
|
||||||
|
*/
|
||||||
|
val overlayShowOverlayMinimalStyle: Boolean
|
||||||
|
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag that determines if the progress bar should be circular.
|
||||||
|
*/
|
||||||
|
val isCircularProgressBar: Boolean
|
||||||
|
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get()
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region behaviour
|
//region behaviour
|
||||||
|
|||||||
@@ -82,11 +82,15 @@ abstract class BaseGestureController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onScroll(
|
override fun onScroll(
|
||||||
from: MotionEvent,
|
from: MotionEvent?,
|
||||||
to: MotionEvent,
|
to: MotionEvent,
|
||||||
distanceX: Float,
|
distanceX: Float,
|
||||||
distanceY: Float,
|
distanceY: Float,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
if (from == null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// submit to swipe detector
|
// submit to swipe detector
|
||||||
submitForSwipe(from, to, distanceX, distanceY)
|
submitForSwipe(from, to, distanceX, distanceY)
|
||||||
|
|
||||||
|
|||||||
@@ -1,138 +1,145 @@
|
|||||||
package app.revanced.extension.youtube.swipecontrols.views
|
package app.revanced.extension.youtube.swipecontrols.views
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.RectF
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.GradientDrawable
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.TypedValue
|
import android.util.AttributeSet
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import android.widget.TextView
|
|
||||||
import app.revanced.extension.shared.StringRef.str
|
|
||||||
import app.revanced.extension.shared.Utils
|
import app.revanced.extension.shared.Utils
|
||||||
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
|
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
|
||||||
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
|
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
|
||||||
import app.revanced.extension.youtube.swipecontrols.misc.applyDimension
|
import kotlin.math.min
|
||||||
import kotlin.math.round
|
import kotlin.math.round
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main overlay layout for volume and brightness swipe controls
|
* Main overlay layout for displaying volume and brightness level with both circular and horizontal progress bars.
|
||||||
*
|
|
||||||
* @param context context to create in
|
|
||||||
*/
|
*/
|
||||||
class SwipeControlsOverlayLayout(
|
class SwipeControlsOverlayLayout(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val config: SwipeControlsConfigurationProvider,
|
private val config: SwipeControlsConfigurationProvider,
|
||||||
) : RelativeLayout(context), SwipeControlsOverlay {
|
) : RelativeLayout(context), SwipeControlsOverlay {
|
||||||
/**
|
|
||||||
* DO NOT use this, for tools only
|
|
||||||
*/
|
|
||||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
|
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
|
||||||
|
|
||||||
private val feedbackTextView: TextView
|
// Drawable icons for brightness and volume
|
||||||
private val autoBrightnessIcon: Drawable
|
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
||||||
private val manualBrightnessIcon: Drawable
|
private val lowBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_low")
|
||||||
private val mutedVolumeIcon: Drawable
|
private val mediumBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_medium")
|
||||||
private val normalVolumeIcon: Drawable
|
private val highBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_high")
|
||||||
|
private val fullBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_full")
|
||||||
|
private val mutedVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_mute")
|
||||||
|
private val lowVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_low")
|
||||||
|
private val normalVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_normal")
|
||||||
|
private val fullVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_high")
|
||||||
|
|
||||||
private fun getDrawable(name: String, width: Int, height: Int): Drawable {
|
// Function to retrieve drawable resources by name
|
||||||
return resources.getDrawable(
|
private fun getDrawable(name: String): Drawable {
|
||||||
|
val drawable = resources.getDrawable(
|
||||||
Utils.getResourceIdentifier(context, name, "drawable"),
|
Utils.getResourceIdentifier(context, name, "drawable"),
|
||||||
context.theme,
|
context.theme,
|
||||||
).apply {
|
|
||||||
setTint(config.overlayForegroundColor)
|
|
||||||
setBounds(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
)
|
)
|
||||||
|
drawable.setTint(config.overlayTextColor)
|
||||||
|
return drawable
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Initialize progress bars
|
||||||
|
private val circularProgressView: CircularProgressView
|
||||||
|
private val horizontalProgressView: HorizontalProgressView
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// init views
|
// Initialize circular progress bar
|
||||||
val feedbackTextViewPadding = 2.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP)
|
circularProgressView = CircularProgressView(
|
||||||
val compoundIconPadding = 4.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP)
|
context,
|
||||||
feedbackTextView = TextView(context).apply {
|
config.overlayBackgroundOpacity,
|
||||||
layoutParams = LayoutParams(
|
config.overlayShowOverlayMinimalStyle,
|
||||||
LayoutParams.WRAP_CONTENT,
|
config.overlayProgressColor,
|
||||||
LayoutParams.WRAP_CONTENT,
|
config.overlayFillBackgroundPaint,
|
||||||
|
config.overlayTextColor
|
||||||
).apply {
|
).apply {
|
||||||
|
layoutParams = LayoutParams(300, 300).apply {
|
||||||
addRule(CENTER_IN_PARENT, TRUE)
|
addRule(CENTER_IN_PARENT, TRUE)
|
||||||
setPadding(
|
|
||||||
feedbackTextViewPadding,
|
|
||||||
feedbackTextViewPadding,
|
|
||||||
feedbackTextViewPadding,
|
|
||||||
feedbackTextViewPadding,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
background = GradientDrawable().apply {
|
visibility = GONE // Initially hidden
|
||||||
cornerRadius = 8f
|
|
||||||
setColor(config.overlayTextBackgroundColor)
|
|
||||||
}
|
}
|
||||||
setTextColor(config.overlayForegroundColor)
|
addView(circularProgressView)
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, config.overlayTextSize.toFloat())
|
|
||||||
compoundDrawablePadding = compoundIconPadding
|
|
||||||
visibility = GONE
|
|
||||||
}
|
|
||||||
addView(feedbackTextView)
|
|
||||||
|
|
||||||
// get icons scaled, assuming square icons
|
// Initialize horizontal progress bar
|
||||||
val iconHeight = round(feedbackTextView.lineHeight * .8).toInt()
|
val screenWidth = resources.displayMetrics.widthPixels
|
||||||
autoBrightnessIcon = getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
|
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
|
||||||
manualBrightnessIcon = getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
|
horizontalProgressView = HorizontalProgressView(
|
||||||
mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight)
|
context,
|
||||||
normalVolumeIcon = getDrawable("revanced_ic_sc_volume_normal", iconHeight, iconHeight)
|
config.overlayBackgroundOpacity,
|
||||||
|
config.overlayShowOverlayMinimalStyle,
|
||||||
|
config.overlayProgressColor,
|
||||||
|
config.overlayFillBackgroundPaint,
|
||||||
|
config.overlayTextColor
|
||||||
|
).apply {
|
||||||
|
layoutParams = LayoutParams(layoutWidth, 100).apply {
|
||||||
|
addRule(CENTER_HORIZONTAL)
|
||||||
|
topMargin = 40 // Top margin
|
||||||
|
}
|
||||||
|
visibility = GONE // Initially hidden
|
||||||
|
}
|
||||||
|
addView(horizontalProgressView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler and callback for hiding progress bars
|
||||||
private val feedbackHideHandler = Handler(Looper.getMainLooper())
|
private val feedbackHideHandler = Handler(Looper.getMainLooper())
|
||||||
private val feedbackHideCallback = Runnable {
|
private val feedbackHideCallback = Runnable {
|
||||||
feedbackTextView.visibility = GONE
|
circularProgressView.visibility = GONE
|
||||||
|
horizontalProgressView.visibility = GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* show the feedback view for a given time
|
* Displays the progress bar with the appropriate value, icon, and type (brightness or volume).
|
||||||
*
|
|
||||||
* @param message the message to show
|
|
||||||
* @param icon the icon to use
|
|
||||||
*/
|
*/
|
||||||
private fun showFeedbackView(message: String, icon: Drawable) {
|
private fun showFeedbackView(value: String, progress: Int, max: Int, icon: Drawable, isBrightness: Boolean) {
|
||||||
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
|
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
|
||||||
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
|
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
|
||||||
feedbackTextView.apply {
|
|
||||||
text = message
|
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
|
||||||
setCompoundDrawablesRelative(
|
viewToShow.apply {
|
||||||
icon,
|
setProgress(progress, max, value, isBrightness)
|
||||||
null,
|
this.icon = icon
|
||||||
null,
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
visibility = VISIBLE
|
visibility = VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle volume change
|
||||||
override fun onVolumeChanged(newVolume: Int, maximumVolume: Int) {
|
override fun onVolumeChanged(newVolume: Int, maximumVolume: Int) {
|
||||||
showFeedbackView(
|
val volumePercentage = (newVolume.toFloat() / maximumVolume) * 100
|
||||||
"$newVolume",
|
val icon = when {
|
||||||
if (newVolume > 0) normalVolumeIcon else mutedVolumeIcon,
|
newVolume == 0 -> mutedVolumeIcon
|
||||||
)
|
volumePercentage < 33 -> lowVolumeIcon
|
||||||
|
volumePercentage < 66 -> normalVolumeIcon
|
||||||
|
else -> fullVolumeIcon
|
||||||
|
}
|
||||||
|
showFeedbackView("$newVolume", newVolume, maximumVolume, icon, isBrightness = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle brightness change
|
||||||
override fun onBrightnessChanged(brightness: Double) {
|
override fun onBrightnessChanged(brightness: Double) {
|
||||||
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
|
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
|
||||||
showFeedbackView(
|
showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
|
||||||
str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text"),
|
} else {
|
||||||
autoBrightnessIcon,
|
val brightnessValue = round(brightness).toInt()
|
||||||
)
|
val icon = when {
|
||||||
} else if (brightness >= 0) {
|
brightnessValue < 25 -> lowBrightnessIcon
|
||||||
showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon)
|
brightnessValue < 50 -> mediumBrightnessIcon
|
||||||
|
brightnessValue < 75 -> highBrightnessIcon
|
||||||
|
else -> fullBrightnessIcon
|
||||||
|
}
|
||||||
|
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Begin swipe session
|
||||||
override fun onEnterSwipeSession() {
|
override fun onEnterSwipeSession() {
|
||||||
if (config.shouldEnableHapticFeedback) {
|
if (config.shouldEnableHapticFeedback) {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@@ -143,3 +150,229 @@ class SwipeControlsOverlayLayout(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for progress views.
|
||||||
|
*/
|
||||||
|
abstract class AbstractProgressView(
|
||||||
|
context: Context,
|
||||||
|
protected val overlayBackgroundOpacity: Int,
|
||||||
|
protected val overlayShowOverlayMinimalStyle: Boolean,
|
||||||
|
protected val overlayProgressColor: Int,
|
||||||
|
protected val overlayFillBackgroundPaint: Int,
|
||||||
|
protected val overlayTextColor: Int,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : View(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
// Combined paint creation function for both fill and stroke styles
|
||||||
|
private fun createPaint(color: Int, style: Paint.Style = Paint.Style.FILL, strokeCap: Paint.Cap = Paint.Cap.BUTT, strokeWidth: Float = 0f) = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
this.style = style
|
||||||
|
this.color = color
|
||||||
|
this.strokeCap = strokeCap
|
||||||
|
this.strokeWidth = strokeWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize paints
|
||||||
|
public val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
|
||||||
|
public val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
|
||||||
|
public val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
|
||||||
|
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
color = overlayTextColor
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
textSize = 40f // Can adjust based on need
|
||||||
|
}
|
||||||
|
|
||||||
|
protected var progress = 0
|
||||||
|
protected var maxProgress = 100
|
||||||
|
protected var displayText: String = "0"
|
||||||
|
protected var isBrightness = true
|
||||||
|
public var icon: Drawable? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
// Stroke widths are now set in createPaint for progressPaint and fillBackgroundPaint
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||||
|
progress = value
|
||||||
|
maxProgress = max
|
||||||
|
displayText = text
|
||||||
|
isBrightness = isBrightnessMode
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
// Base class implementation can be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom view for rendering a circular progress indicator with icons and text.
|
||||||
|
*/
|
||||||
|
class CircularProgressView(
|
||||||
|
context: Context,
|
||||||
|
overlayBackgroundOpacity: Int,
|
||||||
|
overlayShowOverlayMinimalStyle: Boolean,
|
||||||
|
overlayProgressColor: Int,
|
||||||
|
overlayFillBackgroundPaint: Int,
|
||||||
|
overlayTextColor: Int,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : AbstractProgressView(
|
||||||
|
context,
|
||||||
|
overlayBackgroundOpacity,
|
||||||
|
overlayShowOverlayMinimalStyle,
|
||||||
|
overlayProgressColor,
|
||||||
|
overlayFillBackgroundPaint,
|
||||||
|
overlayTextColor,
|
||||||
|
attrs,
|
||||||
|
defStyleAttr
|
||||||
|
) {
|
||||||
|
private val rectF = RectF()
|
||||||
|
|
||||||
|
init {
|
||||||
|
textPaint.textSize = 40f // Override default text size for circular view
|
||||||
|
progressPaint.strokeWidth = 20f
|
||||||
|
fillBackgroundPaint.strokeWidth = 20f
|
||||||
|
progressPaint.strokeCap = Paint.Cap.ROUND
|
||||||
|
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
|
||||||
|
progressPaint.style = Paint.Style.STROKE
|
||||||
|
fillBackgroundPaint.style = Paint.Style.STROKE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
|
||||||
|
val size = min(width, height).toFloat()
|
||||||
|
rectF.set(20f, 20f, size - 20f, size - 20f)
|
||||||
|
|
||||||
|
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
|
||||||
|
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
|
||||||
|
|
||||||
|
// Select the paint for drawing based on whether it's brightness or volume.
|
||||||
|
val sweepAngle = (progress.toFloat() / maxProgress) * 360
|
||||||
|
canvas.drawArc(rectF, -90f, sweepAngle, false, progressPaint) // Draw the progress arc.
|
||||||
|
|
||||||
|
// Draw the icon in the center.
|
||||||
|
icon?.let {
|
||||||
|
val iconSize = if (overlayShowOverlayMinimalStyle) 100 else 80
|
||||||
|
val iconX = (width - iconSize) / 2
|
||||||
|
val iconY = (height / 2) - if (overlayShowOverlayMinimalStyle) 50 else 80
|
||||||
|
it.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
|
||||||
|
it.draw(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not a minimal style mode, draw the text inside the ring.
|
||||||
|
if (!overlayShowOverlayMinimalStyle) {
|
||||||
|
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom view for rendering a rectangular progress bar with icons and text.
|
||||||
|
*/
|
||||||
|
class HorizontalProgressView(
|
||||||
|
context: Context,
|
||||||
|
overlayBackgroundOpacity: Int,
|
||||||
|
overlayShowOverlayMinimalStyle: Boolean,
|
||||||
|
overlayProgressColor: Int,
|
||||||
|
overlayFillBackgroundPaint: Int,
|
||||||
|
overlayTextColor: Int,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : AbstractProgressView(
|
||||||
|
context,
|
||||||
|
overlayBackgroundOpacity,
|
||||||
|
overlayShowOverlayMinimalStyle,
|
||||||
|
overlayProgressColor,
|
||||||
|
overlayFillBackgroundPaint,
|
||||||
|
overlayTextColor,
|
||||||
|
attrs,
|
||||||
|
defStyleAttr
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val iconSize = 60f
|
||||||
|
private val padding = 40f
|
||||||
|
|
||||||
|
init {
|
||||||
|
textPaint.textSize = 36f // Override default text size for horizontal view
|
||||||
|
progressPaint.strokeWidth = 0f
|
||||||
|
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||||
|
progressPaint.style = Paint.Style.FILL
|
||||||
|
fillBackgroundPaint.style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
|
||||||
|
val width = width.toFloat()
|
||||||
|
val height = height.toFloat()
|
||||||
|
|
||||||
|
// Radius for rounded corners
|
||||||
|
val cornerRadius = min(width, height) / 2
|
||||||
|
|
||||||
|
// Calculate the total width for the elements
|
||||||
|
val minimalElementWidth = 5 * padding + iconSize
|
||||||
|
|
||||||
|
// Calculate the starting point (X) to center the elements
|
||||||
|
val minimalStartX = (width - minimalElementWidth) / 2
|
||||||
|
|
||||||
|
// Draw the background
|
||||||
|
if (!overlayShowOverlayMinimalStyle) {
|
||||||
|
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
|
||||||
|
} else {
|
||||||
|
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overlayShowOverlayMinimalStyle) {
|
||||||
|
// Draw the fill background
|
||||||
|
val startX = 2 * padding + iconSize
|
||||||
|
val endX = width - 4 * padding
|
||||||
|
val fillWidth = endX - startX
|
||||||
|
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
startX,
|
||||||
|
height / 2 - 5f,
|
||||||
|
endX,
|
||||||
|
height / 2 + 5f,
|
||||||
|
10f, 10f,
|
||||||
|
fillBackgroundPaint
|
||||||
|
)
|
||||||
|
|
||||||
|
// Draw the progress
|
||||||
|
val progressWidth = (progress.toFloat() / maxProgress) * fillWidth
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
startX,
|
||||||
|
height / 2 - 5f,
|
||||||
|
startX + progressWidth,
|
||||||
|
height / 2 + 5f,
|
||||||
|
10f, 10f,
|
||||||
|
progressPaint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the icon
|
||||||
|
icon?.let {
|
||||||
|
val iconX = if (!overlayShowOverlayMinimalStyle) {
|
||||||
|
padding
|
||||||
|
} else {
|
||||||
|
padding + minimalStartX
|
||||||
|
}
|
||||||
|
val iconY = height / 2 - iconSize / 2
|
||||||
|
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
|
||||||
|
it.draw(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the text on the right
|
||||||
|
val textX = if (!overlayShowOverlayMinimalStyle) {
|
||||||
|
width - 2 * padding
|
||||||
|
} else {
|
||||||
|
minimalStartX + minimalElementWidth - 2 * padding
|
||||||
|
}
|
||||||
|
val textY = height / 2 + textPaint.textSize / 3
|
||||||
|
|
||||||
|
// Draw the text
|
||||||
|
canvas.drawText(displayText, textX, textY, textPaint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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(
|
* Injection point.
|
||||||
viewGroup,
|
*/
|
||||||
|
public static void initializeButton(View controlsView) {
|
||||||
|
try {
|
||||||
|
instance = new PlayerControlButton(
|
||||||
|
controlsView,
|
||||||
"revanced_copy_video_url_button",
|
"revanced_copy_video_url_button",
|
||||||
Settings.COPY_VIDEO_URL,
|
"revanced_copy_video_url_button_placeholder",
|
||||||
|
Settings.COPY_VIDEO_URL::get,
|
||||||
view -> CopyVideoUrlPatch.copyUrl(false),
|
view -> CopyVideoUrlPatch.copyUrl(false),
|
||||||
view -> {
|
view -> {
|
||||||
CopyVideoUrlPatch.copyUrl(true);
|
CopyVideoUrlPatch.copyUrl(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
*/
|
|
||||||
public static void initializeButton(View view) {
|
|
||||||
try {
|
|
||||||
instance = new CopyVideoUrlButton((ViewGroup) view);
|
|
||||||
} 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(
|
* Injection point.
|
||||||
bottomControlsViewGroup,
|
*/
|
||||||
|
public static void initializeButton(View controlsView) {
|
||||||
|
try {
|
||||||
|
instance = new PlayerControlButton(
|
||||||
|
controlsView,
|
||||||
"revanced_copy_video_url_timestamp_button",
|
"revanced_copy_video_url_timestamp_button",
|
||||||
Settings.COPY_VIDEO_URL_TIMESTAMP,
|
"revanced_copy_video_url_timestamp_button_placeholder",
|
||||||
|
Settings.COPY_VIDEO_URL_TIMESTAMP::get,
|
||||||
view -> CopyVideoUrlPatch.copyUrl(true),
|
view -> CopyVideoUrlPatch.copyUrl(true),
|
||||||
view -> {
|
view -> {
|
||||||
CopyVideoUrlPatch.copyUrl(false);
|
CopyVideoUrlPatch.copyUrl(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
*/
|
|
||||||
public static void initializeButton(View bottomControlsViewGroup) {
|
|
||||||
try {
|
|
||||||
instance = new CopyVideoUrlTimestampButton((ViewGroup) bottomControlsViewGroup);
|
|
||||||
} 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;
|
||||||
|
|
||||||
|
View placeholder = placeHolderRef.get();
|
||||||
|
final boolean shouldBeShown = visibilityCheck.shouldBeShown();
|
||||||
|
|
||||||
|
if (visible && shouldBeShown) {
|
||||||
|
button.clearAnimation();
|
||||||
|
if (animated) {
|
||||||
|
button.startAnimation(PlayerControlButton.fadeInAnimation);
|
||||||
|
}
|
||||||
|
button.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
if (placeholder != null) {
|
||||||
|
placeholder.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visible && setting.get()) {
|
View button = buttonRef.get();
|
||||||
iView.clearAnimation();
|
if (button == null) return;
|
||||||
if (animated) {
|
|
||||||
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
|
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);
|
||||||
}
|
}
|
||||||
iView.setVisibility(View.VISIBLE);
|
} else {
|
||||||
} else if (iView.getVisibility() == View.VISIBLE) {
|
button.setVisibility(View.GONE);
|
||||||
iView.clearAnimation();
|
if (placeholder != null) placeholder.setVisibility(View.GONE);
|
||||||
if (animated) {
|
|
||||||
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
|
|
||||||
}
|
}
|
||||||
iView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "setVisibility failure", ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.12.0-dev.7
|
version = 5.14.0-dev.8
|
||||||
|
|||||||
@@ -348,6 +348,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;
|
||||||
}
|
}
|
||||||
@@ -768,8 +776,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;)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;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 {
|
||||||
@@ -1116,6 +1124,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;
|
||||||
}
|
}
|
||||||
@@ -1451,6 +1463,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
|||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
|
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
|
||||||
|
|
||||||
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.
|
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package app.revanced.patches.nunl.ads
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
internal val jwUtilCreateAdvertisementFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||||
|
custom { methodDef, classDef ->
|
||||||
|
classDef.type == "Lnl/sanomamedia/android/nu/video/util/JWUtil;" && methodDef.name == "createAdvertising"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val screenMapperFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("Lnl/nu/android/bff/domain/models/screen/ScreenEntity;")
|
||||||
|
parameters("Lnl/nu/performance/api/client/objects/Screen;")
|
||||||
|
|
||||||
|
opcodes(
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.CHECK_CAST
|
||||||
|
)
|
||||||
|
|
||||||
|
custom { methodDef, classDef ->
|
||||||
|
classDef.type == "Lnl/nu/android/bff/data/mappers/ScreenMapper;" && methodDef.name == "map"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val nextPageRepositoryImplFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
|
||||||
|
returns("Lnl/nu/android/bff/domain/models/Page;")
|
||||||
|
parameters("Lnl/nu/performance/api/client/PacResponse;", "Ljava/lang/String;")
|
||||||
|
|
||||||
|
opcodes(
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.CHECK_CAST
|
||||||
|
)
|
||||||
|
|
||||||
|
custom { methodDef, classDef ->
|
||||||
|
classDef.type == "Lnl/nu/android/bff/data/repositories/NextPageRepositoryImpl;" && methodDef.name == "mapToPage"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package app.revanced.patches.nunl.ads
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val hideAdsPatch = bytecodePatch(
|
||||||
|
name = "Hide ads",
|
||||||
|
description = "Hide ads and sponsored articles in list pages and remove pre-roll ads on videos.",
|
||||||
|
) {
|
||||||
|
compatibleWith("nl.sanomamedia.android.nu"("11.0.0", "11.0.1", "11.1.0"))
|
||||||
|
|
||||||
|
dependsOn(sharedExtensionPatch("nunl", mainActivityOnCreateHook))
|
||||||
|
|
||||||
|
execute {
|
||||||
|
// Disable video pre-roll ads.
|
||||||
|
// Whenever the app tries to create an ad via JWUtils.createAdvertising, don't actually tell the underlying JWPlayer library to do so => JWPlayer will not display ads.
|
||||||
|
jwUtilCreateAdvertisementFingerprint.method.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
new-instance v0, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;
|
||||||
|
invoke-direct { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;-><init>()V
|
||||||
|
invoke-virtual { v0 }, Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig${'$'}Builder;->build()Lcom/jwplayer/pub/api/configuration/ads/VastAdvertisingConfig;
|
||||||
|
move-result-object v0
|
||||||
|
return-object v0
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter injected content from API calls out of lists.
|
||||||
|
arrayOf(screenMapperFingerprint, nextPageRepositoryImplFingerprint).forEach {
|
||||||
|
// Index of instruction moving result of BlockPage;->getBlocks(...).
|
||||||
|
val moveGetBlocksResultObjectIndex = it.patternMatch!!.startIndex
|
||||||
|
it.method.apply {
|
||||||
|
val moveInstruction = getInstruction<OneRegisterInstruction>(moveGetBlocksResultObjectIndex)
|
||||||
|
|
||||||
|
val listRegister = moveInstruction.registerA
|
||||||
|
|
||||||
|
// Add instruction after moving List<Block> to register and then filter this List<Block> in place.
|
||||||
|
addInstructions(
|
||||||
|
moveGetBlocksResultObjectIndex + 1,
|
||||||
|
"""
|
||||||
|
invoke-static { v$listRegister }, Lapp/revanced/extension/nunl/ads/HideAdsPatch;->filterAds(Ljava/util/List;)V
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package app.revanced.patches.nunl.ads
|
||||||
|
|
||||||
|
import app.revanced.patches.shared.misc.extension.extensionHook
|
||||||
|
|
||||||
|
internal val mainActivityOnCreateHook = extensionHook {
|
||||||
|
custom { method, classDef ->
|
||||||
|
classDef.type == "Lnl/sanomamedia/android/nu/main/NUMainActivity;" && method.name == "onCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package app.revanced.patches.nunl.firebase
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal val getFingerprintHashForPackageFingerprints = arrayOf(
|
||||||
|
"Lcom/google/firebase/installations/remote/FirebaseInstallationServiceClient;",
|
||||||
|
"Lcom/google/firebase/remoteconfig/internal/ConfigFetchHttpClient;",
|
||||||
|
"Lcom/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient;"
|
||||||
|
).map { className ->
|
||||||
|
fingerprint {
|
||||||
|
accessFlags(AccessFlags.PRIVATE)
|
||||||
|
parameters()
|
||||||
|
returns("Ljava/lang/String;")
|
||||||
|
|
||||||
|
custom { methodDef, classDef ->
|
||||||
|
classDef.type == className && methodDef.name == "getFingerprintHashForPackage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package app.revanced.patches.nunl.firebase
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val spoofCertificatePatch = bytecodePatch(
|
||||||
|
name = "Spoof certificate",
|
||||||
|
description = "Spoofs the X-Android-Cert header to allow push messages.",
|
||||||
|
) {
|
||||||
|
compatibleWith("nl.sanomamedia.android.nu")
|
||||||
|
|
||||||
|
execute {
|
||||||
|
getFingerprintHashForPackageFingerprints.forEach { fingerprint ->
|
||||||
|
fingerprint.method.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
const-string v0, "eae41fc018df2731a9b6ae1ac327da44a288667b"
|
||||||
|
return-object v0
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
package app.revanced.patches.reddit.customclients.boostforreddit.api
|
package app.revanced.patches.reddit.customclients.boostforreddit.api
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
import app.revanced.patches.reddit.customclients.spoofClientPatch
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption ->
|
val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com") { clientIdOption ->
|
||||||
compatibleWith("com.rubenmayayo.reddit")
|
compatibleWith("com.rubenmayayo.reddit")
|
||||||
@@ -23,14 +26,15 @@ val spoofClientPatch = spoofClientPatch(redirectUri = "http://rubenmayayo.com")
|
|||||||
|
|
||||||
// region Patch user agent.
|
// region Patch user agent.
|
||||||
|
|
||||||
// Use a random number as the platform in the user agent string.
|
// Use a random user agent.
|
||||||
val platformName = (0..100000).random()
|
val randomName = (0..100000).random()
|
||||||
val platformParameter = 0
|
val userAgent = "$randomName:app.revanced.$randomName:v1.0.0 (by /u/revanced)"
|
||||||
|
buildUserAgentFingerprint.let {
|
||||||
|
val userAgentTemplateIndex = it.stringMatches!!.first().index
|
||||||
|
val register = it.method.getInstruction<OneRegisterInstruction>(userAgentTemplateIndex).registerA
|
||||||
|
|
||||||
buildUserAgentFingerprint.method.addInstructions(
|
it.method.replaceInstruction(userAgentTemplateIndex, "const-string v$register, \"$userAgent\"")
|
||||||
0,
|
}
|
||||||
"const-string p$platformParameter, \"$platformName\"",
|
|
||||||
)
|
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
|||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodImplementation
|
||||||
|
|
||||||
val spoofClientPatch = spoofClientPatch(redirectUri = "infinity://localhost") { clientIdOption ->
|
val spoofClientPatch = spoofClientPatch(redirectUri = "infinity://localhost") { clientIdOption ->
|
||||||
compatibleWith("ml.docilealligator.infinityforreddit")
|
compatibleWith(
|
||||||
|
"ml.docilealligator.infinityforreddit",
|
||||||
|
"ml.docilealligator.infinityforreddit.plus",
|
||||||
|
"ml.docilealligator.infinityforreddit.patreon"
|
||||||
|
)
|
||||||
|
|
||||||
val clientId by clientIdOption
|
val clientId by clientIdOption
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ val unlockSubscriptionPatch = bytecodePatch(
|
|||||||
) {
|
) {
|
||||||
dependsOn(spoofClientPatch)
|
dependsOn(spoofClientPatch)
|
||||||
|
|
||||||
compatibleWith("ml.docilealligator.infinityforreddit")
|
compatibleWith(
|
||||||
|
"ml.docilealligator.infinityforreddit",
|
||||||
|
"ml.docilealligator.infinityforreddit.plus",
|
||||||
|
"ml.docilealligator.infinityforreddit.patreon"
|
||||||
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
setOf(
|
setOf(
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.w3c.dom.Element
|
|||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
abstract class BasePreference(
|
abstract class BasePreference(
|
||||||
val key: String? = null,
|
val key: String? = null,
|
||||||
val titleKey: String = "${key}_title",
|
val titleKey: String? = "${key}_title",
|
||||||
val summaryKey: String? = "${key}_summary",
|
val summaryKey: String? = "${key}_summary",
|
||||||
val icon: String? = null,
|
val icon: String? = null,
|
||||||
val layout: String? = null,
|
val layout: String? = null,
|
||||||
@@ -35,7 +35,7 @@ abstract class BasePreference(
|
|||||||
open fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit): Element =
|
open fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit): Element =
|
||||||
ownerDocument.createElement(tag).apply {
|
ownerDocument.createElement(tag).apply {
|
||||||
key?.let { setAttribute("android:key", it) }
|
key?.let { setAttribute("android:key", it) }
|
||||||
setAttribute("android:title", "@string/${titleKey}")
|
titleKey?.let { setAttribute("android:title", "@string/${titleKey}") }
|
||||||
summaryKey?.let { addSummary(it) }
|
summaryKey?.let { addSummary(it) }
|
||||||
icon?.let {
|
icon?.let {
|
||||||
setAttribute("android:icon", it)
|
setAttribute("android:icon", it)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.w3c.dom.Document
|
|||||||
@Suppress("MemberVisibilityCanBePrivate")
|
@Suppress("MemberVisibilityCanBePrivate")
|
||||||
open class PreferenceCategory(
|
open class PreferenceCategory(
|
||||||
key: String? = null,
|
key: String? = null,
|
||||||
titleKey: String = "${key}_title",
|
titleKey: String? = "${key}_title",
|
||||||
icon: String? = null,
|
icon: String? = null,
|
||||||
layout: String? = null,
|
layout: String? = null,
|
||||||
sorting: Sorting = Sorting.BY_TITLE,
|
sorting: Sorting = Sorting.BY_TITLE,
|
||||||
|
|||||||
@@ -137,3 +137,15 @@ internal val patchIncludedExtensionMethodFingerprint = fingerprint {
|
|||||||
classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded"
|
classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Feature flag that turns on Platypus programming language code compiled to native C++.
|
||||||
|
// This code appears to replace the player config after the streams are loaded.
|
||||||
|
// Flag is present in YouTube 19.34, but is missing Platypus stream replacement code until 19.43.
|
||||||
|
// Flag and Platypus code is also present in newer versions of YouTube Music.
|
||||||
|
internal const val MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG = 45645570L
|
||||||
|
|
||||||
|
internal val mediaFetchHotConfigFingerprint = fingerprint {
|
||||||
|
literal {
|
||||||
|
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,10 +31,11 @@ internal const val EXTENSION_CLASS_DESCRIPTOR =
|
|||||||
|
|
||||||
fun spoofVideoStreamsPatch(
|
fun spoofVideoStreamsPatch(
|
||||||
block: BytecodePatchBuilder.() -> Unit = {},
|
block: BytecodePatchBuilder.() -> Unit = {},
|
||||||
|
applyMediaFetchHotConfigChanges: BytecodePatchBuilder.() -> Boolean = { false },
|
||||||
executeBlock: BytecodePatchContext.() -> Unit = {},
|
executeBlock: BytecodePatchContext.() -> Unit = {},
|
||||||
) = bytecodePatch(
|
) = bytecodePatch(
|
||||||
name = "Spoof video streams",
|
name = "Spoof video streams",
|
||||||
description = "Spoofs the client video streams to fix playback.",
|
description = "Adds options to spoof the client video streams to fix playback.",
|
||||||
) {
|
) {
|
||||||
block()
|
block()
|
||||||
|
|
||||||
@@ -238,6 +239,17 @@ fun spoofVideoStreamsPatch(
|
|||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region turn off stream config replacement feature flag.
|
||||||
|
|
||||||
|
if (applyMediaFetchHotConfigChanges()) {
|
||||||
|
mediaFetchHotConfigFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||||
|
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG,
|
||||||
|
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
executeBlock()
|
executeBlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ package app.revanced.patches.tiktok.misc.extension
|
|||||||
|
|
||||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||||
|
|
||||||
val sharedExtensionPatch = sharedExtensionPatch("tiktok", initHook)
|
val sharedExtensionPatch = sharedExtensionPatch("tiktok", initHook, jatoInitHook, storeRegionInitHook)
|
||||||
|
|||||||
@@ -3,12 +3,35 @@ package app.revanced.patches.tiktok.misc.extension
|
|||||||
import app.revanced.patches.shared.misc.extension.extensionHook
|
import app.revanced.patches.shared.misc.extension.extensionHook
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
internal val initHook = extensionHook(
|
internal val initHook = extensionHook {
|
||||||
insertIndexResolver = { 1 }, // Insert after call to super class.
|
|
||||||
) {
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
|
||||||
custom { method, classDef ->
|
custom { method, classDef ->
|
||||||
classDef.endsWith("/AwemeHostApplication;") &&
|
classDef.type == "Lcom/ss/android/ugc/aweme/main/MainActivity;" &&
|
||||||
method.name == "<init>"
|
method.name == "onCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In some cases the extension code can be called before
|
||||||
|
* the app main activity onCreate is called.
|
||||||
|
*
|
||||||
|
* This class is called from startup code titled "BPEA RunnableGuardLancet".
|
||||||
|
*/
|
||||||
|
internal val jatoInitHook = extensionHook(
|
||||||
|
contextRegisterResolver = { "p1" }
|
||||||
|
) {
|
||||||
|
parameters("Landroid/content/Context;")
|
||||||
|
custom { method, classDef ->
|
||||||
|
classDef.type == "Lcom/ss/android/ugc/aweme/legoImp/task/JatoInitTask;" &&
|
||||||
|
method.name == "run"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val storeRegionInitHook = extensionHook(
|
||||||
|
contextRegisterResolver = { "p1" }
|
||||||
|
) {
|
||||||
|
parameters("Landroid/content/Context;")
|
||||||
|
custom { method, classDef ->
|
||||||
|
classDef.type == "Lcom/ss/android/ugc/aweme/legoImp/task/StoreRegionInitTask;" &&
|
||||||
|
method.name == "run"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
|
|||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/extension/tiktok/settings/AdPersonalizationActivityHook;"
|
"Lapp/revanced/extension/tiktok/settings/AdPersonalizationActivityHook;"
|
||||||
|
|
||||||
val settingsPatch = bytecodePatch(
|
val settingsPatch = bytecodePatch(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/extension/tudortmund/lockscreen/ShowOnLockscreenPatch;"
|
"Lapp/revanced/extension/tudortmund/lockscreen/ShowOnLockscreenPatch;"
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
|||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/extension/youtube/patches/HideGetPremiumPatch;"
|
"Lapp/revanced/extension/youtube/patches/HideGetPremiumPatch;"
|
||||||
|
|
||||||
val hideGetPremiumPatch = bytecodePatch(
|
val hideGetPremiumPatch = bytecodePatch(
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ private val downloadsResourcePatch = resourcePatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DownloadsPatch;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/DownloadsPatch;"
|
||||||
|
|
||||||
internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;"
|
internal const val BUTTON_DESCRIPTOR = "Lapp/revanced/extension/youtube/videoplayer/ExternalDownloadButton;"
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
val enableSeekbarTappingPatch = bytecodePatch(
|
val enableSeekbarTappingPatch = bytecodePatch(
|
||||||
name = "Seekbar tapping",
|
name = "Enable tap to seek",
|
||||||
description = "Adds an option to enable tap-to-seek on the seekbar of the video player.",
|
description = "Adds an option to enable tap to seek on the seekbar of the video player.",
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
sharedExtensionPatch,
|
sharedExtensionPatch,
|
||||||
|
|||||||
@@ -42,9 +42,10 @@ private val swipeControlsResourcePatch = resourcePatch {
|
|||||||
SwitchPreference("revanced_swipe_haptic_feedback"),
|
SwitchPreference("revanced_swipe_haptic_feedback"),
|
||||||
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
|
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
|
||||||
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
|
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
|
||||||
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
|
SwitchPreference("revanced_swipe_show_circular_overlay"),
|
||||||
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
|
SwitchPreference("revanced_swipe_overlay_minimal_style"),
|
||||||
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
||||||
|
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
|
||||||
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
|
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,7 +54,12 @@ private val swipeControlsResourcePatch = resourcePatch {
|
|||||||
ResourceGroup(
|
ResourceGroup(
|
||||||
"drawable",
|
"drawable",
|
||||||
"revanced_ic_sc_brightness_auto.xml",
|
"revanced_ic_sc_brightness_auto.xml",
|
||||||
"revanced_ic_sc_brightness_manual.xml",
|
"revanced_ic_sc_brightness_full.xml",
|
||||||
|
"revanced_ic_sc_brightness_high.xml",
|
||||||
|
"revanced_ic_sc_brightness_low.xml",
|
||||||
|
"revanced_ic_sc_brightness_medium.xml",
|
||||||
|
"revanced_ic_sc_volume_high.xml",
|
||||||
|
"revanced_ic_sc_volume_low.xml",
|
||||||
"revanced_ic_sc_volume_mute.xml",
|
"revanced_ic_sc_volume_mute.xml",
|
||||||
"revanced_ic_sc_volume_normal.xml",
|
"revanced_ic_sc_volume_normal.xml",
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ val hideButtonsPatch = resourcePatch(
|
|||||||
PreferenceScreenPreference(
|
PreferenceScreenPreference(
|
||||||
"revanced_hide_buttons_screen",
|
"revanced_hide_buttons_screen",
|
||||||
preferences = setOf(
|
preferences = setOf(
|
||||||
|
SwitchPreference("revanced_disable_like_subscribe_glow"),
|
||||||
SwitchPreference("revanced_hide_like_dislike_button"),
|
SwitchPreference("revanced_hide_like_dislike_button"),
|
||||||
SwitchPreference("revanced_hide_share_button"),
|
SwitchPreference("revanced_hide_share_button"),
|
||||||
SwitchPreference("revanced_hide_report_button"),
|
SwitchPreference("revanced_hide_report_button"),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
|||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;"
|
"Lapp/revanced/extension/youtube/patches/NavigationButtonsPatch;"
|
||||||
|
|
||||||
val navigationButtonsPatch = bytecodePatch(
|
val navigationButtonsPatch = bytecodePatch(
|
||||||
@@ -58,6 +58,7 @@ val navigationButtonsPatch = bytecodePatch(
|
|||||||
SwitchPreference("revanced_hide_shorts_button"),
|
SwitchPreference("revanced_hide_shorts_button"),
|
||||||
SwitchPreference("revanced_hide_create_button"),
|
SwitchPreference("revanced_hide_create_button"),
|
||||||
SwitchPreference("revanced_hide_subscriptions_button"),
|
SwitchPreference("revanced_hide_subscriptions_button"),
|
||||||
|
SwitchPreference("revanced_hide_notifications_button"),
|
||||||
SwitchPreference("revanced_switch_create_with_notifications_button"),
|
SwitchPreference("revanced_switch_create_with_notifications_button"),
|
||||||
SwitchPreference("revanced_hide_navigation_button_labels"),
|
SwitchPreference("revanced_hide_navigation_button_labels"),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
|
|||||||
|
|
||||||
val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
||||||
name = "Hide player overlay buttons",
|
name = "Hide player overlay buttons",
|
||||||
description = "Adds options to hide the player cast, autoplay, caption button and next/ previous buttons.",
|
description = "Adds options to hide the player Cast, Autoplay, Captions, and Previous & Next buttons.",
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
sharedExtensionPatch,
|
sharedExtensionPatch,
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import app.revanced.patcher.patch.bytecodePatch
|
|||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
|
import app.revanced.patches.youtube.layout.buttons.navigation.navigationButtonsPatch
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
@@ -15,7 +17,7 @@ import com.android.tools.smali.dexlib2.Opcode
|
|||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;"
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val changeFormFactorPatch = bytecodePatch(
|
val changeFormFactorPatch = bytecodePatch(
|
||||||
@@ -26,6 +28,7 @@ val changeFormFactorPatch = bytecodePatch(
|
|||||||
sharedExtensionPatch,
|
sharedExtensionPatch,
|
||||||
settingsPatch,
|
settingsPatch,
|
||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
|
navigationButtonsPatch
|
||||||
)
|
)
|
||||||
|
|
||||||
compatibleWith(
|
compatibleWith(
|
||||||
@@ -50,6 +53,8 @@ val changeFormFactorPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
|
||||||
|
|
||||||
createPlayerRequestBodyWithModelFingerprint.method.apply {
|
createPlayerRequestBodyWithModelFingerprint.method.apply {
|
||||||
val formFactorEnumClass = formFactorEnumConstructorFingerprint.originalClassDef.type
|
val formFactorEnumClass = formFactorEnumConstructorFingerprint.originalClassDef.type
|
||||||
|
|
||||||
|
|||||||
@@ -44,9 +44,12 @@ private val hideEndscreenCardsResourcePatch = resourcePatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/extension/youtube/patches/HideEndscreenCardsPatch;"
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val hideEndscreenCardsPatch = bytecodePatch(
|
val hideEndscreenCardsPatch = bytecodePatch(
|
||||||
name = "Hide endscreen cards",
|
name = "Hide end screen cards",
|
||||||
description = "Adds an option to hide suggested video cards at the end of videos.",
|
description = "Adds an option to hide suggested video cards at the end of videos.",
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
@@ -78,9 +81,7 @@ val hideEndscreenCardsPatch = bytecodePatch(
|
|||||||
|
|
||||||
addInstruction(
|
addInstruction(
|
||||||
insertIndex,
|
insertIndex,
|
||||||
"invoke-static { v$viewRegister }, " +
|
"invoke-static { v$viewRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideEndscreen(Landroid/view/View;)V",
|
||||||
"Lapp/revanced/extension/youtube/patches/HideEndscreenCardsPatch;->" +
|
|
||||||
"hideEndscreen(Landroid/view/View;)V",
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.hide.endscreensuggestion
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
|
internal val autoNavConstructorFingerprint = fingerprint {
|
||||||
|
returns("V")
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||||
|
strings("main_app_autonav")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val autoNavStatusFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("Z")
|
||||||
|
parameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val removeOnLayoutChangeListenerFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
|
parameters()
|
||||||
|
opcodes(
|
||||||
|
Opcode.IPUT,
|
||||||
|
Opcode.INVOKE_VIRTUAL
|
||||||
|
)
|
||||||
|
// This is the only reference present in the entire smali.
|
||||||
|
custom { method, _ ->
|
||||||
|
method.indexOfFirstInstruction {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
reference?.name == "removeOnLayoutChangeListener" &&
|
||||||
|
reference.definingClass.endsWith("/YouTubePlayerOverlaysLayout;")
|
||||||
|
} >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package app.revanced.patches.youtube.layout.hide.endscreensuggestion
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.util.smali.ExternalLabel
|
||||||
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/extension/youtube/patches/HideEndScreenSuggestedVideoPatch;"
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val hideEndScreenSuggestedVideoPatch = bytecodePatch(
|
||||||
|
name = "Hide end screen suggested video",
|
||||||
|
description = "Adds an option to hide the suggested video at the end of videos.",
|
||||||
|
) {
|
||||||
|
dependsOn(
|
||||||
|
sharedExtensionPatch,
|
||||||
|
addResourcesPatch,
|
||||||
|
)
|
||||||
|
|
||||||
|
compatibleWith(
|
||||||
|
"com.google.android.youtube"(
|
||||||
|
"19.16.39",
|
||||||
|
"19.25.37",
|
||||||
|
"19.34.42",
|
||||||
|
"19.43.41",
|
||||||
|
"19.45.38",
|
||||||
|
"19.46.42",
|
||||||
|
"19.47.53",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
addResources("youtube", "layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch")
|
||||||
|
|
||||||
|
PreferenceScreen.PLAYER.addPreferences(
|
||||||
|
SwitchPreference("revanced_end_screen_suggested_video"),
|
||||||
|
)
|
||||||
|
|
||||||
|
removeOnLayoutChangeListenerFingerprint.let {
|
||||||
|
val endScreenMethod = navigate(it.originalMethod).to(it.patternMatch!!.endIndex).stop()
|
||||||
|
|
||||||
|
endScreenMethod.apply {
|
||||||
|
val autoNavStatusMethodName = autoNavStatusFingerprint.match(
|
||||||
|
autoNavConstructorFingerprint.classDef
|
||||||
|
).originalMethod.name
|
||||||
|
|
||||||
|
val invokeIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
reference?.name == autoNavStatusMethodName &&
|
||||||
|
reference.returnType == "Z" &&
|
||||||
|
reference.parameterTypes.isEmpty()
|
||||||
|
}
|
||||||
|
val iGetObjectIndex = indexOfFirstInstructionReversedOrThrow(invokeIndex, Opcode.IGET_OBJECT)
|
||||||
|
val invokeReference = getInstruction<ReferenceInstruction>(invokeIndex).reference
|
||||||
|
val iGetObjectReference = getInstruction<ReferenceInstruction>(iGetObjectIndex).reference
|
||||||
|
val opcodeName = getInstruction(invokeIndex).opcode.name
|
||||||
|
|
||||||
|
addInstructionsWithLabels(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideEndScreenSuggestedVideo()Z
|
||||||
|
move-result v0
|
||||||
|
if-eqz v0, :show_end_screen_recommendation
|
||||||
|
|
||||||
|
iget-object v0, p0, $iGetObjectReference
|
||||||
|
|
||||||
|
# This reference checks whether autoplay is turned on.
|
||||||
|
$opcodeName { v0 }, $invokeReference
|
||||||
|
move-result v0
|
||||||
|
|
||||||
|
# Hide suggested video end screen only when autoplay is turned off.
|
||||||
|
if-nez v0, :show_end_screen_recommendation
|
||||||
|
return-void
|
||||||
|
""",
|
||||||
|
ExternalLabel("show_end_screen_recommendation", getInstruction(0))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
|||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/extension/youtube/patches/DisableFullscreenAmbientModePatch;"
|
"Lapp/revanced/extension/youtube/patches/DisableFullscreenAmbientModePatch;"
|
||||||
|
|
||||||
val disableFullscreenAmbientModePatch = bytecodePatch(
|
val disableFullscreenAmbientModePatch = bytecodePatch(
|
||||||
|
|||||||
@@ -158,9 +158,9 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
|||||||
SwitchPreference("revanced_hide_comments_by_members_header"),
|
SwitchPreference("revanced_hide_comments_by_members_header"),
|
||||||
SwitchPreference("revanced_hide_comments_section"),
|
SwitchPreference("revanced_hide_comments_section"),
|
||||||
SwitchPreference("revanced_hide_comments_create_a_short_button"),
|
SwitchPreference("revanced_hide_comments_create_a_short_button"),
|
||||||
|
SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons"),
|
||||||
SwitchPreference("revanced_hide_comments_preview_comment"),
|
SwitchPreference("revanced_hide_comments_preview_comment"),
|
||||||
SwitchPreference("revanced_hide_comments_thanks_button"),
|
SwitchPreference("revanced_hide_comments_thanks_button"),
|
||||||
SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons"),
|
|
||||||
),
|
),
|
||||||
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
|
||||||
),
|
),
|
||||||
@@ -172,7 +172,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
|||||||
SwitchPreference("revanced_hide_emergency_box"),
|
SwitchPreference("revanced_hide_emergency_box"),
|
||||||
SwitchPreference("revanced_hide_info_panels"),
|
SwitchPreference("revanced_hide_info_panels"),
|
||||||
SwitchPreference("revanced_hide_join_membership_button"),
|
SwitchPreference("revanced_hide_join_membership_button"),
|
||||||
SwitchPreference("revanced_disable_like_subscribe_glow"),
|
|
||||||
SwitchPreference("revanced_hide_medical_panels"),
|
SwitchPreference("revanced_hide_medical_panels"),
|
||||||
SwitchPreference("revanced_hide_quick_actions"),
|
SwitchPreference("revanced_hide_quick_actions"),
|
||||||
SwitchPreference("revanced_hide_related_videos"),
|
SwitchPreference("revanced_hide_related_videos"),
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/pat
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val hideShortsComponentsPatch = bytecodePatch(
|
val hideShortsComponentsPatch = bytecodePatch(
|
||||||
name = "Hide Shorts components",
|
name = "Hide Shorts components",
|
||||||
description = "Adds options to hide components related to YouTube Shorts.",
|
description = "Adds options to hide components related to Shorts.",
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
sharedExtensionPatch,
|
sharedExtensionPatch,
|
||||||
|
|||||||
@@ -1,79 +1,9 @@
|
|||||||
package app.revanced.patches.youtube.layout.hide.suggestedvideoendscreen
|
package app.revanced.patches.youtube.layout.hide.suggestedvideoendscreen
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patches.youtube.layout.hide.endscreensuggestion.hideEndScreenSuggestedVideoPatch
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
|
||||||
import app.revanced.patches.shared.misc.mapping.get
|
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
|
||||||
|
|
||||||
internal var sizeAdjustableLiteAutoNavOverlay = -1L
|
@Deprecated("Use 'Hide suggested video end screen' instead.")
|
||||||
private set
|
val disableSuggestedVideoEndScreenPatch = bytecodePatch {
|
||||||
|
dependsOn(hideEndScreenSuggestedVideoPatch)
|
||||||
internal val disableSuggestedVideoEndScreenResourcePatch = resourcePatch {
|
|
||||||
dependsOn(
|
|
||||||
settingsPatch,
|
|
||||||
resourceMappingPatch,
|
|
||||||
addResourcesPatch,
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
|
||||||
addResources("youtube", "layout.hide.suggestedvideoendscreen.disableSuggestedVideoEndScreenResourcePatch")
|
|
||||||
|
|
||||||
PreferenceScreen.PLAYER.addPreferences(
|
|
||||||
SwitchPreference("revanced_disable_suggested_video_end_screen"),
|
|
||||||
)
|
|
||||||
|
|
||||||
sizeAdjustableLiteAutoNavOverlay = resourceMappings[
|
|
||||||
"layout",
|
|
||||||
"size_adjustable_lite_autonav_overlay",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
|
||||||
"Lapp/revanced/extension/youtube/patches/DisableSuggestedVideoEndScreenPatch;"
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val disableSuggestedVideoEndScreenPatch = bytecodePatch(
|
|
||||||
name = "Disable suggested video end screen",
|
|
||||||
description = "Adds an option to disable the suggested video end screen at the end of videos.",
|
|
||||||
) {
|
|
||||||
dependsOn(
|
|
||||||
sharedExtensionPatch,
|
|
||||||
disableSuggestedVideoEndScreenResourcePatch,
|
|
||||||
)
|
|
||||||
|
|
||||||
compatibleWith(
|
|
||||||
"com.google.android.youtube"(
|
|
||||||
"19.16.39",
|
|
||||||
"19.25.37",
|
|
||||||
"19.34.42",
|
|
||||||
"19.43.41",
|
|
||||||
"19.45.38",
|
|
||||||
"19.46.42",
|
|
||||||
"19.47.53",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
|
||||||
createEndScreenViewFingerprint.method.apply {
|
|
||||||
val addOnClickEventListenerIndex = createEndScreenViewFingerprint.patternMatch!!.endIndex - 1
|
|
||||||
val viewRegister = getInstruction<FiveRegisterInstruction>(addOnClickEventListenerIndex).registerC
|
|
||||||
|
|
||||||
addInstruction(
|
|
||||||
addOnClickEventListenerIndex + 1,
|
|
||||||
"invoke-static {v$viewRegister}, " +
|
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->closeEndScreen(Landroid/widget/ImageView;)V",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user