mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 09:53:55 +01:00
Compare commits
107 Commits
v5.10.0-de
...
v5.13.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
905bb0ea5f | ||
|
|
a94a663859 | ||
|
|
04b37dd55a | ||
|
|
2382e9d09e | ||
|
|
97f504976a | ||
|
|
0a6c5158e0 | ||
|
|
a959d798e8 | ||
|
|
39a0b9bda6 | ||
|
|
92c38b2cb4 | ||
|
|
4732210d4b | ||
|
|
f30a49f1cb | ||
|
|
bcd157dd2b | ||
|
|
d299ea5973 | ||
|
|
a20021e290 | ||
|
|
373ca966f3 | ||
|
|
12de922afa | ||
|
|
580bb3cf6c | ||
|
|
421af92f4c | ||
|
|
4d03e1b5a1 | ||
|
|
24d68df6cd | ||
|
|
e9aee17746 | ||
|
|
7c4285e3e6 | ||
|
|
e3110271a7 | ||
|
|
0079eceb87 | ||
|
|
af2a97cb16 | ||
|
|
aeb552e8f2 | ||
|
|
6e936fea42 | ||
|
|
f63769f39f | ||
|
|
1c9ab20a63 | ||
|
|
cdeccad908 | ||
|
|
399889c6fa | ||
|
|
ec77861410 | ||
|
|
b5afc6d827 | ||
|
|
b7ebfddf65 | ||
|
|
2742aca48b | ||
|
|
14ca4d3288 | ||
|
|
a06c0318bf | ||
|
|
7f9f668435 | ||
|
|
76fd33ca54 | ||
|
|
9a653e9c5a | ||
|
|
f81b658fb7 | ||
|
|
7ff39d89d6 | ||
|
|
78ab0ec2bd | ||
|
|
3ab67f1539 | ||
|
|
8652cd613f | ||
|
|
bc8388713c | ||
|
|
d4b2e3be3e | ||
|
|
57c48b7829 | ||
|
|
aaa7523ee4 | ||
|
|
785df4fe69 | ||
|
|
83208eb50d | ||
|
|
9437db11eb | ||
|
|
1843c8bf70 | ||
|
|
778b51fbff | ||
|
|
ee0fdcdf86 | ||
|
|
57cc73d9c4 | ||
|
|
043ebbb6d4 | ||
|
|
d5551923fc | ||
|
|
f844a1cd76 | ||
|
|
a7e3277cc1 | ||
|
|
6fa2deea69 | ||
|
|
dcca2a3697 | ||
|
|
018160fd9c | ||
|
|
680252967e | ||
|
|
e79eba81d9 | ||
|
|
a73db03671 | ||
|
|
055ad04281 | ||
|
|
aaeee4a895 | ||
|
|
654b339f66 | ||
|
|
64cdce28a6 |
3
.github/workflows/pull_strings.yml
vendored
3
.github/workflows/pull_strings.yml
vendored
@@ -2,7 +2,7 @@ name: Pull strings
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */8 * * *"
|
||||
- cron: "0 */6 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -26,6 +26,7 @@ jobs:
|
||||
config: crowdin.yml
|
||||
upload_sources: false
|
||||
download_translations: true
|
||||
skip_ref_checkout: true
|
||||
localization_branch_name: feat/translations
|
||||
create_pull_request: false
|
||||
env:
|
||||
|
||||
5
.github/workflows/push_strings.yml
vendored
5
.github/workflows/push_strings.yml
vendored
@@ -18,6 +18,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Preprocess strings
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./gradlew clean preprocessCrowdinStrings
|
||||
|
||||
- name: Push strings
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
|
||||
292
CHANGELOG.md
292
CHANGELOG.md
@@ -1,3 +1,295 @@
|
||||
# [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **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))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube Music:** Support version `8.05.51` ([128441e](https://github.com/ReVanced/revanced-patches/commit/128441e78bc0d096c3fc2f57782ab90c39c3ae4b))
|
||||
|
||||
# [5.12.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.5...v5.12.0-dev.6) (2025-02-16)
|
||||
|
||||
|
||||
### 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))
|
||||
|
||||
# [5.12.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.4...v5.12.0-dev.5) (2025-02-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([a006758](https://github.com/ReVanced/revanced-patches/commit/a0067581d0f877e1b4eb1f888a25786f09676b2e))
|
||||
|
||||
# [5.12.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.3...v5.12.0-dev.4) (2025-02-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **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))
|
||||
|
||||
# [5.12.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.2...v5.12.0-dev.3) (2025-02-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **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))
|
||||
|
||||
# [5.12.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.12.0-dev.1...v5.12.0-dev.2) (2025-02-11)
|
||||
|
||||
|
||||
### 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))
|
||||
|
||||
# [5.12.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.11.0...v5.12.0-dev.1) (2025-02-10)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([8f4883f](https://github.com/ReVanced/revanced-patches/commit/8f4883fc002420bfb4056401e23445c99e1d3fce))
|
||||
|
||||
# [5.11.0](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.11.0) (2025-02-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix broken `Remove screen capture restriction`, `Remove screenshot restriction`, `Spoof Wi-Fi connection`, and `Export internal data documents provider` patch ([#4405](https://github.com/ReVanced/revanced-patches/issues/4405)) ([1d52b74](https://github.com/ReVanced/revanced-patches/commit/1d52b7478d34e699d8c629eeaa9fdbb470b7d5c8))
|
||||
* **YouTube - Enable slide to seek:** Change patch to default include ([50358cd](https://github.com/ReVanced/revanced-patches/commit/50358cddea3eef4051d248040d23f774521dce00))
|
||||
* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([f67ab2b](https://github.com/ReVanced/revanced-patches/commit/f67ab2baf25d543ceb55fcec48bda441ebf2b998))
|
||||
* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([1cba294](https://github.com/ReVanced/revanced-patches/commit/1cba2948a6787118eb380ffcec35ee4fb99447ea))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Change start page:** Add additional start pages ([#4413](https://github.com/ReVanced/revanced-patches/issues/4413)) ([b434182](https://github.com/ReVanced/revanced-patches/commit/b434182df69313c4eb5f0dfd98101cb80e46ead2))
|
||||
|
||||
# [5.11.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.11.0-dev.1...v5.11.0-dev.2) (2025-02-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix broken `Remove screen capture restriction`, `Remove screenshot restriction`, `Spoof Wi-Fi connection`, and `Export internal data documents provider` patch ([#4405](https://github.com/ReVanced/revanced-patches/issues/4405)) ([1d52b74](https://github.com/ReVanced/revanced-patches/commit/1d52b7478d34e699d8c629eeaa9fdbb470b7d5c8))
|
||||
|
||||
# [5.11.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.3...v5.11.0-dev.1) (2025-02-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Change start page:** Add additional start pages ([#4413](https://github.com/ReVanced/revanced-patches/issues/4413)) ([b434182](https://github.com/ReVanced/revanced-patches/commit/b434182df69313c4eb5f0dfd98101cb80e46ead2))
|
||||
|
||||
## [5.10.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.2...v5.10.1-dev.3) (2025-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([f67ab2b](https://github.com/ReVanced/revanced-patches/commit/f67ab2baf25d543ceb55fcec48bda441ebf2b998))
|
||||
|
||||
## [5.10.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.1-dev.1...v5.10.1-dev.2) (2025-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Enable slide to seek:** Change patch to default include ([50358cd](https://github.com/ReVanced/revanced-patches/commit/50358cddea3eef4051d248040d23f774521dce00))
|
||||
|
||||
## [5.10.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.10.0...v5.10.1-dev.1) (2025-02-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([1cba294](https://github.com/ReVanced/revanced-patches/commit/1cba2948a6787118eb380ffcec35ee4fb99447ea))
|
||||
|
||||
# [5.10.0](https://github.com/ReVanced/revanced-patches/compare/v5.9.0...v5.10.0) (2025-01-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SwissId - Play integrity Removal:** Add recommended app version ([#4370](https://github.com/ReVanced/revanced-patches/issues/4370)) ([d8ed474](https://github.com/ReVanced/revanced-patches/commit/d8ed474b165f094fdedc32caaae1f82ebc99eb3d))
|
||||
* Use correct path to fix invalid file paths ([5ff4ee8](https://github.com/ReVanced/revanced-patches/commit/5ff4ee823da55c7b135eab8b62e07be465612b55))
|
||||
* **YouTube - Hide ads:** fix 'Hide the Visit store button on channel pages' not working ([#4364](https://github.com/ReVanced/revanced-patches/issues/4364)) ([9d63ea9](https://github.com/ReVanced/revanced-patches/commit/9d63ea9a10ab5128ce18a1f53a946e84550da258))
|
||||
* **YouTube - Hide Ads:** Hide end screen store banner without leaving empty space ([#4367](https://github.com/ReVanced/revanced-patches/issues/4367)) ([7e68390](https://github.com/ReVanced/revanced-patches/commit/7e683906418434dd4e2104337d73a2292415c615))
|
||||
* **YouTube - Hide ads:** Hide new types of tablet ads ([574bcc8](https://github.com/ReVanced/revanced-patches/commit/574bcc844746b7445ec3e93b47daceafefad85e7))
|
||||
* **YouTube - Hide layout components:** Hide new kind of community post ([#4341](https://github.com/ReVanced/revanced-patches/issues/4341)) ([02685c4](https://github.com/ReVanced/revanced-patches/commit/02685c4567aca55f22d45dc238a7d1f0ea264143))
|
||||
* **YouTube - Hide seekbar:** Do not hide player seekbar if hide feed seekbar is enabled ([#4333](https://github.com/ReVanced/revanced-patches/issues/4333)) ([f5cf6f2](https://github.com/ReVanced/revanced-patches/commit/f5cf6f2a445492d33815a9772f49deac2d70eba9))
|
||||
* **YouTube - Hide video description components:** Use correct string key names ([0f28c2b](https://github.com/ReVanced/revanced-patches/commit/0f28c2b44c0051ea7ab3136433b84c73321cf5bd))
|
||||
* **YouTube - Spoof video streams:** Update settings side effects summary text ([#4369](https://github.com/ReVanced/revanced-patches/issues/4369)) ([e5b3aa1](https://github.com/ReVanced/revanced-patches/commit/e5b3aa1cc6a2465cb006487d528de888bc7cd430))
|
||||
* **YouTube - Theme:** Fix 19.25 - 19.45 patch error ([5b47a5f](https://github.com/ReVanced/revanced-patches/commit/5b47a5f0f6299daaae209341064fd85f16ca18a6))
|
||||
* **YouTube - Theme:** Replace custom seekbar gradient colors instead of disabling ([#4329](https://github.com/ReVanced/revanced-patches/issues/4329)) ([f03da98](https://github.com/ReVanced/revanced-patches/commit/f03da983051021e0c372557a5354d5d967409564))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide ads:** Add `Hide end screen store banner` ([#4351](https://github.com/ReVanced/revanced-patches/issues/4351)) ([5505087](https://github.com/ReVanced/revanced-patches/commit/55050878028fed82b0f583a9f7ba06b8f267f8ec))
|
||||
* **YouTube - Hide video description components:** Add `Hide How this content was made section` ([#4355](https://github.com/ReVanced/revanced-patches/issues/4355)) ([68ec54e](https://github.com/ReVanced/revanced-patches/commit/68ec54ef850ae8d6461dd0ef2846e6efbb59e482))
|
||||
* **YouTube - Theme:** Add option to use custom seekbar accent color ([#4337](https://github.com/ReVanced/revanced-patches/issues/4337)) ([952b4fc](https://github.com/ReVanced/revanced-patches/commit/952b4fc4c9291e1a3e71437b503857763c973dd4))
|
||||
* **YouTube:** Add patch `Disable HDR video` ([#4347](https://github.com/ReVanced/revanced-patches/issues/4347)) ([0528f7c](https://github.com/ReVanced/revanced-patches/commit/0528f7cad856a2b1347e41944167b0583fc4a3d9))
|
||||
|
||||
# [5.10.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.10...v5.10.0-dev.11) (2025-01-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Use correct path to fix invalid file paths ([5ff4ee8](https://github.com/ReVanced/revanced-patches/commit/5ff4ee823da55c7b135eab8b62e07be465612b55))
|
||||
|
||||
# [5.10.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.9...v5.10.0-dev.10) (2025-01-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide ads:** Hide new types of tablet ads ([574bcc8](https://github.com/ReVanced/revanced-patches/commit/574bcc844746b7445ec3e93b47daceafefad85e7))
|
||||
|
||||
# [5.10.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.8...v5.10.0-dev.9) (2025-01-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **SwissId - Play integrity Removal:** Add recommended app version ([#4370](https://github.com/ReVanced/revanced-patches/issues/4370)) ([d8ed474](https://github.com/ReVanced/revanced-patches/commit/d8ed474b165f094fdedc32caaae1f82ebc99eb3d))
|
||||
|
||||
# [5.10.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.7...v5.10.0-dev.8) (2025-01-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Update settings side effects summary text ([#4369](https://github.com/ReVanced/revanced-patches/issues/4369)) ([e5b3aa1](https://github.com/ReVanced/revanced-patches/commit/e5b3aa1cc6a2465cb006487d528de888bc7cd430))
|
||||
|
||||
# [5.10.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.6...v5.10.0-dev.7) (2025-01-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide ads:** fix 'Hide the Visit store button on channel pages' not working ([#4364](https://github.com/ReVanced/revanced-patches/issues/4364)) ([9d63ea9](https://github.com/ReVanced/revanced-patches/commit/9d63ea9a10ab5128ce18a1f53a946e84550da258))
|
||||
|
||||
# [5.10.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.5...v5.10.0-dev.6) (2025-01-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide Ads:** Hide end screen store banner without leaving empty space ([#4367](https://github.com/ReVanced/revanced-patches/issues/4367)) ([7e68390](https://github.com/ReVanced/revanced-patches/commit/7e683906418434dd4e2104337d73a2292415c615))
|
||||
|
||||
# [5.10.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.4...v5.10.0-dev.5) (2025-01-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide video description components:** Use correct string key names ([0f28c2b](https://github.com/ReVanced/revanced-patches/commit/0f28c2b44c0051ea7ab3136433b84c73321cf5bd))
|
||||
|
||||
# [5.10.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.3...v5.10.0-dev.4) (2025-01-27)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
android.namespace = "app.revanced.extension"
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
|
||||
@@ -12,7 +12,7 @@ import android.os.Handler;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
/** @noinspection deprecation, unused */
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class SpoofWifiPatch {
|
||||
|
||||
// Used to check what the (real or fake) active network is (take a look at `hasTransport`).
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.extension.all.misc.directory.documentsprovider;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ProviderInfo;
|
||||
@@ -23,6 +24,7 @@ import java.util.Objects;
|
||||
/**
|
||||
* A DocumentsProvider that allows access to the app's internal data directory.
|
||||
*/
|
||||
@SuppressLint("LongLogTag")
|
||||
public class InternalDataDocumentsProvider extends DocumentsProvider {
|
||||
private static final String[] rootColumns =
|
||||
{"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"};
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
android.namespace = "app.revanced.extension"
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
|
||||
@@ -5,7 +5,8 @@ import android.os.Build;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
public final class RemoveScreencaptureRestrictionPatch {
|
||||
@SuppressWarnings("unused")
|
||||
public final class RemoveScreenCaptureRestrictionPatch {
|
||||
// Member of AudioAttributes.Builder
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
public static AudioAttributes.Builder setAllowedCapturePolicy(final AudioAttributes.Builder builder, final int capturePolicy) {
|
||||
@@ -1 +1,16 @@
|
||||
android.namespace = "app.revanced.extension"
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package app.revanced.extension.all.screenshot.removerestriction;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class RemoveScreenshotRestrictionPatch {
|
||||
|
||||
public static void addFlags(Window window, int flags) {
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
// Do not remove. Necessary for the extension plugin to be applied to the project.
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
@@ -356,7 +356,7 @@ public class Utils {
|
||||
|
||||
public static Context getContext() {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,6 @@ public class BaseSettings {
|
||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
|
||||
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
|
||||
// Client type must be last spoof setting due to cyclic references.
|
||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_UNPLUGGED, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ public class BooleanSetting extends Setting<Boolean> {
|
||||
*/
|
||||
public static void privateSetValue(@NonNull BooleanSetting setting, @NonNull Boolean newValue) {
|
||||
setting.value = Objects.requireNonNull(newValue);
|
||||
|
||||
if (setting.isSetToDefault()) {
|
||||
setting.removeFromPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,10 +69,8 @@ public class BooleanSetting extends Setting<Boolean> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull Boolean newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveBoolean(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveBoolean(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -89,10 +89,8 @@ public class EnumSetting<T extends Enum<?>> extends Setting<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull T newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveEnumAsString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveEnumAsString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -55,10 +55,8 @@ public class FloatSetting extends Setting<Float> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull Float newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveFloatString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveFloatString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -55,10 +55,8 @@ public class IntegerSetting extends Setting<Integer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull Integer newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveIntegerString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveIntegerString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -55,10 +55,8 @@ public class LongSetting extends Setting<Long> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull Long newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveLongString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveLongString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -14,7 +14,6 @@ import java.util.*;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class Setting<T> {
|
||||
|
||||
/**
|
||||
@@ -288,6 +287,13 @@ public abstract class Setting<T> {
|
||||
*/
|
||||
public static void privateSetValueFromString(@NonNull Setting<?> setting, @NonNull String newValue) {
|
||||
setting.setValueFromString(newValue);
|
||||
|
||||
// Clear the preference value since default is used, to allow changing
|
||||
// the changing the default for a future release. Without this after upgrading
|
||||
// the saved value will be whatever was the default when the app was first installed.
|
||||
if (setting.isSetToDefault()) {
|
||||
setting.removeFromPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,7 +309,33 @@ public abstract class Setting<T> {
|
||||
/**
|
||||
* Persistently saves the value.
|
||||
*/
|
||||
public abstract void save(@NonNull T newValue);
|
||||
public final void save(@NonNull T newValue) {
|
||||
if (value.equals(newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
|
||||
if (defaultValue.equals(newValue)) {
|
||||
removeFromPreferences();
|
||||
} else {
|
||||
saveToPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save {@link #value} to {@link #preferences}.
|
||||
*/
|
||||
protected abstract void saveToPreferences();
|
||||
|
||||
/**
|
||||
* Remove {@link #value} from {@link #preferences}.
|
||||
*/
|
||||
protected final void removeFromPreferences() {
|
||||
Logger.printDebug(() -> "Clearing stored preference value (reset to default): " + key);
|
||||
preferences.removeKey(key);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public abstract T get();
|
||||
|
||||
@@ -55,10 +55,8 @@ public class StringSetting extends Setting<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NonNull String newValue) {
|
||||
// Must set before saving to preferences (otherwise importing fails to update UI correctly).
|
||||
value = Objects.requireNonNull(newValue);
|
||||
preferences.saveString(key, newValue);
|
||||
public void saveToPreferences() {
|
||||
preferences.saveString(key, value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -66,22 +66,6 @@ public enum ClientType {
|
||||
true,
|
||||
"Android Creator"
|
||||
),
|
||||
ANDROID_VR(
|
||||
ANDROID_VR_NO_AUTH.id,
|
||||
ANDROID_VR_NO_AUTH.clientName,
|
||||
ANDROID_VR_NO_AUTH.packageName,
|
||||
ANDROID_VR_NO_AUTH.deviceMake,
|
||||
ANDROID_VR_NO_AUTH.deviceModel,
|
||||
ANDROID_VR_NO_AUTH.osName,
|
||||
ANDROID_VR_NO_AUTH.osVersion,
|
||||
ANDROID_VR_NO_AUTH.androidSdkVersion,
|
||||
ANDROID_VR_NO_AUTH.buildId,
|
||||
ANDROID_VR_NO_AUTH.cronetVersion,
|
||||
ANDROID_VR_NO_AUTH.clientVersion,
|
||||
ANDROID_VR_NO_AUTH.requiresAuth,
|
||||
true,
|
||||
"Android VR"
|
||||
),
|
||||
IOS_UNPLUGGED(
|
||||
33,
|
||||
"IOS_UNPLUGGED",
|
||||
@@ -112,6 +96,22 @@ public enum ClientType {
|
||||
forceAVC()
|
||||
? "iOS TV Force AVC"
|
||||
: "iOS TV"
|
||||
),
|
||||
ANDROID_VR_AUTH(
|
||||
ANDROID_VR_NO_AUTH.id,
|
||||
ANDROID_VR_NO_AUTH.clientName,
|
||||
ANDROID_VR_NO_AUTH.packageName,
|
||||
ANDROID_VR_NO_AUTH.deviceMake,
|
||||
ANDROID_VR_NO_AUTH.deviceModel,
|
||||
ANDROID_VR_NO_AUTH.osName,
|
||||
ANDROID_VR_NO_AUTH.osVersion,
|
||||
ANDROID_VR_NO_AUTH.androidSdkVersion,
|
||||
ANDROID_VR_NO_AUTH.buildId,
|
||||
ANDROID_VR_NO_AUTH.cronetVersion,
|
||||
ANDROID_VR_NO_AUTH.clientVersion,
|
||||
ANDROID_VR_NO_AUTH.requiresAuth,
|
||||
true,
|
||||
"Android VR Auth"
|
||||
);
|
||||
|
||||
private static boolean forceAVC() {
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
@@ -3,3 +3,14 @@ dependencies {
|
||||
compileOnly(project(":extensions:tiktok:stub"))
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 22
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
protected void syncSettingWithPreference(@NonNull Preference pref,
|
||||
@NonNull Setting<?> setting,
|
||||
boolean applySettingToPreference) {
|
||||
if (pref instanceof RangeValuePreference rangeValuePref) {
|
||||
if (pref instanceof RangeValuePreference) {
|
||||
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
|
||||
Setting.privateSetValueFromString(setting, rangeValuePref.getValue());
|
||||
} else if (pref instanceof DownloadPathPreference downloadPathPref) {
|
||||
} else if (pref instanceof DownloadPathPreference) {
|
||||
DownloadPathPreference downloadPathPref = (DownloadPathPreference) pref;
|
||||
Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
|
||||
} else {
|
||||
super.syncSettingWithPreference(pref, setting, applySettingToPreference);
|
||||
@@ -32,7 +34,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
final var context = getContext();
|
||||
final var context = getActivity();
|
||||
|
||||
// Currently no resources can be compiled for TikTok (fails with aapt error).
|
||||
// So all TikTok Strings are hard coded in the extension.
|
||||
|
||||
@@ -1,37 +1,60 @@
|
||||
package app.revanced.extension.tiktok.spoof.sim;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.tiktok.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
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) {
|
||||
if (ENABLED) {
|
||||
if (isContextNotSet("countryIso")) return value;
|
||||
|
||||
if (Settings.SIM_SPOOF.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 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();
|
||||
Logger.printDebug(() -> "Spoofing sim MCC-MNC from: " + value + " to: " + mcc_mnc);
|
||||
return mcc_mnc;
|
||||
}
|
||||
|
||||
return 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();
|
||||
Logger.printDebug(() -> "Spoofing sim operator from: " + value + " to: " + operator);
|
||||
Logger.printDebug(() -> "Spoofing sim operatorName from: " + value + " to: " + operator);
|
||||
return operator;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,9 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
minSdk = 22
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
dependencies {
|
||||
compileOnly(project(":extensions:tumblr:stub"))
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
android.namespace = "app.revanced.extension"
|
||||
|
||||
plugins {
|
||||
id(libs.plugins.android.library.get().pluginId)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,3 +6,14 @@ dependencies {
|
||||
compileOnly(libs.annotation)
|
||||
compileOnly(libs.appcompat)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,9 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
minSdk = 21
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
//noinspection GradleDependency
|
||||
android.compileSdk = 33
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:youtube:stub"))
|
||||
compileOnly(libs.annotation)
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
minSdk = 26
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,21 +23,30 @@ public final class ChangeStartPagePatch {
|
||||
/**
|
||||
* Browse id.
|
||||
*/
|
||||
ALL_SUBSCRIPTIONS("FEchannels", TRUE),
|
||||
BROWSE("FEguide_builder", TRUE),
|
||||
EXPLORE("FEexplore", TRUE),
|
||||
HISTORY("FEhistory", TRUE),
|
||||
LIBRARY("FElibrary", TRUE),
|
||||
MOVIE("FEstorefront", TRUE),
|
||||
NOTIFICATIONS("FEactivity", TRUE),
|
||||
PLAYLISTS("FEplaylist_aggregation", TRUE),
|
||||
SUBSCRIPTIONS("FEsubscriptions", TRUE),
|
||||
TRENDING("FEtrending", TRUE),
|
||||
YOUR_CLIPS("FEclips", TRUE),
|
||||
|
||||
/**
|
||||
* Channel id, this can be used as a browseId.
|
||||
*/
|
||||
COURSES("UCtFRv9O2AHqOZjjynzrv-xg", TRUE),
|
||||
FASHION("UCrpQ4p1Ql_hG8rKXIKM1MOQ", TRUE),
|
||||
GAMING("UCOpNcN46UbXVtpKMrmU4Abg", TRUE),
|
||||
LIVE("UC4R8DWoMoI7CAwX8_LjQHig", TRUE),
|
||||
MUSIC("UC-9-kyTW8ZkZNDHQJ6FgpwQ", TRUE),
|
||||
NEWS("UCYfdidRxbB8Qhf0Nx7ioOYw", TRUE),
|
||||
SHOPPING("UCkYQyvc_i9hXEo4xic9Hh2g", TRUE),
|
||||
SPORTS("UCEgdi0XIXXZ-qJOFPf4JSKw", TRUE),
|
||||
VIRTUAL_REALITY("UCzuqhhs6NWbgTzMuM09WKDQ", TRUE),
|
||||
|
||||
/**
|
||||
* Playlist id, this can be used as a browseId.
|
||||
@@ -51,12 +60,12 @@ public final class ChangeStartPagePatch {
|
||||
SEARCH("com.google.android.youtube.action.open.search", FALSE),
|
||||
SHORTS("com.google.android.youtube.action.open.shorts", FALSE);
|
||||
|
||||
@Nullable
|
||||
final Boolean isBrowseId;
|
||||
|
||||
@NonNull
|
||||
final String id;
|
||||
|
||||
@Nullable
|
||||
final Boolean isBrowseId;
|
||||
|
||||
StartPage(@NonNull String id, @Nullable Boolean isBrowseId) {
|
||||
this.id = id;
|
||||
this.isBrowseId = isBrowseId;
|
||||
@@ -122,7 +131,7 @@ public final class ChangeStartPagePatch {
|
||||
}
|
||||
appLaunched = true;
|
||||
|
||||
final String intentAction = START_PAGE.id;
|
||||
String intentAction = START_PAGE.id;
|
||||
Logger.printDebug(() -> "Changing intent action to " + intentAction);
|
||||
intent.setAction(intentAction);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ public final class NavigationButtonsPatch {
|
||||
{
|
||||
put(NavigationButton.HOME, Settings.HIDE_HOME_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.SUBSCRIPTIONS, Settings.HIDE_SUBSCRIPTIONS_BUTTON.get());
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeD
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.os.Build;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
@@ -366,9 +365,7 @@ public class ReturnYouTubeDislikePatch {
|
||||
private static final List<WeakReference<TextView>> shortsTextViewRefs = new ArrayList<>();
|
||||
|
||||
private static void clearRemovedShortsTextViews() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // YouTube requires Android N or greater
|
||||
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
|
||||
}
|
||||
shortsTextViewRefs.removeIf(ref -> ref.get() == null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
@@ -58,7 +55,6 @@ public class ShortsAutoplayPatch {
|
||||
/**
|
||||
* @return If the app is currently in background PiP mode.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
private static boolean isAppInBackgroundPiPMode() {
|
||||
Activity activity = mainActivityRef.get();
|
||||
return activity != null && activity.isInPictureInPictureMode();
|
||||
@@ -80,7 +76,6 @@ public class ShortsAutoplayPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
|
||||
try {
|
||||
final boolean autoplay;
|
||||
|
||||
@@ -5,10 +5,13 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
@SuppressWarnings("unused")
|
||||
public class VideoAdsPatch {
|
||||
|
||||
// Used by app.revanced.patches.youtube.ad.general.video.patch.VideoAdsPatch
|
||||
// depends on Whitelist patch (still needs to be written)
|
||||
private static final boolean SHOW_VIDEO_ADS = !Settings.HIDE_VIDEO_ADS.get();
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean shouldShowAds() {
|
||||
return !Settings.HIDE_VIDEO_ADS.get(); // TODO && Whitelist.shouldShowAds();
|
||||
return SHOW_VIDEO_ADS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,13 +7,10 @@ import static app.revanced.extension.youtube.patches.announcements.requests.Anno
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.os.Build;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -31,7 +28,6 @@ public final class AnnouncementsPatch {
|
||||
private AnnouncementsPatch() {
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
private static boolean isLatestAlready() throws IOException {
|
||||
HttpURLConnection connection =
|
||||
AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS);
|
||||
@@ -70,7 +66,6 @@ public final class AnnouncementsPatch {
|
||||
return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public static void showAnnouncement(final Activity context) {
|
||||
if (!Settings.ANNOUNCEMENTS.get()) return;
|
||||
|
||||
|
||||
@@ -8,10 +8,12 @@ import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import java.util.List;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.StringTrieSearch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class AdsFilter extends Filter {
|
||||
@@ -22,14 +24,16 @@ public final class AdsFilter extends Filter {
|
||||
|
||||
// endregion
|
||||
|
||||
// https://encrypted-tbn0.gstatic.com/shopping?q=abc
|
||||
private static final String STORE_BANNER_DOMAIN = "gstatic.com/shopping";
|
||||
private static final boolean HIDE_END_SCREEN_STORE_BANNER =
|
||||
Settings.HIDE_END_SCREEN_STORE_BANNER.get();
|
||||
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
|
||||
private final StringFilterGroup playerShoppingShelf;
|
||||
private final ByteArrayFilterGroup playerShoppingShelfBuffer;
|
||||
|
||||
private final StringFilterGroup fullscreenOverlay;
|
||||
private final ByteArrayFilterGroup endScreenStoreBannerBuffer;
|
||||
|
||||
private final StringFilterGroup channelProfile;
|
||||
private final ByteArrayFilterGroup visitStoreButton;
|
||||
|
||||
@@ -69,7 +73,9 @@ public final class AdsFilter extends Filter {
|
||||
"full_width_square_image_layout",
|
||||
"video_display_button_group_layout",
|
||||
"landscape_image_wide_button_layout",
|
||||
"video_display_carousel_button_group_layout"
|
||||
"video_display_carousel_button_group_layout",
|
||||
"compact_landscape_image_layout", // Tablet layout search results.
|
||||
"text_image_no_button_layout" // Tablet layout search results.
|
||||
);
|
||||
|
||||
final var generalAds = new StringFilterGroup(
|
||||
@@ -116,32 +122,23 @@ public final class AdsFilter extends Filter {
|
||||
);
|
||||
|
||||
playerShoppingShelf = new StringFilterGroup(
|
||||
null,
|
||||
Settings.HIDE_PLAYER_STORE_SHELF,
|
||||
"horizontal_shelf.eml"
|
||||
);
|
||||
|
||||
playerShoppingShelfBuffer = new ByteArrayFilterGroup(
|
||||
Settings.HIDE_PLAYER_STORE_SHELF,
|
||||
null,
|
||||
"shopping_item_card_list.eml"
|
||||
);
|
||||
|
||||
fullscreenOverlay = new StringFilterGroup(
|
||||
Settings.HIDE_END_SCREEN_STORE_BANNER,
|
||||
"fullscreen_overlay.eml"
|
||||
);
|
||||
|
||||
endScreenStoreBannerBuffer = new ByteArrayFilterGroup(
|
||||
null,
|
||||
"gstatic.com/shopping"
|
||||
);
|
||||
|
||||
channelProfile = new StringFilterGroup(
|
||||
null,
|
||||
"channel_profile.eml"
|
||||
Settings.HIDE_VISIT_STORE_BUTTON,
|
||||
"channel_profile.eml",
|
||||
"page_header.eml"
|
||||
);
|
||||
|
||||
visitStoreButton = new ByteArrayFilterGroup(
|
||||
Settings.HIDE_VISIT_STORE_BUTTON,
|
||||
null,
|
||||
"header_store_button"
|
||||
);
|
||||
|
||||
@@ -167,7 +164,6 @@ public final class AdsFilter extends Filter {
|
||||
viewProducts,
|
||||
selfSponsor,
|
||||
fullscreenAd,
|
||||
fullscreenOverlay,
|
||||
channelProfile,
|
||||
webLinkPanel,
|
||||
shoppingLinks,
|
||||
@@ -186,10 +182,8 @@ public final class AdsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (matchedGroup == fullscreenOverlay) {
|
||||
if (contentIndex == 0 && endScreenStoreBannerBuffer.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
// Check for the index because of likelihood of false positives.
|
||||
if (matchedGroup == shoppingLinks && contentIndex != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -209,13 +203,25 @@ public final class AdsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for the index because of likelihood of false positives.
|
||||
if (matchedGroup == shoppingLinks && contentIndex != 0)
|
||||
return false;
|
||||
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* @param elementsList List of components of the end screen container.
|
||||
* @param protobufList Component (ProtobufList).
|
||||
*/
|
||||
public static void hideEndScreenStoreBanner(List<Object> elementsList, Object protobufList) {
|
||||
if (HIDE_END_SCREEN_STORE_BANNER && protobufList.toString().contains(STORE_BANNER_DOMAIN)) {
|
||||
Logger.printDebug(() -> "Hiding store banner");
|
||||
return;
|
||||
}
|
||||
|
||||
elementsList.add(protobufList);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hide the view, which shows ads in the homepage.
|
||||
*
|
||||
|
||||
@@ -6,8 +6,12 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
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 ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
|
||||
|
||||
private final StringFilterGroup likeSubscribeGlow;
|
||||
private final StringFilterGroup actionBarGroup;
|
||||
private final StringFilterGroup bufferFilterPathGroup;
|
||||
private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList();
|
||||
@@ -20,11 +24,19 @@ final class ButtonsFilter extends Filter {
|
||||
addIdentifierCallbacks(actionBarGroup);
|
||||
|
||||
|
||||
likeSubscribeGlow = new StringFilterGroup(
|
||||
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
|
||||
"animated_button_border.eml"
|
||||
);
|
||||
|
||||
bufferFilterPathGroup = new StringFilterGroup(
|
||||
null,
|
||||
"|ContainerType|button.eml|"
|
||||
);
|
||||
|
||||
addPathCallbacks(
|
||||
likeSubscribeGlow,
|
||||
bufferFilterPathGroup,
|
||||
new StringFilterGroup(
|
||||
Settings.HIDE_LIKE_DISLIKE_BUTTON,
|
||||
"|segmented_like_dislike_button"
|
||||
@@ -40,8 +52,7 @@ final class ButtonsFilter extends Filter {
|
||||
new StringFilterGroup(
|
||||
Settings.HIDE_CLIP_BUTTON,
|
||||
"|clip_button.eml|"
|
||||
),
|
||||
bufferFilterPathGroup
|
||||
)
|
||||
);
|
||||
|
||||
bufferButtonsGroupList.addAll(
|
||||
@@ -83,6 +94,15 @@ final class ButtonsFilter extends Filter {
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
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,
|
||||
// in case every filter group is enabled, hide the action bar.
|
||||
if (matchedGroup == actionBarGroup) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package app.revanced.extension.youtube.patches.components;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
@@ -44,13 +41,11 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
|
||||
return filterGroups.iterator();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@Override
|
||||
public void forEach(@NonNull Consumer<? super T> action) {
|
||||
filterGroups.forEach(action);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@NonNull
|
||||
@Override
|
||||
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 java.lang.Character.UnicodeBlock.*;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
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.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
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 android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
@@ -18,10 +16,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
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(
|
||||
"V.ED", // Playlist browse id.
|
||||
"java.lang.ref.WeakReference"
|
||||
@@ -38,16 +32,15 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private final StringFilterGroup inFeedSurvey;
|
||||
private final StringFilterGroup notifyMe;
|
||||
private final StringFilterGroup singleItemInformationPanel;
|
||||
private final StringFilterGroup expandableMetadata;
|
||||
private final ByteArrayFilterGroup searchResultRecommendations;
|
||||
private final StringFilterGroup searchResultVideo;
|
||||
private final StringFilterGroup compactChannelBarInner;
|
||||
private final StringFilterGroup compactChannelBarInnerButton;
|
||||
private final ByteArrayFilterGroup joinMembershipButton;
|
||||
private final StringFilterGroup likeSubscribeGlow;
|
||||
private final StringFilterGroup horizontalShelves;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public LayoutComponentsFilter() {
|
||||
exceptions.addPatterns(
|
||||
"home_video_with_context",
|
||||
@@ -81,7 +74,8 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"text_post_root_slim.eml",
|
||||
"post_base_wrapper_slim.eml",
|
||||
"poll_post_root.eml",
|
||||
"videos_post_root.eml"
|
||||
"videos_post_root.eml",
|
||||
"post_shelf_slim.eml"
|
||||
);
|
||||
|
||||
final var communityGuidelines = new StringFilterGroup(
|
||||
@@ -122,8 +116,12 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
);
|
||||
|
||||
final var infoPanel = new StringFilterGroup(
|
||||
Settings.HIDE_HIDE_INFO_PANELS,
|
||||
"publisher_transparency_panel",
|
||||
Settings.HIDE_INFO_PANELS,
|
||||
"publisher_transparency_panel"
|
||||
);
|
||||
|
||||
singleItemInformationPanel = new StringFilterGroup(
|
||||
Settings.HIDE_INFO_PANELS,
|
||||
"single_item_information_panel"
|
||||
);
|
||||
|
||||
@@ -216,10 +214,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"sponsorships"
|
||||
);
|
||||
|
||||
likeSubscribeGlow = new StringFilterGroup(
|
||||
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
|
||||
"animated_button_border.eml"
|
||||
);
|
||||
|
||||
final var channelWatermark = new StringFilterGroup(
|
||||
Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
|
||||
@@ -253,7 +247,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
expandableMetadata,
|
||||
inFeedSurvey,
|
||||
notifyMe,
|
||||
likeSubscribeGlow,
|
||||
compactChannelBar,
|
||||
communityPosts,
|
||||
paidPromotion,
|
||||
@@ -268,6 +261,7 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
compactChannelBarInner,
|
||||
medicalPanel,
|
||||
infoPanel,
|
||||
singleItemInformationPanel,
|
||||
emergencyBox,
|
||||
subscribersCommunityGuidelines,
|
||||
channelGuidelines,
|
||||
@@ -284,6 +278,19 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
@Override
|
||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||
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 (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
|
||||
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
@@ -291,15 +298,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
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.
|
||||
// Filter them separately here.
|
||||
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package app.revanced.extension.youtube.patches.components;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
@@ -16,7 +13,6 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||
private final ByteArrayFilterGroup exception;
|
||||
private final StringFilterGroup videoQualityMenuFooter;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public PlayerFlyoutMenuItemsFilter() {
|
||||
exception = new ByteArrayFilterGroup(
|
||||
// Whitelist Quality menu item when "Hide Additional settings menu" is enabled
|
||||
|
||||
@@ -6,11 +6,19 @@ import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
|
||||
import com.airbnb.lottie.LottieAnimationView;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Scanner;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -93,17 +101,6 @@ public final class SeekbarColorPatch {
|
||||
return customSeekbarColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point
|
||||
*/
|
||||
public static boolean useLotteLaunchSplashScreen(boolean original) {
|
||||
Logger.printDebug(() -> "useLotteLaunchSplashScreen original: " + original);
|
||||
|
||||
if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false;
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
private static int colorChannelTo3Bits(int channel8Bits) {
|
||||
final float channel3Bits = channel8Bits * 7 / 255f;
|
||||
|
||||
@@ -127,6 +124,17 @@ public final class SeekbarColorPatch {
|
||||
/**
|
||||
* Injection point
|
||||
*/
|
||||
public static boolean useLotteLaunchSplashScreen(boolean original) {
|
||||
// This method is only used for development purposes to force the old style launch screen.
|
||||
// Forcing this off on some devices can cause unexplained startup crashes,
|
||||
// where the lottie animation is still used even though this condition appears to bypass it.
|
||||
return original; // false = drawable style, true = lottie style.
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Old drawable style launch screen.
|
||||
*/
|
||||
public static void setSplashAnimationDrawableTheme(AnimatedVectorDrawable vectorDrawable) {
|
||||
// Alternatively a ColorMatrixColorFilter can be used to change the color of the drawable
|
||||
// without using any styles, but a color filter cannot selectively change the seekbar
|
||||
@@ -134,6 +142,8 @@ public final class SeekbarColorPatch {
|
||||
// Even if the seekbar color xml value is changed to a completely different color (such as green),
|
||||
// a color filter still cannot be selectively applied when the drawable has more than 1 color.
|
||||
try {
|
||||
// Must set the color even if custom seekbar is off,
|
||||
// because the xml color was replaced with a themed value.
|
||||
String seekbarStyle = get9BitStyleIdentifier(customSeekbarColor);
|
||||
Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle);
|
||||
|
||||
@@ -154,6 +164,77 @@ public final class SeekbarColorPatch {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Modern Lottie style animation.
|
||||
*/
|
||||
public static void setSplashAnimationLottie(LottieAnimationView view, int resourceId) {
|
||||
try {
|
||||
if (!SEEKBAR_CUSTOM_COLOR_ENABLED) {
|
||||
view.patch_setAnimation(resourceId);
|
||||
return;
|
||||
}
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (false) { // Set true to force slow animation for development.
|
||||
final int longAnimation = Utils.getResourceIdentifier(
|
||||
Utils.isDarkModeEnabled(Utils.getContext())
|
||||
? "startup_animation_5s_30fps_dark"
|
||||
: "startup_animation_5s_30fps_light",
|
||||
"raw");
|
||||
if (longAnimation != 0) {
|
||||
resourceId = longAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
// Must specify primary key name otherwise the morphing YT logo color is also changed.
|
||||
String originalKey = "\"k\":";
|
||||
String originalPrimary = originalKey + "[1,0,0.2,1]";
|
||||
String originalAccent = originalKey + "[1,0.152941176471,0.56862745098,1]";
|
||||
|
||||
String replacementPrimary = originalKey + getColorStringArray(customSeekbarColor);
|
||||
String replacementAccent = originalKey + getColorStringArray(customSeekbarColorGradient[1]);
|
||||
|
||||
String json = loadRawResourceAsString(resourceId);
|
||||
if (json == null) {
|
||||
return; // Should never happen.
|
||||
}
|
||||
|
||||
if (BaseSettings.DEBUG.get() && (!json.contains(originalPrimary) || !json.contains(originalAccent))) {
|
||||
String jsonFinal = json;
|
||||
Logger.printException(() -> "Could not replace launch animation colors: " + jsonFinal);
|
||||
}
|
||||
|
||||
Logger.printDebug(() -> "Replacing Lottie animation JSON");
|
||||
json = json.replace(originalPrimary, replacementPrimary);
|
||||
json = json.replace(originalAccent, replacementAccent);
|
||||
|
||||
// cacheKey is not needed since the animation will not be reused.
|
||||
view.patch_setAnimation(new ByteArrayInputStream(json.getBytes()), null);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setSplashAnimationLottie failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getColorStringArray(int color) {
|
||||
return Arrays.toString(new double[]{
|
||||
Color.red(color) / 255.0,
|
||||
Color.green(color) / 255.0,
|
||||
Color.blue(color) / 255.0,
|
||||
Color.alpha(color) / 255.0
|
||||
});
|
||||
}
|
||||
|
||||
private static String loadRawResourceAsString(int resourceId) {
|
||||
try (InputStream inputStream = Utils.getContext().getResources().openRawResource(resourceId);
|
||||
Scanner scanner = new Scanner(inputStream, StandardCharsets.UTF_8.name()).useDelimiter("\\A")) {
|
||||
return scanner.next();
|
||||
} catch (IOException e) {
|
||||
Logger.printException(() -> "Could not load resource: " + resourceId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
|
||||
@@ -234,6 +234,12 @@ public class ReturnYouTubeDislike {
|
||||
// example video: https://www.youtube.com/watch?v=UnrU5vxCHxw
|
||||
// RYD data: https://returnyoutubedislikeapi.com/votes?videoId=UnrU5vxCHxw
|
||||
//
|
||||
if (!Settings.RYD_ESTIMATED_LIKE.get()) {
|
||||
// Change the "Likes" string to show that likes and dislikes are hidden.
|
||||
String hiddenMessageString = str("revanced_ryd_video_likes_hidden_by_video_owner");
|
||||
return newSpanUsingStylingOfAnotherSpan(oldSpannable, hiddenMessageString);
|
||||
}
|
||||
|
||||
Logger.printDebug(() -> "Using estimated likes");
|
||||
oldLikes = formatDislikeCount(voteData.getLikeCount());
|
||||
}
|
||||
@@ -346,56 +352,52 @@ public class ReturnYouTubeDislike {
|
||||
}
|
||||
|
||||
private static String formatDislikeCount(long dislikeCount) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
|
||||
if (dislikeCountFormatter == null) {
|
||||
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
|
||||
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
|
||||
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe.
|
||||
if (dislikeCountFormatter == null) {
|
||||
// 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);
|
||||
|
||||
// YouTube disregards locale specific number characters
|
||||
// and instead shows english number characters everywhere.
|
||||
// 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 "۱,۲۳٤"
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||
dislikeCountFormatter.setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
// YouTube disregards locale specific number characters
|
||||
// and instead shows English number characters everywhere.
|
||||
// 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 "۱,۲۳٤"
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||
dislikeCountFormatter.setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
return dislikeCountFormatter.format(dislikeCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Will never be reached, as the oldest supported YouTube app requires Android N or greater.
|
||||
return String.valueOf(dislikeCount);
|
||||
return dislikeCountFormatter.format(dislikeCount);
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatDislikePercentage(float dislikePercentage) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize
|
||||
if (dislikePercentageFormatter == null) {
|
||||
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
|
||||
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
|
||||
synchronized (ReturnYouTubeDislike.class) { // Number formatter is not thread safe, must synchronize.
|
||||
if (dislikePercentageFormatter == null) {
|
||||
Locale locale = Locale.getDefault();
|
||||
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
|
||||
|
||||
// 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
|
||||
&& dislikePercentageFormatter instanceof DecimalFormat) {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||
((DecimalFormat) dislikePercentageFormatter).setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
// 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
|
||||
&& dislikePercentageFormatter instanceof DecimalFormat decimalFormatter) {
|
||||
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
|
||||
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
|
||||
decimalFormatter.setDecimalFormatSymbols(symbols);
|
||||
}
|
||||
if (dislikePercentage >= 0.01) { // at least 1%
|
||||
dislikePercentageFormatter.setMaximumFractionDigits(0); // show only whole percentage points
|
||||
} else {
|
||||
dislikePercentageFormatter.setMaximumFractionDigits(1); // show up to 1 digit precision
|
||||
}
|
||||
return dislikePercentageFormatter.format(dislikePercentage);
|
||||
}
|
||||
}
|
||||
|
||||
// Will never be reached, as the oldest supported YouTube app requires Android N or greater.
|
||||
return String.valueOf((int) (dislikePercentage * 100));
|
||||
if (dislikePercentage >= 0.01) { // at least 1%
|
||||
dislikePercentageFormatter.setMaximumFractionDigits(0); // show only whole percentage points
|
||||
} else {
|
||||
dislikePercentageFormatter.setMaximumFractionDigits(1); // show up to 1 digit precision
|
||||
}
|
||||
|
||||
return dislikePercentageFormatter.format(dislikePercentage);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -403,15 +405,13 @@ public class ReturnYouTubeDislike {
|
||||
Objects.requireNonNull(videoId);
|
||||
synchronized (fetchCache) {
|
||||
// Remove any expired entries.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
final long now = System.currentTimeMillis();
|
||||
fetchCache.values().removeIf(value -> {
|
||||
final boolean expired = value.isExpired(now);
|
||||
if (expired)
|
||||
Logger.printDebug(() -> "Removing expired fetch: " + value.videoId);
|
||||
return expired;
|
||||
});
|
||||
}
|
||||
final long now = System.currentTimeMillis();
|
||||
fetchCache.values().removeIf(value -> {
|
||||
final boolean expired = value.isExpired(now);
|
||||
if (expired)
|
||||
Logger.printDebug(() -> "Removing expired fetch: " + value.videoId);
|
||||
return expired;
|
||||
});
|
||||
|
||||
ReturnYouTubeDislike fetch = fetchCache.get(videoId);
|
||||
if (fetch == null) {
|
||||
@@ -551,6 +551,15 @@ public class ReturnYouTubeDislike {
|
||||
}
|
||||
|
||||
if (spanIsForLikes) {
|
||||
if (!Utils.containsNumber(original)) {
|
||||
if (!Settings.RYD_ESTIMATED_LIKE.get()) {
|
||||
Logger.printDebug(() -> "Likes are hidden");
|
||||
return original;
|
||||
} else {
|
||||
Logger.printDebug(() -> "Using estimated likes");
|
||||
}
|
||||
}
|
||||
|
||||
// Scrolling Shorts does not cause the Spans to be reloaded,
|
||||
// so there is no need to cache the likes for this situations.
|
||||
Logger.printDebug(() -> "Creating likes span for: " + votingData.videoId);
|
||||
|
||||
@@ -5,15 +5,12 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
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.youtube.ThemeHelper;
|
||||
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.ReturnYouTubeDislikePreferenceFragment;
|
||||
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
|
||||
@@ -66,6 +64,10 @@ public class LicenseActivityHook {
|
||||
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
|
||||
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
|
||||
// half broken appearance because all the preference icons may not be available yet.
|
||||
@@ -79,7 +81,6 @@ public class LicenseActivityHook {
|
||||
* <p>
|
||||
* Hooks LicenseActivity#onCreate in order to inject our own fragment.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
public static void initialize(Activity licenseActivity) {
|
||||
try {
|
||||
ThemeHelper.setActivityTheme(licenseActivity);
|
||||
@@ -119,15 +120,13 @@ public class LicenseActivityHook {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressLint("UseCompatLoadingForDrawables")
|
||||
private static void createToolbar(Activity activity, String toolbarTitleResourceName) {
|
||||
// Replace dummy placeholder toolbar.
|
||||
// This is required to fix submenu title alignment issue with Android ASOP 15+
|
||||
ViewGroup toolBarParent = activity.findViewById(
|
||||
getResourceIdentifier("revanced_toolbar_parent", "id"));
|
||||
ViewGroup dummyToolbar = toolBarParent.findViewById(getResourceIdentifier(
|
||||
"revanced_toolbar", "id"));
|
||||
ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
|
||||
toolbarLayoutParams = dummyToolbar.getLayoutParams();
|
||||
toolBarParent.removeView(dummyToolbar);
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public class Settings extends BaseSettings {
|
||||
|
||||
// Ads
|
||||
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE);
|
||||
public static final BooleanSetting HIDE_END_SCREEN_STORE_BANNER = new BooleanSetting("revanced_hide_end_screen_store_banner", FALSE);
|
||||
public static final BooleanSetting HIDE_END_SCREEN_STORE_BANNER = new BooleanSetting("revanced_hide_end_screen_store_banner", TRUE, true);
|
||||
public static final BooleanSetting HIDE_FULLSCREEN_ADS = new BooleanSetting("revanced_hide_fullscreen_ads", TRUE);
|
||||
public static final BooleanSetting HIDE_GENERAL_ADS = new BooleanSetting("revanced_hide_general_ads", TRUE);
|
||||
public static final BooleanSetting HIDE_GET_PREMIUM = new BooleanSetting("revanced_hide_get_premium", TRUE);
|
||||
@@ -124,7 +124,6 @@ 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_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_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_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);
|
||||
@@ -137,7 +136,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
|
||||
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
|
||||
public static final BooleanSetting HIDE_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_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);
|
||||
@@ -185,6 +184,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_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
|
||||
// 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_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);
|
||||
@@ -228,7 +228,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_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 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_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);
|
||||
@@ -306,20 +308,21 @@ public class Settings extends BaseSettings {
|
||||
|
||||
// Swipe controls
|
||||
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_VOLUME = new BooleanSetting("revanced_swipe_volume", 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, true);
|
||||
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127, true,
|
||||
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
|
||||
// Debugging
|
||||
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true,
|
||||
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
|
||||
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
|
||||
@@ -332,6 +335,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting RYD_SHORTS = new BooleanSetting("ryd_shorts", TRUE, parent(RYD_ENABLED));
|
||||
public static final BooleanSetting RYD_DISLIKE_PERCENTAGE = new BooleanSetting("ryd_dislike_percentage", FALSE, parent(RYD_ENABLED));
|
||||
public static final BooleanSetting RYD_COMPACT_LAYOUT = new BooleanSetting("ryd_compact_layout", FALSE, parent(RYD_ENABLED));
|
||||
public static final BooleanSetting RYD_ESTIMATED_LIKE = new BooleanSetting("ryd_estimated_like", TRUE, parent(RYD_ENABLED));
|
||||
public static final BooleanSetting RYD_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("ryd_toast_on_connection_error", TRUE, parent(RYD_ENABLED));
|
||||
|
||||
// SponsorBlock
|
||||
@@ -343,13 +347,14 @@ public class Settings extends BaseSettings {
|
||||
public static final IntegerSetting SB_CREATE_NEW_SEGMENT_STEP = new IntegerSetting("sb_create_new_segment_step", 150, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_VOTING_BUTTON = new BooleanSetting("sb_voting_button", FALSE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_CREATE_NEW_SEGMENT = new BooleanSetting("sb_create_new_segment", FALSE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_SQUARE_LAYOUT = new BooleanSetting("sb_square_layout", FALSE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_COMPACT_SKIP_BUTTON = new BooleanSetting("sb_compact_skip_button", FALSE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_AUTO_HIDE_SKIP_BUTTON = new BooleanSetting("sb_auto_hide_skip_button", TRUE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_TOAST_ON_SKIP = new BooleanSetting("sb_toast_on_skip", TRUE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("sb_toast_on_connection_error", TRUE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_TRACK_SKIP_COUNT = new BooleanSetting("sb_track_skip_count", TRUE, parent(SB_ENABLED));
|
||||
public static final FloatSetting SB_SEGMENT_MIN_DURATION = new FloatSetting("sb_min_segment_duration", 0F, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_VIDEO_LENGTH_WITHOUT_SEGMENTS = new BooleanSetting("sb_video_length_without_segments", TRUE, parent(SB_ENABLED));
|
||||
public static final BooleanSetting SB_VIDEO_LENGTH_WITHOUT_SEGMENTS = new BooleanSetting("sb_video_length_without_segments", FALSE, parent(SB_ENABLED));
|
||||
public static final StringSetting SB_API_URL = new StringSetting("sb_api_url", "https://sponsor.ajay.app");
|
||||
public static final BooleanSetting SB_USER_IS_VIP = new BooleanSetting("sb_user_is_vip", FALSE);
|
||||
public static final IntegerSetting SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS = new IntegerSetting("sb_local_time_saved_number_segments", 0);
|
||||
@@ -400,12 +405,6 @@ public class Settings extends BaseSettings {
|
||||
|
||||
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
|
||||
|
||||
// Old spoof versions that no longer work reliably.
|
||||
if (SPOOF_APP_VERSION_TARGET.get().compareTo(SPOOF_APP_VERSION_TARGET.defaultValue) < 0) {
|
||||
Logger.printInfo(() -> "Resetting spoof app version target");
|
||||
SPOOF_APP_VERSION_TARGET.resetToDefault();
|
||||
}
|
||||
|
||||
// Migrate renamed enum.
|
||||
//noinspection deprecation
|
||||
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {
|
||||
@@ -436,6 +435,11 @@ public class Settings extends BaseSettings {
|
||||
DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.resetToDefault();
|
||||
}
|
||||
|
||||
if (!DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.isSetToDefault()) {
|
||||
SWIPE_OVERLAY_OPACITY.save(DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.get() / 255);
|
||||
DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.resetToDefault();
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region SB import/export callbacks
|
||||
|
||||
@@ -3,18 +3,14 @@ package app.revanced.extension.youtube.settings.preference;
|
||||
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.preference.Preference;
|
||||
import android.text.Html;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
/**
|
||||
* Allows using basic html for the summary text.
|
||||
*/
|
||||
@SuppressWarnings({"unused", "deprecation"})
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public class HtmlPreference extends Preference {
|
||||
{
|
||||
setSummary(Html.fromHtml(getSummary().toString(), FROM_HTML_MODE_COMPACT));
|
||||
|
||||
@@ -17,8 +17,6 @@ import android.view.WindowInsets;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -98,7 +96,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
listPreference.setEntryValues(sortedEntryValues);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
@@ -153,13 +150,10 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||
toolbar.setTitle(childScreen.getTitle());
|
||||
toolbar.setNavigationIcon(getBackButtonDrawable());
|
||||
toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss());
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
final int margin = (int) TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
|
||||
);
|
||||
toolbar.setTitleMargin(margin, 0, margin, 0);
|
||||
}
|
||||
final int margin = (int) TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
|
||||
);
|
||||
toolbar.setTitleMargin(margin, 0, margin, 0);
|
||||
|
||||
TextView toolbarTextView = Utils.getChildView(toolbar,
|
||||
true, TextView.class::isInstance);
|
||||
|
||||
@@ -39,6 +39,11 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
||||
*/
|
||||
private SwitchPreference compactLayoutPreference;
|
||||
|
||||
/**
|
||||
* If hidden likes are replaced with an estimated value.
|
||||
*/
|
||||
private SwitchPreference estimatedLikesPreference;
|
||||
|
||||
/**
|
||||
* If segmented like/dislike button uses smaller compact layout.
|
||||
*/
|
||||
@@ -48,6 +53,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
||||
shortsPreference.setEnabled(Settings.RYD_SHORTS.isAvailable());
|
||||
percentagePreference.setEnabled(Settings.RYD_DISLIKE_PERCENTAGE.isAvailable());
|
||||
compactLayoutPreference.setEnabled(Settings.RYD_COMPACT_LAYOUT.isAvailable());
|
||||
estimatedLikesPreference.setEnabled(Settings.RYD_ESTIMATED_LIKE.isAvailable());
|
||||
toastOnRYDNotAvailable.setEnabled(Settings.RYD_TOAST_ON_CONNECTION_ERROR.isAvailable());
|
||||
}
|
||||
|
||||
@@ -117,6 +123,19 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
|
||||
});
|
||||
preferenceScreen.addPreference(compactLayoutPreference);
|
||||
|
||||
estimatedLikesPreference = new SwitchPreference(context);
|
||||
estimatedLikesPreference.setChecked(Settings.RYD_ESTIMATED_LIKE.get());
|
||||
estimatedLikesPreference.setTitle(str("revanced_ryd_estimated_like_title"));
|
||||
estimatedLikesPreference.setSummaryOn(str("revanced_ryd_estimated_like_summary_on"));
|
||||
estimatedLikesPreference.setSummaryOff(str("revanced_ryd_estimated_like_summary_off"));
|
||||
estimatedLikesPreference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
Settings.RYD_ESTIMATED_LIKE.save((Boolean) newValue);
|
||||
ReturnYouTubeDislike.clearAllUICaches();
|
||||
updateUIState();
|
||||
return true;
|
||||
});
|
||||
preferenceScreen.addPreference(estimatedLikesPreference);
|
||||
|
||||
toastOnRYDNotAvailable = new SwitchPreference(context);
|
||||
toastOnRYDNotAvailable.setChecked(Settings.RYD_TOAST_ON_CONNECTION_ERROR.get());
|
||||
toastOnRYDNotAvailable.setTitle(str("revanced_ryd_toast_on_connection_error_title"));
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.*;
|
||||
import android.text.Html;
|
||||
@@ -37,8 +36,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
private SwitchPreference sbEnabled;
|
||||
private SwitchPreference addNewSegment;
|
||||
private SwitchPreference votingEnabled;
|
||||
private SwitchPreference compactSkipButton;
|
||||
private SwitchPreference autoHideSkipSegmentButton;
|
||||
private SwitchPreference compactSkipButton;
|
||||
private SwitchPreference squareLayout;
|
||||
private SwitchPreference showSkipToast;
|
||||
private SwitchPreference trackSkips;
|
||||
private SwitchPreference showTimeWithoutSegments;
|
||||
@@ -62,7 +62,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
} else if (!Settings.SB_CREATE_NEW_SEGMENT.get()) {
|
||||
SponsorBlockViewController.hideNewSegmentLayout();
|
||||
}
|
||||
// Voting and add new segment buttons automatically shows/hide themselves.
|
||||
// Voting and add new segment buttons automatically show/hide themselves.
|
||||
|
||||
SponsorBlockViewController.updateLayout();
|
||||
|
||||
sbEnabled.setChecked(enabled);
|
||||
|
||||
@@ -72,11 +74,14 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
votingEnabled.setChecked(Settings.SB_VOTING_BUTTON.get());
|
||||
votingEnabled.setEnabled(enabled);
|
||||
|
||||
autoHideSkipSegmentButton.setEnabled(enabled);
|
||||
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get());
|
||||
|
||||
compactSkipButton.setChecked(Settings.SB_COMPACT_SKIP_BUTTON.get());
|
||||
compactSkipButton.setEnabled(enabled);
|
||||
|
||||
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get());
|
||||
autoHideSkipSegmentButton.setEnabled(enabled);
|
||||
squareLayout.setChecked(Settings.SB_SQUARE_LAYOUT.get());
|
||||
squareLayout.setEnabled(enabled);
|
||||
|
||||
showSkipToast.setChecked(Settings.SB_TOAST_ON_SKIP.get());
|
||||
showSkipToast.setEnabled(enabled);
|
||||
@@ -176,6 +181,17 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
return true;
|
||||
});
|
||||
|
||||
autoHideSkipSegmentButton = new SwitchPreference(context);
|
||||
autoHideSkipSegmentButton.setTitle(str("revanced_sb_enable_auto_hide_skip_segment_button"));
|
||||
autoHideSkipSegmentButton.setSummaryOn(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_on"));
|
||||
autoHideSkipSegmentButton.setSummaryOff(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_off"));
|
||||
category.addPreference(autoHideSkipSegmentButton);
|
||||
autoHideSkipSegmentButton.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
Settings.SB_AUTO_HIDE_SKIP_BUTTON.save((Boolean) newValue);
|
||||
updateUI();
|
||||
return true;
|
||||
});
|
||||
|
||||
compactSkipButton = new SwitchPreference(context);
|
||||
compactSkipButton.setTitle(str("revanced_sb_enable_compact_skip_button"));
|
||||
compactSkipButton.setSummaryOn(str("revanced_sb_enable_compact_skip_button_sum_on"));
|
||||
@@ -187,13 +203,13 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
return true;
|
||||
});
|
||||
|
||||
autoHideSkipSegmentButton = new SwitchPreference(context);
|
||||
autoHideSkipSegmentButton.setTitle(str("revanced_sb_enable_auto_hide_skip_segment_button"));
|
||||
autoHideSkipSegmentButton.setSummaryOn(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_on"));
|
||||
autoHideSkipSegmentButton.setSummaryOff(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_off"));
|
||||
category.addPreference(autoHideSkipSegmentButton);
|
||||
autoHideSkipSegmentButton.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
Settings.SB_AUTO_HIDE_SKIP_BUTTON.save((Boolean) newValue);
|
||||
squareLayout = new SwitchPreference(context);
|
||||
squareLayout.setTitle(str("revanced_sb_square_layout"));
|
||||
squareLayout.setSummaryOn(str("revanced_sb_square_layout_sum_on"));
|
||||
squareLayout.setSummaryOff(str("revanced_sb_square_layout_sum_off"));
|
||||
category.addPreference(squareLayout);
|
||||
squareLayout.setOnPreferenceChangeListener((preference1, newValue) -> {
|
||||
Settings.SB_SQUARE_LAYOUT.save((Boolean) newValue);
|
||||
updateUI();
|
||||
return true;
|
||||
});
|
||||
@@ -393,9 +409,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT
|
||||
| InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
importExport.getEditText().setAutofillHints((String) null);
|
||||
}
|
||||
importExport.getEditText().setAutofillHints((String) null);
|
||||
importExport.getEditText().setTextSize(TypedValue.COMPLEX_UNIT_PT, 8);
|
||||
importExport.setOnPreferenceClickListener(preference1 -> {
|
||||
importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings());
|
||||
|
||||
@@ -85,10 +85,12 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
||||
String summary = str(key + "_summary");
|
||||
|
||||
// Android VR supports AV1 but all other clients do not.
|
||||
if (clientType != ClientType.ANDROID_VR && clientType != ClientType.ANDROID_VR_NO_AUTH) {
|
||||
if (clientType != ClientType.ANDROID_VR_AUTH && clientType != ClientType.ANDROID_VR_NO_AUTH) {
|
||||
summary += '\n' + str("revanced_spoof_video_streams_about_no_av1");
|
||||
}
|
||||
|
||||
summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos");
|
||||
|
||||
setTitle(title);
|
||||
setSummary(summary);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,9 @@ package app.revanced.extension.youtube.shared;
|
||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
@@ -257,7 +255,6 @@ public final class NavigationBar {
|
||||
* Injection point.
|
||||
* Fixes missing drawable.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.N)
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
|
||||
if (fillBellCairoBlack != 0) {
|
||||
|
||||
@@ -21,9 +21,6 @@ enum class PlayerType {
|
||||
|
||||
/**
|
||||
* 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_MAXIMIZED,
|
||||
@@ -56,8 +53,7 @@ enum class PlayerType {
|
||||
val newType = nameToPlayerType[enumName]
|
||||
if (newType == null) {
|
||||
Logger.printException { "Unknown PlayerType encountered: $enumName" }
|
||||
} else if (current != newType) {
|
||||
Logger.printDebug { "PlayerType changed to: $newType" }
|
||||
} else {
|
||||
current = newType
|
||||
}
|
||||
}
|
||||
@@ -68,9 +64,13 @@ enum class PlayerType {
|
||||
@JvmStatic
|
||||
var current
|
||||
get() = currentPlayerType
|
||||
private set(value) {
|
||||
currentPlayerType = value
|
||||
onChange(currentPlayerType)
|
||||
private set(type) {
|
||||
if (currentPlayerType != type) {
|
||||
Logger.printDebug { "Changed to: $type" }
|
||||
|
||||
currentPlayerType = type
|
||||
onChange(type)
|
||||
}
|
||||
}
|
||||
|
||||
@Volatile // Read/write from different threads.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@ package app.revanced.extension.youtube.sponsorblock.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
@@ -14,15 +15,15 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
||||
import static app.revanced.extension.shared.Utils.getResourceColor;
|
||||
import static app.revanced.extension.shared.Utils.getResourceDimensionPixelSize;
|
||||
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||
|
||||
public final class NewSegmentLayout extends FrameLayout {
|
||||
private static final ColorStateList rippleColorStateList = new ColorStateList(
|
||||
new int[][]{new int[]{android.R.attr.state_enabled}},
|
||||
new int[]{0x33ffffff} // sets the ripple color to white
|
||||
new int[]{0x33ffffff} // Ripple effect color (semi-transparent white)
|
||||
);
|
||||
private final int rippleEffectId;
|
||||
|
||||
final int defaultBottomMargin;
|
||||
final int ctaBottomMargin;
|
||||
@@ -47,10 +48,6 @@ public final class NewSegmentLayout extends FrameLayout {
|
||||
getResourceIdentifier(context, "revanced_sb_new_segment", "layout"), this, true
|
||||
);
|
||||
|
||||
TypedValue rippleEffect = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, rippleEffect, true);
|
||||
rippleEffectId = rippleEffect.resourceId;
|
||||
|
||||
initializeButton(
|
||||
context,
|
||||
"revanced_sb_new_segment_rewind",
|
||||
@@ -120,6 +117,28 @@ public final class NewSegmentLayout extends FrameLayout {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the layout of this UI control.
|
||||
*/
|
||||
public void updateLayout() {
|
||||
final boolean squareLayout = Settings.SB_SQUARE_LAYOUT.get();
|
||||
|
||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) getLayoutParams();
|
||||
final int margin = squareLayout
|
||||
? 0
|
||||
: SponsorBlockViewController.ROUNDED_LAYOUT_MARGIN;
|
||||
params.setMarginStart(margin);
|
||||
setLayoutParams(params);
|
||||
|
||||
GradientDrawable backgroundDrawable = new GradientDrawable();
|
||||
backgroundDrawable.setColor(getResourceColor("skip_ad_button_background_color"));
|
||||
final float cornerRadius = squareLayout
|
||||
? 0
|
||||
: 16 * getResources().getDisplayMetrics().density;
|
||||
backgroundDrawable.setCornerRadius(cornerRadius);
|
||||
setBackground(backgroundDrawable);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ButtonOnClickHandlerFunction {
|
||||
void apply();
|
||||
|
||||
@@ -8,6 +8,7 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -19,11 +20,19 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
|
||||
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
|
||||
|
||||
public class SkipSponsorButton extends FrameLayout {
|
||||
private static final boolean highContrast = true;
|
||||
/**
|
||||
* Adds a high contrast border around the skip button.
|
||||
*
|
||||
* This feature is not currently used.
|
||||
* If this is added, it needs an additional button width change because
|
||||
* as-is the skip button text is clipped when this is on.
|
||||
*/
|
||||
private static final boolean highContrast = false;
|
||||
private final LinearLayout skipSponsorBtnContainer;
|
||||
private final TextView skipSponsorTextView;
|
||||
private final Paint background;
|
||||
@@ -49,18 +58,23 @@ public class SkipSponsorButton extends FrameLayout {
|
||||
|
||||
LayoutInflater.from(context).inflate(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button", "layout"), this, true); // layout:skip_ad_button
|
||||
setMinimumHeight(getResourceDimensionPixelSize("ad_skip_ad_button_min_height")); // dimen:ad_skip_ad_button_min_height
|
||||
skipSponsorBtnContainer = Objects.requireNonNull((LinearLayout) findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_container", "id"))); // id:skip_ad_button_container
|
||||
skipSponsorBtnContainer = Objects.requireNonNull(findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_container", "id"))); // id:skip_ad_button_container
|
||||
|
||||
background = new Paint();
|
||||
background.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color);
|
||||
background.setStyle(Paint.Style.FILL);
|
||||
|
||||
border = new Paint();
|
||||
border.setColor(getResourceColor("skip_ad_button_border_color")); // color:skip_ad_button_border_color);
|
||||
border.setStrokeWidth(getResourceDimension("ad_skip_ad_button_border_width")); // dimen:ad_skip_ad_button_border_width);
|
||||
border.setStyle(Paint.Style.STROKE);
|
||||
skipSponsorTextView = Objects.requireNonNull((TextView) findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_text", "id"))); // id:skip_ad_button_text;
|
||||
|
||||
skipSponsorTextView = Objects.requireNonNull(findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_text", "id"))); // id:skip_ad_button_text;
|
||||
defaultBottomMargin = getResourceDimensionPixelSize("skip_button_default_bottom_margin"); // dimen:skip_button_default_bottom_margin
|
||||
ctaBottomMargin = getResourceDimensionPixelSize("skip_button_cta_bottom_margin"); // dimen:skip_button_cta_bottom_margin
|
||||
|
||||
updateLayout();
|
||||
|
||||
skipSponsorBtnContainer.setOnClickListener(v -> {
|
||||
// The view controller handles hiding this button, but hide it here as well just in case something goofs.
|
||||
setVisibility(View.GONE);
|
||||
@@ -72,30 +86,56 @@ public class SkipSponsorButton extends FrameLayout {
|
||||
protected final void dispatchDraw(Canvas canvas) {
|
||||
final int left = skipSponsorBtnContainer.getLeft();
|
||||
final int top = skipSponsorBtnContainer.getTop();
|
||||
final int leftPlusWidth = (left + skipSponsorBtnContainer.getWidth());
|
||||
final int topPlusHeight = (top + skipSponsorBtnContainer.getHeight());
|
||||
canvas.drawRect(left, top, leftPlusWidth, topPlusHeight, background);
|
||||
if (!highContrast) {
|
||||
canvas.drawLines(new float[]{
|
||||
leftPlusWidth, top, left, top,
|
||||
left, top, left, topPlusHeight,
|
||||
left, topPlusHeight, leftPlusWidth, topPlusHeight},
|
||||
border);
|
||||
final int right = left + skipSponsorBtnContainer.getWidth();
|
||||
final int bottom = top + skipSponsorBtnContainer.getHeight();
|
||||
|
||||
// Determine corner radius for rounded button
|
||||
float cornerRadius = skipSponsorBtnContainer.getHeight() / 2f;
|
||||
|
||||
if (Settings.SB_SQUARE_LAYOUT.get()) {
|
||||
// Square button.
|
||||
canvas.drawRect(left, top, right, bottom, background);
|
||||
if (highContrast) {
|
||||
canvas.drawLines(new float[]{
|
||||
right, top, left, top,
|
||||
left, top, left, bottom,
|
||||
left, bottom, right, bottom},
|
||||
border); // Draw square border.
|
||||
}
|
||||
} else {
|
||||
// Rounded button.
|
||||
RectF rect = new RectF(left, top, right, bottom);
|
||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, background); // Draw rounded background.
|
||||
if (highContrast) {
|
||||
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, border); // Draw rounded border.
|
||||
}
|
||||
}
|
||||
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true, if this button state was changed
|
||||
* Update the layout of this button.
|
||||
*/
|
||||
public boolean updateSkipButtonText(@NonNull SponsorSegment segment) {
|
||||
public void updateLayout() {
|
||||
if (Settings.SB_SQUARE_LAYOUT.get()) {
|
||||
// No padding for square corners.
|
||||
setPadding(0, 0, 0, 0);
|
||||
} else {
|
||||
// Apply padding for rounded corners.
|
||||
final int padding = SponsorBlockViewController.ROUNDED_LAYOUT_MARGIN;
|
||||
setPadding(padding, 0, padding, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateSkipButtonText(@NonNull SponsorSegment segment) {
|
||||
this.segment = segment;
|
||||
CharSequence newText = segment.getSkipButtonText();
|
||||
|
||||
//noinspection StringEqualsCharSequence
|
||||
if (newText.equals(skipSponsorTextView.getText())) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
skipSponsorTextView.setText(newText);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,11 @@ import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
|
||||
import kotlin.Unit;
|
||||
|
||||
public class SponsorBlockViewController {
|
||||
public static final int ROUNDED_LAYOUT_MARGIN = 12;
|
||||
|
||||
private static WeakReference<RelativeLayout> inlineSponsorOverlayRef = new WeakReference<>(null);
|
||||
private static WeakReference<ViewGroup> youtubeOverlaysLayoutRef = new WeakReference<>(null);
|
||||
private static WeakReference<SkipSponsorButton> skipHighlightButtonRef = new WeakReference<>(null);
|
||||
@@ -36,7 +39,7 @@ public class SponsorBlockViewController {
|
||||
static {
|
||||
PlayerType.getOnChange().addObserver((PlayerType type) -> {
|
||||
playerTypeChanged(type);
|
||||
return null;
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,12 +83,16 @@ public class SponsorBlockViewController {
|
||||
});
|
||||
youtubeOverlaysLayoutRef = new WeakReference<>(viewGroup);
|
||||
|
||||
skipHighlightButtonRef = new WeakReference<>(
|
||||
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_skip_highlight_button", "id"))));
|
||||
skipSponsorButtonRef = new WeakReference<>(
|
||||
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_skip_sponsor_button", "id"))));
|
||||
newSegmentLayoutRef = new WeakReference<>(
|
||||
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_new_segment_view", "id"))));
|
||||
skipHighlightButtonRef = new WeakReference<>(Objects.requireNonNull(
|
||||
layout.findViewById(getResourceIdentifier("revanced_sb_skip_highlight_button", "id"))));
|
||||
|
||||
skipSponsorButtonRef = new WeakReference<>(Objects.requireNonNull(
|
||||
layout.findViewById(getResourceIdentifier("revanced_sb_skip_sponsor_button", "id"))));
|
||||
|
||||
NewSegmentLayout newSegmentLayout = Objects.requireNonNull(
|
||||
layout.findViewById(getResourceIdentifier("revanced_sb_new_segment_view", "id")));
|
||||
newSegmentLayoutRef = new WeakReference<>(newSegmentLayout);
|
||||
newSegmentLayout.updateLayout();
|
||||
|
||||
newSegmentLayoutVisible = false;
|
||||
skipHighlight = null;
|
||||
@@ -101,6 +108,23 @@ public class SponsorBlockViewController {
|
||||
hideNewSegmentLayout();
|
||||
}
|
||||
|
||||
public static void updateLayout() {
|
||||
SkipSponsorButton button = skipSponsorButtonRef.get();
|
||||
if (button != null) {
|
||||
button.updateLayout();
|
||||
}
|
||||
|
||||
button = skipHighlightButtonRef.get();
|
||||
if (button != null) {
|
||||
button.updateLayout();
|
||||
}
|
||||
|
||||
NewSegmentLayout newSegmentLayout = newSegmentLayoutRef.get();
|
||||
if (newSegmentLayout != null) {
|
||||
newSegmentLayout.updateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public static void showSkipHighlightButton(@NonNull SponsorSegment segment) {
|
||||
skipHighlight = Objects.requireNonNull(segment);
|
||||
NewSegmentLayout newSegmentLayout = newSegmentLayoutRef.get();
|
||||
@@ -214,8 +238,8 @@ public class SponsorBlockViewController {
|
||||
// but if buttons are showing when the end of the video is reached then they need
|
||||
// to be forcefully hidden
|
||||
if (!Settings.AUTO_REPEAT.get()) {
|
||||
CreateSegmentButtonController.hide();
|
||||
VotingButtonController.hide();
|
||||
CreateSegmentButton.hideControls();
|
||||
VotingButton.hideControls();
|
||||
}
|
||||
} catch (Exception 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;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package app.revanced.extension.youtube.swipecontrols
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import app.revanced.extension.shared.StringRef.str
|
||||
import app.revanced.extension.shared.Utils
|
||||
import app.revanced.extension.youtube.settings.Settings
|
||||
import app.revanced.extension.youtube.shared.PlayerType
|
||||
|
||||
@@ -18,19 +20,17 @@ class SwipeControlsConfigurationProvider(
|
||||
* should swipe controls be enabled? (global setting)
|
||||
*/
|
||||
val enableSwipeControls: Boolean
|
||||
get() = isFullscreenVideo && (enableVolumeControls || enableBrightnessControl)
|
||||
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
|
||||
|
||||
/**
|
||||
* should swipe controls for volume be enabled?
|
||||
*/
|
||||
val enableVolumeControls: Boolean
|
||||
get() = Settings.SWIPE_VOLUME.get()
|
||||
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
|
||||
|
||||
/**
|
||||
* should swipe controls for volume be enabled?
|
||||
*/
|
||||
val enableBrightnessControl: Boolean
|
||||
get() = Settings.SWIPE_BRIGHTNESS.get()
|
||||
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
|
||||
|
||||
/**
|
||||
* is the video player currently in fullscreen mode?
|
||||
@@ -44,7 +44,7 @@ class SwipeControlsConfigurationProvider(
|
||||
* should volume key controls be overwritten? (global setting)
|
||||
*/
|
||||
val overwriteVolumeKeyControls: Boolean
|
||||
get() = isFullscreenVideo && enableVolumeControls
|
||||
get() = enableVolumeControls && isFullscreenVideo
|
||||
//endregion
|
||||
|
||||
//region gesture adjustments
|
||||
@@ -63,7 +63,6 @@ class SwipeControlsConfigurationProvider(
|
||||
//endregion
|
||||
|
||||
//region overlay adjustments
|
||||
|
||||
/**
|
||||
* should the overlay enable haptic feedback?
|
||||
*/
|
||||
@@ -77,23 +76,52 @@ class SwipeControlsConfigurationProvider(
|
||||
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
|
||||
get() = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
|
||||
val overlayBackgroundOpacity: Int
|
||||
get() {
|
||||
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
|
||||
|
||||
if (opacity < 0 || opacity > 100) {
|
||||
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
|
||||
Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
|
||||
opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
|
||||
}
|
||||
|
||||
opacity = opacity * 255 / 100
|
||||
return Color.argb(opacity, 0, 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* get the background color for text on the overlay, as a color int
|
||||
* The color of the progress overlay.
|
||||
*/
|
||||
val overlayTextBackgroundColor: Int
|
||||
get() = Color.argb(Settings.SWIPE_OVERLAY_BACKGROUND_ALPHA.get(), 0, 0, 0)
|
||||
val overlayProgressColor: Int
|
||||
get() = 0xBFFFFFFF.toInt()
|
||||
|
||||
/**
|
||||
* get the foreground color for text on the overlay, as a color int
|
||||
* The color used for the background of the progress overlay fill.
|
||||
*/
|
||||
val overlayForegroundColor: Int
|
||||
val overlayFillBackgroundPaint: Int
|
||||
get() = 0x80D3D3D3.toInt()
|
||||
|
||||
/**
|
||||
* The color used for the text and icons in the overlay.
|
||||
*/
|
||||
val overlayTextColor: Int
|
||||
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
|
||||
|
||||
//region behaviour
|
||||
|
||||
@@ -82,11 +82,15 @@ abstract class BaseGestureController(
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
from: MotionEvent,
|
||||
from: MotionEvent?,
|
||||
to: MotionEvent,
|
||||
distanceX: Float,
|
||||
distanceY: Float,
|
||||
): Boolean {
|
||||
if (from == null) {
|
||||
return false
|
||||
}
|
||||
|
||||
// submit to swipe detector
|
||||
submitForSwipe(from, to, distanceX, distanceY)
|
||||
|
||||
|
||||
@@ -1,138 +1,145 @@
|
||||
package app.revanced.extension.youtube.swipecontrols.views
|
||||
|
||||
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.GradientDrawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.TypedValue
|
||||
import android.util.AttributeSet
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.youtube.swipecontrols.SwipeControlsConfigurationProvider
|
||||
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
|
||||
import app.revanced.extension.youtube.swipecontrols.misc.applyDimension
|
||||
import kotlin.math.min
|
||||
import kotlin.math.round
|
||||
|
||||
/**
|
||||
* main overlay layout for volume and brightness swipe controls
|
||||
*
|
||||
* @param context context to create in
|
||||
* Main overlay layout for displaying volume and brightness level with both circular and rectangular progress bars.
|
||||
*/
|
||||
class SwipeControlsOverlayLayout(
|
||||
context: Context,
|
||||
private val config: SwipeControlsConfigurationProvider,
|
||||
) : RelativeLayout(context), SwipeControlsOverlay {
|
||||
/**
|
||||
* DO NOT use this, for tools only
|
||||
*/
|
||||
|
||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
|
||||
|
||||
private val feedbackTextView: TextView
|
||||
private val autoBrightnessIcon: Drawable
|
||||
private val manualBrightnessIcon: Drawable
|
||||
private val mutedVolumeIcon: Drawable
|
||||
private val normalVolumeIcon: Drawable
|
||||
// Drawable icons for brightness and volume
|
||||
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
||||
private val lowBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_low")
|
||||
private val mediumBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_medium")
|
||||
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 {
|
||||
return resources.getDrawable(
|
||||
// Function to retrieve drawable resources by name
|
||||
private fun getDrawable(name: String): Drawable {
|
||||
val drawable = resources.getDrawable(
|
||||
Utils.getResourceIdentifier(context, name, "drawable"),
|
||||
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 views
|
||||
val feedbackTextViewPadding = 2.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP)
|
||||
val compoundIconPadding = 4.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP)
|
||||
feedbackTextView = TextView(context).apply {
|
||||
layoutParams = LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
).apply {
|
||||
// Initialize circular progress bar
|
||||
circularProgressView = CircularProgressView(
|
||||
context,
|
||||
config.overlayBackgroundOpacity,
|
||||
config.overlayShowOverlayMinimalStyle,
|
||||
config.overlayProgressColor,
|
||||
config.overlayFillBackgroundPaint,
|
||||
config.overlayTextColor
|
||||
).apply {
|
||||
layoutParams = LayoutParams(300, 300).apply {
|
||||
addRule(CENTER_IN_PARENT, TRUE)
|
||||
setPadding(
|
||||
feedbackTextViewPadding,
|
||||
feedbackTextViewPadding,
|
||||
feedbackTextViewPadding,
|
||||
feedbackTextViewPadding,
|
||||
)
|
||||
}
|
||||
background = GradientDrawable().apply {
|
||||
cornerRadius = 8f
|
||||
setColor(config.overlayTextBackgroundColor)
|
||||
}
|
||||
setTextColor(config.overlayForegroundColor)
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, config.overlayTextSize.toFloat())
|
||||
compoundDrawablePadding = compoundIconPadding
|
||||
visibility = GONE
|
||||
visibility = GONE // Initially hidden
|
||||
}
|
||||
addView(feedbackTextView)
|
||||
addView(circularProgressView)
|
||||
|
||||
// get icons scaled, assuming square icons
|
||||
val iconHeight = round(feedbackTextView.lineHeight * .8).toInt()
|
||||
autoBrightnessIcon = getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight)
|
||||
manualBrightnessIcon = getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight)
|
||||
mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight)
|
||||
normalVolumeIcon = getDrawable("revanced_ic_sc_volume_normal", iconHeight, iconHeight)
|
||||
// Initialize rectangular progress bar
|
||||
val screenWidth = resources.displayMetrics.widthPixels
|
||||
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
|
||||
horizontalProgressView = HorizontalProgressView(
|
||||
context,
|
||||
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 feedbackHideCallback = Runnable {
|
||||
feedbackTextView.visibility = GONE
|
||||
circularProgressView.visibility = GONE
|
||||
horizontalProgressView.visibility = GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* show the feedback view for a given time
|
||||
*
|
||||
* @param message the message to show
|
||||
* @param icon the icon to use
|
||||
* Displays the progress bar with the appropriate value, icon, and type (brightness or volume).
|
||||
*/
|
||||
private fun showFeedbackView(message: String, icon: Drawable) {
|
||||
private fun showFeedbackView(value: String, progress: Int, max: Int, icon: Drawable, isBrightness: Boolean) {
|
||||
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
|
||||
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
|
||||
feedbackTextView.apply {
|
||||
text = message
|
||||
setCompoundDrawablesRelative(
|
||||
icon,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
|
||||
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
|
||||
viewToShow.apply {
|
||||
setProgress(progress, max, value, isBrightness)
|
||||
this.icon = icon
|
||||
visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
// Handle volume change
|
||||
override fun onVolumeChanged(newVolume: Int, maximumVolume: Int) {
|
||||
showFeedbackView(
|
||||
"$newVolume",
|
||||
if (newVolume > 0) normalVolumeIcon else mutedVolumeIcon,
|
||||
)
|
||||
val volumePercentage = (newVolume.toFloat() / maximumVolume) * 100
|
||||
val icon = when {
|
||||
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) {
|
||||
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
|
||||
showFeedbackView(
|
||||
str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text"),
|
||||
autoBrightnessIcon,
|
||||
)
|
||||
} else if (brightness >= 0) {
|
||||
showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon)
|
||||
showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
|
||||
} else {
|
||||
val brightnessValue = round(brightness).toInt()
|
||||
val icon = when {
|
||||
brightnessValue < 25 -> lowBrightnessIcon
|
||||
brightnessValue < 50 -> mediumBrightnessIcon
|
||||
brightnessValue < 75 -> highBrightnessIcon
|
||||
else -> fullBrightnessIcon
|
||||
}
|
||||
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
|
||||
}
|
||||
}
|
||||
|
||||
// Begin swipe session
|
||||
override fun onEnterSwipeSession() {
|
||||
if (config.shouldEnableHapticFeedback) {
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -143,3 +150,233 @@ class SwipeControlsOverlayLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract base class for progress views to reduce code duplication.
|
||||
*/
|
||||
/**
|
||||
* Abstract base class for progress views to reduce code duplication.
|
||||
*/
|
||||
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 = 30f // 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 text and icon.
|
||||
*/
|
||||
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 horizontal 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 in icon-only 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 = 30f // 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;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class CopyVideoUrlButton extends PlayerControlButton {
|
||||
public class CopyVideoUrlButton {
|
||||
@Nullable
|
||||
private static CopyVideoUrlButton instance;
|
||||
|
||||
public CopyVideoUrlButton(ViewGroup viewGroup) {
|
||||
super(
|
||||
viewGroup,
|
||||
"revanced_copy_video_url_button",
|
||||
Settings.COPY_VIDEO_URL,
|
||||
view -> CopyVideoUrlPatch.copyUrl(false),
|
||||
view -> {
|
||||
CopyVideoUrlPatch.copyUrl(true);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
private static PlayerControlButton instance;
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void initializeButton(View view) {
|
||||
public static void initializeButton(View controlsView) {
|
||||
try {
|
||||
instance = new CopyVideoUrlButton((ViewGroup) view);
|
||||
instance = new PlayerControlButton(
|
||||
controlsView,
|
||||
"revanced_copy_video_url_button",
|
||||
"revanced_copy_video_url_button_placeholder",
|
||||
Settings.COPY_VIDEO_URL::get,
|
||||
view -> CopyVideoUrlPatch.copyUrl(false),
|
||||
view -> {
|
||||
CopyVideoUrlPatch.copyUrl(true);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "initializeButton failure", ex);
|
||||
}
|
||||
@@ -41,14 +38,14 @@ public class CopyVideoUrlButton extends PlayerControlButton {
|
||||
/**
|
||||
* injection point
|
||||
*/
|
||||
public static void changeVisibilityImmediate(boolean visible) {
|
||||
public static void setVisibilityImmediate(boolean visible) {
|
||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,35 @@
|
||||
package app.revanced.extension.youtube.videoplayer;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class CopyVideoUrlTimestampButton extends PlayerControlButton {
|
||||
public class CopyVideoUrlTimestampButton {
|
||||
@Nullable
|
||||
private static CopyVideoUrlTimestampButton instance;
|
||||
|
||||
public CopyVideoUrlTimestampButton(ViewGroup bottomControlsViewGroup) {
|
||||
super(
|
||||
bottomControlsViewGroup,
|
||||
"revanced_copy_video_url_timestamp_button",
|
||||
Settings.COPY_VIDEO_URL_TIMESTAMP,
|
||||
view -> CopyVideoUrlPatch.copyUrl(true),
|
||||
view -> {
|
||||
CopyVideoUrlPatch.copyUrl(false);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
private static PlayerControlButton instance;
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void initializeButton(View bottomControlsViewGroup) {
|
||||
public static void initializeButton(View controlsView) {
|
||||
try {
|
||||
instance = new CopyVideoUrlTimestampButton((ViewGroup) bottomControlsViewGroup);
|
||||
instance = new PlayerControlButton(
|
||||
controlsView,
|
||||
"revanced_copy_video_url_timestamp_button",
|
||||
"revanced_copy_video_url_timestamp_button_placeholder",
|
||||
Settings.COPY_VIDEO_URL_TIMESTAMP::get,
|
||||
view -> CopyVideoUrlPatch.copyUrl(true),
|
||||
view -> {
|
||||
CopyVideoUrlPatch.copyUrl(false);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "initializeButton failure", ex);
|
||||
}
|
||||
@@ -41,14 +38,14 @@ public class CopyVideoUrlTimestampButton extends PlayerControlButton {
|
||||
/**
|
||||
* injection point
|
||||
*/
|
||||
public static void changeVisibilityImmediate(boolean visible) {
|
||||
public static void setVisibilityImmediate(boolean visible) {
|
||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package app.revanced.extension.youtube.videoplayer;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -11,26 +10,23 @@ import app.revanced.extension.youtube.patches.VideoInformation;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class ExternalDownloadButton extends PlayerControlButton {
|
||||
public class ExternalDownloadButton {
|
||||
@Nullable
|
||||
private static ExternalDownloadButton instance;
|
||||
|
||||
public ExternalDownloadButton(ViewGroup viewGroup) {
|
||||
super(
|
||||
viewGroup,
|
||||
"revanced_external_download_button",
|
||||
Settings.EXTERNAL_DOWNLOADER,
|
||||
ExternalDownloadButton::onDownloadClick,
|
||||
null
|
||||
);
|
||||
}
|
||||
private static PlayerControlButton instance;
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void initializeButton(View view) {
|
||||
public static void initializeButton(View controlsView) {
|
||||
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) {
|
||||
Logger.printException(() -> "initializeButton failure", ex);
|
||||
}
|
||||
@@ -39,14 +35,14 @@ public class ExternalDownloadButton extends PlayerControlButton {
|
||||
/**
|
||||
* injection point
|
||||
*/
|
||||
public static void changeVisibilityImmediate(boolean visible) {
|
||||
public static void setVisibilityImmediate(boolean 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
package app.revanced.extension.youtube.videoplayer;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PlaybackSpeedDialogButton extends PlayerControlButton {
|
||||
public class PlaybackSpeedDialogButton {
|
||||
@Nullable
|
||||
private static PlaybackSpeedDialogButton instance;
|
||||
|
||||
public PlaybackSpeedDialogButton(ViewGroup viewGroup) {
|
||||
super(
|
||||
viewGroup,
|
||||
"revanced_playback_speed_dialog_button",
|
||||
Settings.PLAYBACK_SPEED_DIALOG_BUTTON,
|
||||
view -> CustomPlaybackSpeedPatch.showOldPlaybackSpeedMenu(),
|
||||
null
|
||||
);
|
||||
}
|
||||
private static PlayerControlButton instance;
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void initializeButton(View view) {
|
||||
public static void initializeButton(View controlsView) {
|
||||
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) {
|
||||
Logger.printException(() -> "initializeButton failure", ex);
|
||||
}
|
||||
@@ -38,14 +34,14 @@ public class PlaybackSpeedDialogButton extends PlayerControlButton {
|
||||
/**
|
||||
* injection point
|
||||
*/
|
||||
public static void changeVisibilityImmediate(boolean visible) {
|
||||
public static void setVisibilityImmediate(boolean visible) {
|
||||
if (instance != null) instance.setVisibilityImmediate(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +1,92 @@
|
||||
package app.revanced.extension.youtube.videoplayer;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
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 {
|
||||
private static final Animation fadeIn;
|
||||
private static final Animation fadeOut;
|
||||
public class PlayerControlButton {
|
||||
public interface PlayerControlButtonVisibility {
|
||||
/**
|
||||
* @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 final WeakReference<ImageView> buttonRef;
|
||||
protected final BooleanSetting setting;
|
||||
protected boolean isVisible;
|
||||
|
||||
static {
|
||||
// TODO: check if these durations are correct.
|
||||
fadeIn = Utils.getResourceAnimation("fade_in");
|
||||
fadeIn.setDuration(Utils.getResourceInteger("fade_duration_fast"));
|
||||
fadeInDuration = Utils.getResourceInteger("fade_duration_fast");
|
||||
fadeOutDuration = Utils.getResourceInteger("fade_duration_scheduled");
|
||||
|
||||
fadeOut = Utils.getResourceAnimation("fade_out");
|
||||
fadeOut.setDuration(Utils.getResourceInteger("fade_duration_scheduled"));
|
||||
fadeInAnimation = Utils.getResourceAnimation("fade_in");
|
||||
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.setDuration(Utils.getResourceInteger("fade_duration_fast"));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Animation getButtonFadeIn() {
|
||||
return fadeIn;
|
||||
}
|
||||
private final WeakReference<View> buttonRef;
|
||||
/**
|
||||
* 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 static Animation getButtonFadeOut() {
|
||||
return fadeOut;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Animation getButtonFadeOutImmediately() {
|
||||
return fadeOutImmediate;
|
||||
}
|
||||
|
||||
public PlayerControlButton(@NonNull ViewGroup bottomControlsViewGroup, @NonNull String imageViewButtonId,
|
||||
@NonNull BooleanSetting booleanSetting, @NonNull View.OnClickListener onClickListener,
|
||||
public PlayerControlButton(View controlsViewGroup,
|
||||
String imageViewButtonId,
|
||||
@Nullable String placeholderId,
|
||||
PlayerControlButtonVisibility buttonVisibility,
|
||||
View.OnClickListener onClickListener,
|
||||
@Nullable View.OnLongClickListener longClickListener) {
|
||||
Logger.printDebug(() -> "Initializing button: " + imageViewButtonId);
|
||||
|
||||
ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById(
|
||||
Utils.getResourceIdentifier(imageViewButtonId, "id")
|
||||
));
|
||||
ImageView imageView = Utils.getChildViewByResourceName(controlsViewGroup, imageViewButtonId);
|
||||
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);
|
||||
if (longClickListener != null) {
|
||||
imageView.setOnLongClickListener(longClickListener);
|
||||
}
|
||||
|
||||
setting = booleanSetting;
|
||||
visibilityCheck = buttonVisibility;
|
||||
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) {
|
||||
@@ -92,26 +111,80 @@ public abstract class PlayerControlButton {
|
||||
if (isVisible == visible) return;
|
||||
isVisible = visible;
|
||||
|
||||
ImageView iView = buttonRef.get();
|
||||
if (iView == null) {
|
||||
return;
|
||||
}
|
||||
View button = buttonRef.get();
|
||||
if (button == null) return;
|
||||
|
||||
if (visible && setting.get()) {
|
||||
iView.clearAnimation();
|
||||
View placeholder = placeHolderRef.get();
|
||||
final boolean shouldBeShown = visibilityCheck.shouldBeShown();
|
||||
|
||||
if (visible && shouldBeShown) {
|
||||
button.clearAnimation();
|
||||
if (animated) {
|
||||
iView.startAnimation(PlayerControlButton.getButtonFadeIn());
|
||||
button.startAnimation(PlayerControlButton.fadeInAnimation);
|
||||
}
|
||||
iView.setVisibility(View.VISIBLE);
|
||||
} else if (iView.getVisibility() == View.VISIBLE) {
|
||||
iView.clearAnimation();
|
||||
if (animated) {
|
||||
iView.startAnimation(PlayerControlButton.getButtonFadeOut());
|
||||
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);
|
||||
}
|
||||
iView.setVisibility(View.GONE);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setVisibility failure", ex);
|
||||
Logger.printException(() -> "private_setVisibility failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes the button state after the player state changes.
|
||||
*/
|
||||
private void playerTypeChanged(PlayerType newType) {
|
||||
if (newType != PlayerType.WATCH_WHILE_MINIMIZED && !newType.isMaximizedOrFullscreen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
View button = buttonRef.get();
|
||||
if (button == null) return;
|
||||
|
||||
button.clearAnimation();
|
||||
View placeholder = placeHolderRef.get();
|
||||
|
||||
if (visibilityCheck.shouldBeShown()) {
|
||||
if (isVisible) {
|
||||
button.setVisibility(View.VISIBLE);
|
||||
if (placeholder != null) placeholder.setVisibility(View.GONE);
|
||||
} else {
|
||||
button.setVisibility(View.GONE);
|
||||
if (placeholder != null) placeholder.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
button.setVisibility(View.GONE);
|
||||
if (placeholder != null) placeholder.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
if (!isVisible) return;
|
||||
|
||||
Utils.verifyOnMainThread();
|
||||
View view = buttonRef.get();
|
||||
if (view == null) return;
|
||||
view.setVisibility(View.GONE);
|
||||
|
||||
view = placeHolderRef.get();
|
||||
if (view != null) view.setVisibility(View.GONE);
|
||||
isVisible = false;
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,9 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 33
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.airbnb.lottie;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class LottieAnimationView {
|
||||
|
||||
public void patch_setAnimation(InputStream stream, String cacheKey) {
|
||||
throw new RuntimeException("stub");
|
||||
}
|
||||
|
||||
public final void patch_setAnimation(int rawResInt) {
|
||||
throw new RuntimeException("stub");
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.10.0-dev.4
|
||||
version = 5.13.0-dev.13
|
||||
|
||||
@@ -3,7 +3,6 @@ revanced-patcher = "21.0.0"
|
||||
# Tracking https://github.com/google/smali/issues/64.
|
||||
#noinspection GradleDependency
|
||||
smali = "3.0.5"
|
||||
gson = "2.11.0"
|
||||
# 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818.
|
||||
#noinspection GradleDependency
|
||||
agp = "8.2.2"
|
||||
@@ -11,10 +10,9 @@ annotation = "1.9.1"
|
||||
appcompat = "1.7.0"
|
||||
okhttp = "5.0.0-alpha.14"
|
||||
retrofit = "2.11.0"
|
||||
guava = "33.2.1-jre"
|
||||
guava = "33.4.0-jre"
|
||||
|
||||
[libraries]
|
||||
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
|
||||
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
|
||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.10.1",
|
||||
"semantic-release": "^24.1.2"
|
||||
"semantic-release": "^24.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
@@ -6760,9 +6760,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semantic-release": {
|
||||
"version": "24.1.2",
|
||||
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.1.2.tgz",
|
||||
"integrity": "sha512-hvEJ7yI97pzJuLsDZCYzJgmRxF8kiEJvNZhf0oiZQcexw+Ycjy4wbdsn/sVMURgNCu8rwbAXJdBRyIxM4pe32g==",
|
||||
"version": "24.2.1",
|
||||
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.1.tgz",
|
||||
"integrity": "sha512-z0/3cutKNkLQ4Oy0HTi3lubnjTsdjjgOqmxdPjeYWe6lhFqUPfwslZxRHv3HDZlN4MhnZitb9SLihDkZNxOXfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6782,7 +6782,7 @@
|
||||
"git-log-parser": "^1.2.0",
|
||||
"hook-std": "^3.0.0",
|
||||
"hosted-git-info": "^8.0.0",
|
||||
"import-from-esm": "^1.3.1",
|
||||
"import-from-esm": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^12.0.0",
|
||||
"marked-terminal": "^7.0.0",
|
||||
@@ -6926,6 +6926,20 @@
|
||||
"node": ">=18.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/semantic-release/node_modules/import-from-esm": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz",
|
||||
"integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"import-meta-resolve": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20"
|
||||
}
|
||||
},
|
||||
"node_modules/semantic-release/node_modules/indent-string": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"gradle-semantic-release-plugin": "^1.10.1",
|
||||
"semantic-release": "^24.1.2"
|
||||
"semantic-release": "^24.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,31 @@ patches {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Used by JsonGenerator.
|
||||
implementation(libs.gson)
|
||||
// Required due to smali, or build fails. Can be removed once smali is bumped.
|
||||
implementation(libs.guava)
|
||||
// Android API stubs defined here.
|
||||
compileOnly(project(":patches:stub"))
|
||||
}
|
||||
|
||||
tasks {
|
||||
register<JavaExec>("preprocessCrowdinStrings") {
|
||||
description = "Preprocess strings for Crowdin push"
|
||||
|
||||
dependsOn(compileKotlin)
|
||||
|
||||
classpath = sourceSets["main"].runtimeClasspath
|
||||
mainClass.set("app.revanced.util.CrowdinPreprocessorKt")
|
||||
|
||||
args = listOf(
|
||||
"src/main/resources/addresources/values/strings.xml",
|
||||
// Ideally this would use build/tmp/crowdin/strings.xml
|
||||
// But using that does not work with Crowdin pull because
|
||||
// it does not recognize the strings.xml file belongs to this project.
|
||||
"src/main/resources/addresources/values/strings.xml"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
freeCompilerArgs = listOf("-Xcontext-receivers")
|
||||
@@ -38,4 +55,4 @@ publishing {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ val exportInternalDataDocumentsProviderPatch = resourcePatch(
|
||||
) {
|
||||
dependsOn(
|
||||
bytecodePatch {
|
||||
extendWith("extensions/all/misc/directory/export-internal-data-documents-provider.rve")
|
||||
extendWith("extensions/all/misc/directory/documentsprovider/export-internal-data-documents-provider.rve")
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -359,15 +359,15 @@ val addResourcesPatch = resourcePatch(
|
||||
}
|
||||
|
||||
getOrPut(resourceFileName) {
|
||||
val targetFile = this@finalize["res/$value/$resourceFileName.xml"].also {
|
||||
this@finalize["res/$value/$resourceFileName.xml"].also {
|
||||
it.parentFile?.mkdirs()
|
||||
|
||||
if (it.createNewFile()) {
|
||||
it.writeText("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n</resources>")
|
||||
}
|
||||
}
|
||||
|
||||
document(targetFile.path).let { document ->
|
||||
|
||||
document("res/$value/$resourceFileName.xml").let { document ->
|
||||
|
||||
// Save the target node here as well
|
||||
// in order to avoid having to call document.getNode("resources")
|
||||
|
||||
@@ -25,7 +25,7 @@ private val removeCaptureRestrictionResourcePatch = resourcePatch(
|
||||
}
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||
"Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreencaptureRestrictionPatch"
|
||||
"Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
||||
|
||||
@Suppress("unused")
|
||||
|
||||
@@ -8,7 +8,12 @@ val hideVideoAdsPatch = bytecodePatch(
|
||||
name = "Hide music video ads",
|
||||
description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
navigate(showVideoAdsParentFingerprint.originalMethod)
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
package app.revanced.patches.music.audio.exclusiveaudio
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val enableExclusiveAudioPlaybackPatch = bytecodePatch(
|
||||
name = "Enable exclusive audio playback",
|
||||
description = "Enables the option to play audio without video.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
allowExclusiveAudioPlaybackFingerprint.method.apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
""",
|
||||
)
|
||||
}
|
||||
allowExclusiveAudioPlaybackFingerprint.method.returnEarly(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,16 +9,15 @@ internal val allowExclusiveAudioPlaybackFingerprint = fingerprint {
|
||||
returns("Z")
|
||||
parameters()
|
||||
opcodes(
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.GOTO,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.RETURN
|
||||
Opcode.MOVE_RESULT
|
||||
)
|
||||
}
|
||||
@@ -11,10 +11,14 @@ val permanentRepeatPatch = bytecodePatch(
|
||||
description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
|
||||
val startIndex = repeatTrackFingerprint.patternMatch!!.endIndex
|
||||
val repeatIndex = startIndex + 1
|
||||
|
||||
|
||||
@@ -11,7 +11,12 @@ val hideCategoryBar = bytecodePatch(
|
||||
description = "Hides the category bar at the top of the homepage.",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
constructCategoryBarFingerprint.method.apply {
|
||||
|
||||
@@ -11,7 +11,12 @@ val hideGetPremiumPatch = bytecodePatch(
|
||||
name = "Hide 'Get Music Premium' label",
|
||||
description = "Hides the \"Get Music Premium\" label from the account menu and settings.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
hideGetPremiumFingerprint.method.apply {
|
||||
|
||||
@@ -18,7 +18,12 @@ val removeUpgradeButtonPatch = bytecodePatch(
|
||||
name = "Remove upgrade button",
|
||||
description = "Removes the upgrade tab from the pivot bar.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
pivotBarConstructorFingerprint.method.apply {
|
||||
|
||||
@@ -8,7 +8,12 @@ val bypassCertificateChecksPatch = bytecodePatch(
|
||||
name = "Bypass certificate checks",
|
||||
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music"("7.29.52"))
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
checkCertificateFingerprint.method.returnEarly(true)
|
||||
|
||||
@@ -4,8 +4,10 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal val checkCertificateFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("Z")
|
||||
parameters("Ljava/lang/String;")
|
||||
strings("X509", "Failed to get certificate.")
|
||||
strings(
|
||||
"X509",
|
||||
"Failed to get certificate" // Partial String match.
|
||||
)
|
||||
}
|
||||
@@ -8,7 +8,12 @@ val backgroundPlaybackPatch = bytecodePatch(
|
||||
name = "Remove background playback restrictions",
|
||||
description = "Removes restrictions on background playback, including playing kids videos in the background.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
kidsBackgroundPlaybackPolicyControllerFingerprint.method.addInstruction(
|
||||
|
||||
@@ -25,7 +25,12 @@ val spoofClientPatch = bytecodePatch(
|
||||
name = "Spoof client",
|
||||
description = "Spoofs the client to fix playback.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
"7.16.53",
|
||||
"8.05.51"
|
||||
)
|
||||
)
|
||||
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
|
||||
@@ -56,8 +56,9 @@ val customThemePatch = resourcePatch(
|
||||
document("res/values/colors.xml").use { document ->
|
||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||
|
||||
for (i in 0 until resourcesNode.childNodes.length) {
|
||||
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
|
||||
val childNodes = resourcesNode.childNodes
|
||||
for (i in 0 until childNodes.length) {
|
||||
val node = childNodes.item(i) as? Element ?: continue
|
||||
|
||||
node.textContent =
|
||||
when (node.getAttribute("name")) {
|
||||
|
||||
@@ -15,7 +15,7 @@ val removeGooglePlayIntegrityCheckPatch = bytecodePatch(
|
||||
description = "Removes the Google Play Integrity check. With this it's possible to use SwissID on custom ROMS." +
|
||||
"If the device is rooted, root permissions must be hidden from the app.",
|
||||
) {
|
||||
compatibleWith("com.swisssign.swissid.mobile")
|
||||
compatibleWith("com.swisssign.swissid.mobile"("5.2.9"))
|
||||
|
||||
execute {
|
||||
checkIntegrityFingerprint.method.addInstructions(
|
||||
|
||||
@@ -2,4 +2,4 @@ package app.revanced.patches.tiktok.misc.extension
|
||||
|
||||
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 com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val initHook = extensionHook(
|
||||
insertIndexResolver = { 1 }, // Insert after call to super class.
|
||||
) {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
internal val initHook = extensionHook {
|
||||
custom { method, classDef ->
|
||||
classDef.endsWith("/AwemeHostApplication;") &&
|
||||
method.name == "<init>"
|
||||
classDef.type == "Lcom/ss/android/ugc/aweme/main/MainActivity;" &&
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package app.revanced.patches.windyapp.misc.unlockpro
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
|
||||
@Deprecated("This patch no longer works and will be removed in the future.")
|
||||
@Suppress("unused")
|
||||
val unlockProPatch = bytecodePatch(
|
||||
name = "Unlock pro",
|
||||
description = "Unlocks all pro features.",
|
||||
) {
|
||||
compatibleWith("co.windyapp.android")
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package app.revanced.patches.youtube.ad.general
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionReversed
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val fullScreenEngagementAdContainerFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters()
|
||||
custom { method, _ ->
|
||||
method.containsLiteralInstruction(fullScreenEngagementAdContainer)
|
||||
&& indexOfAddListInstruction(method) >= 0
|
||||
}
|
||||
}
|
||||
|
||||
internal fun indexOfAddListInstruction(method: Method) =
|
||||
method.indexOfFirstInstructionReversed {
|
||||
getReference<MethodReference>()?.name == "add"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package app.revanced.patches.youtube.ad.general
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
@@ -18,11 +20,16 @@ import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.findMutableMethodOf
|
||||
import app.revanced.util.injectHideViewCall
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
|
||||
internal var adAttributionId = -1L
|
||||
private set
|
||||
internal var fullScreenEngagementAdContainer = -1L
|
||||
private set
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/components/AdsFilter;"
|
||||
|
||||
private val hideAdsResourcePatch = resourcePatch {
|
||||
dependsOn(
|
||||
@@ -53,6 +60,7 @@ private val hideAdsResourcePatch = resourcePatch {
|
||||
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/AdsFilter;")
|
||||
|
||||
adAttributionId = resourceMappings["id", "ad_attribution"]
|
||||
fullScreenEngagementAdContainer = resourceMappings["id", "fullscreen_engagement_ad_container"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +78,6 @@ val hideAdsPatch = bytecodePatch(
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
@@ -83,6 +89,23 @@ val hideAdsPatch = bytecodePatch(
|
||||
)
|
||||
|
||||
execute {
|
||||
// Hide end screen store banner
|
||||
|
||||
fullScreenEngagementAdContainerFingerprint.method.apply {
|
||||
val addListIndex = indexOfAddListInstruction(this)
|
||||
val addListInstruction = getInstruction<FiveRegisterInstruction>(addListIndex)
|
||||
val listRegister = addListInstruction.registerC
|
||||
val objectRegister = addListInstruction.registerD
|
||||
|
||||
replaceInstruction(
|
||||
addListIndex,
|
||||
"invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR" +
|
||||
"->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V"
|
||||
)
|
||||
}
|
||||
|
||||
// Hide ad views
|
||||
|
||||
classes.forEach { classDef ->
|
||||
classDef.methods.forEach { method ->
|
||||
with(method.implementation) {
|
||||
@@ -111,7 +134,7 @@ val hideAdsPatch = bytecodePatch(
|
||||
.injectHideViewCall(
|
||||
insertIndex,
|
||||
viewRegister,
|
||||
"Lapp/revanced/extension/youtube/patches/components/AdsFilter;",
|
||||
EXTENSION_CLASS_DESCRIPTOR,
|
||||
"hideAdAttributionView",
|
||||
)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user