Compare commits

...

64 Commits

Author SHA1 Message Date
semantic-release-bot
09773e8934 chore: Release v5.13.0-dev.10 [skip ci]
# [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 ([d77d5bf](d77d5bfbdd))
2025-02-22 16:31:11 +00:00
LisoUseInAIKyrios
d77d5bfbdd fix(YouTube - Copy video URL): Use correct button ordering
Fixes refactoring oversight of button fade fix
2025-02-22 18:28:47 +02:00
semantic-release-bot
a84bded9e7 chore: Release v5.13.0-dev.9 [skip ci]
# [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)) ([e664a24](e664a24f73))
2025-02-22 15:48:25 +00:00
LisoUseInAIKyrios
e664a24f73 fix(YouTube): Do not hide player controls when using double tap to skip forward (#4487)
Co-authored-by: MarcaDian <tolan.sheremeev@gmail.com>
2025-02-22 17:44:53 +02:00
semantic-release-bot
5bf964fff6 chore: Release v5.13.0-dev.8 [skip ci]
# [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)) ([0c0bbb8](0c0bbb8713))
2025-02-22 09:43:04 +00:00
LisoUseInAIKyrios
0c0bbb8713 fix(YouTube - Spoof app version): Force old settings menus if spoofing to older app targets (#4490) 2025-02-22 11:40:06 +02:00
github-actions[bot]
8afe48cd92 chore: Sync translations (#4492) 2025-02-22 11:39:54 +02:00
github-actions[bot]
dde8ea31cb chore: Sync translations (#4491) 2025-02-22 11:34:59 +02:00
semantic-release-bot
d3abbe3e93 chore: Release v5.13.0-dev.7 [skip ci]
# [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 ([c817977](c8179776ed))
2025-02-22 06:25:32 +00:00
LisoUseInAIKyrios
c8179776ed fix(TikTok): Resolve startup app crash 2025-02-22 08:20:19 +02:00
semantic-release-bot
c6c6516b12 chore: Release v5.13.0-dev.6 [skip ci]
# [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)) ([d6eae01](d6eae01e12))
2025-02-21 12:13:01 +00:00
LisoUseInAIKyrios
d6eae01e12 feat(YouTube - Navigation buttons): Add 'Hide notifications' setting (#4485) 2025-02-21 14:09:01 +02:00
semantic-release-bot
ba88603f4b chore: Release v5.13.0-dev.5 [skip ci]
# [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 ([d5aab3d](d5aab3d464))
2025-02-19 14:39:51 +00:00
LisoUseInAIKyrios
d5aab3d464 fix(TikTok): Resolve startup app crash 2025-02-19 16:36:07 +02:00
semantic-release-bot
fca2f70c0e chore: Release v5.13.0-dev.4 [skip ci]
# [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 ([348f7e1](348f7e12cb))
2025-02-19 13:57:05 +00:00
LisoUseInAIKyrios
348f7e12cb fix(TikTok): Resolve startup app crash 2025-02-19 15:53:12 +02:00
semantic-release-bot
b6b7208eeb chore: Release v5.13.0-dev.3 [skip ci]
# [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)) ([a2c79f1](a2c79f1349))
2025-02-19 11:25:29 +00:00
MarcaD
a2c79f1349 fix(YouTube): Fix player button fade out animations (#4469) 2025-02-19 13:22:46 +02:00
github-actions[bot]
4f5bb3c915 chore: Sync translations (#4478) 2025-02-19 13:22:17 +02:00
semantic-release-bot
4b77d27c77 chore: Release v5.13.0-dev.2 [skip ci]
# [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 ([7991c80](7991c80129))
2025-02-18 07:29:55 +00:00
LisoUseInAIKyrios
7991c80129 fix(YouTube - Hide video action buttons): Move 'Disable Like and Subscribe glow' to action buttons settings menu 2025-02-18 09:26:30 +02:00
github-actions[bot]
6baf4ea2ac chore: Sync translations (#4473) 2025-02-18 09:24:59 +02:00
semantic-release-bot
c89538c8f5 chore: Release v5.13.0-dev.1 [skip ci]
# [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 ([94fb367](94fb367618))

### Features

* **YouTube - Swipe controls:** Swipe controls UI improvements ([#4422](https://github.com/ReVanced/revanced-patches/issues/4422)) ([3548359](354835966d))
2025-02-18 07:11:45 +00:00
LisoUseInAIKyrios
94fb367618 fix(YouTube - Hide layout components): Do not hide 'Show anyway' button in search results 2025-02-18 09:08:37 +02:00
MarcaD
354835966d feat(YouTube - Swipe controls): Swipe controls UI improvements (#4422) 2025-02-18 09:07:28 +02:00
github-actions[bot]
168f9b769e chore: Sync translations (#4472) 2025-02-18 09:06:39 +02:00
ILoveOpenSourceApplications
e4c4b3a73a refactor(YouTube): Use more consistent strings (#4376) 2025-02-17 10:07:24 +02:00
semantic-release-bot
fce98b4960 chore: Release v5.12.0 [skip ci]
# [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)) ([a959d79](a959d798e8))
* **Windy.app:** Remove obsolete `Unlock pro` patch ([#4428](https://github.com/ReVanced/revanced-patches/issues/4428)) ([421af92](421af92f4c))
* **YouTube - Spoof video streams:** Change default client to `Android TV` ([#4465](https://github.com/ReVanced/revanced-patches/issues/4465)) ([04b37dd](04b37dd55a))
* **YouTube:** Remove obsolete 18.x targets ([#4454](https://github.com/ReVanced/revanced-patches/issues/4454)) ([92c38b2](92c38b2cb4))

### Features

* **Return YouTube Dislike:** add `Show estimated likes` setting ([#4443](https://github.com/ReVanced/revanced-patches/issues/4443)) ([7c4285e](7c4285e3e6))
* **YouTube - SponsorBlock:** Redesign skip buttons ([#4427](https://github.com/ReVanced/revanced-patches/issues/4427)) ([0079ece](0079eceb87))
* **YouTube Music:** Support version `8.05.50` ([#4439](https://github.com/ReVanced/revanced-patches/issues/4439)) ([bcd157d](bcd157dd2b))
* **YouTube Music:** Support version `8.05.51` ([2382e9d](2382e9d09e))
2025-02-17 06:23:44 +00:00
LisoUseInAIKyrios
839aa81e9c chore: Merge branch dev to main (#4437) 2025-02-17 08:20:23 +02:00
semantic-release-bot
905bb0ea5f chore: Release v5.12.0-dev.7 [skip ci]
# [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)) ([04b37dd](04b37dd55a))

### Features

* **YouTube Music:** Support version `8.05.51` ([2382e9d](2382e9d09e))
2025-02-16 16:41:43 +00:00
github-actions[bot]
a94a663859 chore: Sync translations (#4468) 2025-02-16 18:38:36 +02:00
LisoUseInAIKyrios
04b37dd55a fix(YouTube - Spoof video streams): Change default client to Android TV (#4465) 2025-02-16 18:34:12 +02:00
LisoUseInAIKyrios
2382e9d09e feat(YouTube Music): Support version 8.05.51 2025-02-16 18:31:52 +02:00
github-actions[bot]
97f504976a chore: Sync translations (#4467) 2025-02-16 18:30:04 +02:00
semantic-release-bot
0a6c5158e0 chore: Release v5.12.0-dev.6 [skip ci]
# [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)) ([a959d79](a959d798e8))
2025-02-16 13:19:31 +00:00
LisoUseInAIKyrios
a959d798e8 fix: Allow changing default settings for existing app installs (#4464) 2025-02-16 15:16:24 +02:00
semantic-release-bot
39a0b9bda6 chore: Release v5.12.0-dev.5 [skip ci]
# [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)) ([92c38b2](92c38b2cb4))
2025-02-13 12:41:16 +00:00
LisoUseInAIKyrios
92c38b2cb4 fix(YouTube): Remove obsolete 18.x targets (#4454) 2025-02-13 14:38:23 +02:00
github-actions[bot]
4732210d4b chore: Sync translations (#4455) 2025-02-13 14:35:32 +02:00
semantic-release-bot
f30a49f1cb chore: Release v5.12.0-dev.4 [skip ci]
# [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)) ([bcd157d](bcd157dd2b))
2025-02-11 18:40:51 +00:00
Alberto Ponces
bcd157dd2b feat(YouTube Music): Support version 8.05.50 (#4439) 2025-02-11 20:37:58 +02:00
LisoUseInAIKyrios
d299ea5973 chore(deps): Remove unused dependency 2025-02-11 17:41:40 +02:00
dependabot[bot]
a20021e290 chore(deps): bump com.google.code.gson:gson from 2.11.0 to 2.12.1 (#4398)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-11 17:19:52 +02:00
dependabot[bot]
373ca966f3 chore(deps): Bump com.google.guava:guava from 33.2.1-jre to 33.4.0-jre (#4252) 2025-02-11 17:17:35 +02:00
semantic-release-bot
12de922afa chore: Release v5.12.0-dev.3 [skip ci]
# [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)) ([421af92](421af92f4c))
2025-02-11 15:11:34 +00:00
dependabot[bot]
580bb3cf6c chore(deps-dev): bump semantic-release from 24.1.2 to 24.2.1 (#4397) 2025-02-11 17:07:28 +02:00
LisoUseInAIKyrios
421af92f4c fix(Windy.app): Remove obsolete Unlock pro patch (#4428) 2025-02-11 17:05:46 +02:00
github-actions[bot]
4d03e1b5a1 chore: Sync translations (#4446) 2025-02-11 17:05:17 +02:00
LisoUseInAIKyrios
24d68df6cd refactor: Improve XML performance 2025-02-11 15:24:29 +02:00
semantic-release-bot
e9aee17746 chore: Release v5.12.0-dev.2 [skip ci]
# [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)) ([7c4285e](7c4285e3e6))
2025-02-11 10:15:24 +00:00
LisoUseInAIKyrios
7c4285e3e6 feat(Return YouTube Dislike): add Show estimated likes setting (#4443) 2025-02-11 12:12:24 +02:00
semantic-release-bot
e3110271a7 chore: Release v5.12.0-dev.1 [skip ci]
# [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)) ([0079ece](0079eceb87))
2025-02-10 18:33:12 +00:00
MarcaD
0079eceb87 feat(YouTube - SponsorBlock): Redesign skip buttons (#4427)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-02-10 20:30:06 +02:00
github-actions[bot]
af2a97cb16 chore: Sync translations (#4436) 2025-02-10 20:29:28 +02:00
semantic-release-bot
aeb552e8f2 chore: Release v5.11.0 [skip ci]
# [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)) ([399889c](399889c6fa))
* **YouTube - Enable slide to seek:** Change patch to default include ([76fd33c](76fd33ca54))
* **YouTube - Hide layout components:** Hide new type of community post ([#4404](https://github.com/ReVanced/revanced-patches/issues/4404)) ([a06c031](a06c0318bf))
* **YouTube - Theme:** Use custom seekbar color for cairo startup animation ([#4399](https://github.com/ReVanced/revanced-patches/issues/4399)) ([f81b658](f81b658fb7))

### Features

* **YouTube - Change start page:** Add additional start pages ([#4413](https://github.com/ReVanced/revanced-patches/issues/4413)) ([b7ebfdd](b7ebfddf65))
2025-02-07 07:05:36 +00:00
LisoUseInAIKyrios
6e936fea42 chore: Merge branch dev to main (#4388) 2025-02-07 09:02:22 +02:00
github-actions[bot]
f63769f39f chore: Sync translations (#4421) 2025-02-07 08:58:04 +02:00
semantic-release-bot
1c9ab20a63 chore: Release v5.11.0-dev.2 [skip ci]
# [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)) ([399889c](399889c6fa))
2025-02-06 12:29:29 +00:00
github-actions[bot]
cdeccad908 chore: Sync translations (#4417) 2025-02-06 14:25:50 +02:00
LisoUseInAIKyrios
399889c6fa fix: Fix broken Remove screen capture restriction, Remove screenshot restriction, Spoof Wi-Fi connection, and Export internal data documents provider patch (#4405) 2025-02-06 14:24:40 +02:00
github-actions[bot]
ec77861410 chore: Sync translations (#4415) 2025-02-05 20:42:07 +02:00
semantic-release-bot
b5afc6d827 chore: Release v5.11.0-dev.1 [skip ci]
# [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)) ([b7ebfdd](b7ebfddf65))
2025-02-05 18:36:04 +00:00
LisoUseInAIKyrios
b7ebfddf65 feat(YouTube - Change start page): Add additional start pages (#4413) 2025-02-05 20:32:42 +02:00
github-actions[bot]
2742aca48b chore: Sync translations (#4414) 2025-02-05 20:32:21 +02:00
251 changed files with 7351 additions and 5443 deletions

View File

@@ -1,3 +1,179 @@
# [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) ## [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)

View File

@@ -1,4 +1,11 @@
android.namespace = "app.revanced.extension" android {
namespace = "app.revanced.extension"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies { dependencies {
compileOnly(libs.annotation) compileOnly(libs.annotation)

View File

@@ -12,7 +12,7 @@ import android.os.Handler;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
/** @noinspection deprecation, unused */ @SuppressWarnings({"deprecation", "unused"})
public class SpoofWifiPatch { public class SpoofWifiPatch {
// Used to check what the (real or fake) active network is (take a look at `hasTransport`). // Used to check what the (real or fake) active network is (take a look at `hasTransport`).

View File

@@ -1,3 +1,16 @@
android {
namespace = "app.revanced.extension"
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies { dependencies {
compileOnly(libs.annotation) compileOnly(libs.annotation)
} }

View File

@@ -1,5 +1,6 @@
package app.revanced.extension.all.misc.directory.documentsprovider; package app.revanced.extension.all.misc.directory.documentsprovider;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo; 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. * A DocumentsProvider that allows access to the app's internal data directory.
*/ */
@SuppressLint("LongLogTag")
public class InternalDataDocumentsProvider extends DocumentsProvider { public class InternalDataDocumentsProvider extends DocumentsProvider {
private static final String[] rootColumns = private static final String[] rootColumns =
{"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"}; {"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"};

View File

@@ -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 { dependencies {
compileOnly(libs.annotation) compileOnly(libs.annotation)

View File

@@ -5,7 +5,8 @@ import android.os.Build;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
public final class RemoveScreencaptureRestrictionPatch { @SuppressWarnings("unused")
public final class RemoveScreenCaptureRestrictionPatch {
// Member of AudioAttributes.Builder // Member of AudioAttributes.Builder
@RequiresApi(api = Build.VERSION_CODES.Q) @RequiresApi(api = Build.VERSION_CODES.Q)
public static AudioAttributes.Builder setAllowedCapturePolicy(final AudioAttributes.Builder builder, final int capturePolicy) { public static AudioAttributes.Builder setAllowedCapturePolicy(final AudioAttributes.Builder builder, final int capturePolicy) {

View File

@@ -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)
}

View File

@@ -3,6 +3,7 @@ package app.revanced.extension.all.screenshot.removerestriction;
import android.view.Window; import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
@SuppressWarnings("unused")
public class RemoveScreenshotRestrictionPatch { public class RemoveScreenshotRestrictionPatch {
public static void addFlags(Window window, int flags) { public static void addFlags(Window window, int flags) {

View File

@@ -4,7 +4,7 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24

View File

@@ -1 +1,5 @@
// Do not remove. Necessary for the extension plugin to be applied to the project. android {
defaultConfig {
minSdk = 26
}
}

View File

@@ -4,7 +4,7 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24

View File

@@ -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, 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()); "revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
// Client type must be last spoof setting due to cyclic references. // 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));
} }

View File

@@ -47,6 +47,10 @@ public class BooleanSetting extends Setting<Boolean> {
*/ */
public static void privateSetValue(@NonNull BooleanSetting setting, @NonNull Boolean newValue) { public static void privateSetValue(@NonNull BooleanSetting setting, @NonNull Boolean newValue) {
setting.value = Objects.requireNonNull(newValue); setting.value = Objects.requireNonNull(newValue);
if (setting.isSetToDefault()) {
setting.removeFromPreferences();
}
} }
@Override @Override
@@ -65,10 +69,8 @@ public class BooleanSetting extends Setting<Boolean> {
} }
@Override @Override
public void save(@NonNull Boolean newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveBoolean(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveBoolean(key, newValue);
} }
@NonNull @NonNull

View File

@@ -89,10 +89,8 @@ public class EnumSetting<T extends Enum<?>> extends Setting<T> {
} }
@Override @Override
public void save(@NonNull T newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveEnumAsString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveEnumAsString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -55,10 +55,8 @@ public class FloatSetting extends Setting<Float> {
} }
@Override @Override
public void save(@NonNull Float newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveFloatString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveFloatString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -55,10 +55,8 @@ public class IntegerSetting extends Setting<Integer> {
} }
@Override @Override
public void save(@NonNull Integer newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveIntegerString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveIntegerString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -55,10 +55,8 @@ public class LongSetting extends Setting<Long> {
} }
@Override @Override
public void save(@NonNull Long newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveLongString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveLongString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -14,7 +14,6 @@ import java.util.*;
import static app.revanced.extension.shared.StringRef.str; import static app.revanced.extension.shared.StringRef.str;
@SuppressWarnings("unused")
public abstract class Setting<T> { public abstract class Setting<T> {
/** /**
@@ -288,6 +287,13 @@ public abstract class Setting<T> {
*/ */
public static void privateSetValueFromString(@NonNull Setting<?> setting, @NonNull String newValue) { public static void privateSetValueFromString(@NonNull Setting<?> setting, @NonNull String newValue) {
setting.setValueFromString(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. * 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 @NonNull
public abstract T get(); public abstract T get();

View File

@@ -55,10 +55,8 @@ public class StringSetting extends Setting<String> {
} }
@Override @Override
public void save(@NonNull String newValue) { public void saveToPreferences() {
// Must set before saving to preferences (otherwise importing fails to update UI correctly). preferences.saveString(key, value);
value = Objects.requireNonNull(newValue);
preferences.saveString(key, newValue);
} }
@NonNull @NonNull

View File

@@ -66,22 +66,6 @@ public enum ClientType {
true, true,
"Android Creator" "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( IOS_UNPLUGGED(
33, 33,
"IOS_UNPLUGGED", "IOS_UNPLUGGED",
@@ -112,6 +96,22 @@ public enum ClientType {
forceAVC() forceAVC()
? "iOS TV Force AVC" ? "iOS TV Force AVC"
: "iOS TV" : "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() { private static boolean forceAVC() {

View File

@@ -4,7 +4,7 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24

View File

@@ -3,3 +3,14 @@ dependencies {
compileOnly(project(":extensions:tiktok:stub")) compileOnly(project(":extensions:tiktok:stub"))
compileOnly(libs.annotation) compileOnly(libs.annotation)
} }
android {
defaultConfig {
minSdk = 22
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View File

@@ -21,9 +21,11 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
protected void syncSettingWithPreference(@NonNull Preference pref, protected void syncSettingWithPreference(@NonNull Preference pref,
@NonNull Setting<?> setting, @NonNull Setting<?> setting,
boolean applySettingToPreference) { boolean applySettingToPreference) {
if (pref instanceof RangeValuePreference rangeValuePref) { if (pref instanceof RangeValuePreference) {
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
Setting.privateSetValueFromString(setting, rangeValuePref.getValue()); 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()); Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
} else { } else {
super.syncSettingWithPreference(pref, setting, applySettingToPreference); super.syncSettingWithPreference(pref, setting, applySettingToPreference);
@@ -32,7 +34,7 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
@Override @Override
protected void initialize() { protected void initialize() {
final var context = getContext(); final var context = getActivity();
// Currently no resources can be compiled for TikTok (fails with aapt error). // Currently no resources can be compiled for TikTok (fails with aapt error).
// So all TikTok Strings are hard coded in the extension. // So all TikTok Strings are hard coded in the extension.

View File

@@ -4,14 +4,9 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 22
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
} }
} }

View File

@@ -1,3 +1,9 @@
dependencies { dependencies {
compileOnly(project(":extensions:tumblr:stub")) compileOnly(project(":extensions:tumblr:stub"))
} }
android {
defaultConfig {
minSdk = 26
}
}

View File

@@ -1,17 +1,14 @@
android.namespace = "app.revanced.extension"
plugins { plugins {
id(libs.plugins.android.library.get().pluginId) id(libs.plugins.android.library.get().pluginId)
} }
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
} }
} }

View File

@@ -6,3 +6,14 @@ dependencies {
compileOnly(libs.annotation) compileOnly(libs.annotation)
compileOnly(libs.appcompat) compileOnly(libs.appcompat)
} }
android {
defaultConfig {
minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

View File

@@ -4,14 +4,9 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 21
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
} }
} }

View File

@@ -1,8 +1,11 @@
//noinspection GradleDependency
android.compileSdk = 33
dependencies { dependencies {
compileOnly(project(":extensions:shared:library")) compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:youtube:stub")) compileOnly(project(":extensions:youtube:stub"))
compileOnly(libs.annotation) compileOnly(libs.annotation)
} }
android {
defaultConfig {
minSdk = 26
}
}

View File

@@ -23,21 +23,30 @@ public final class ChangeStartPagePatch {
/** /**
* Browse id. * Browse id.
*/ */
ALL_SUBSCRIPTIONS("FEchannels", TRUE),
BROWSE("FEguide_builder", TRUE), BROWSE("FEguide_builder", TRUE),
EXPLORE("FEexplore", TRUE), EXPLORE("FEexplore", TRUE),
HISTORY("FEhistory", TRUE), HISTORY("FEhistory", TRUE),
LIBRARY("FElibrary", TRUE), LIBRARY("FElibrary", TRUE),
MOVIE("FEstorefront", TRUE), MOVIE("FEstorefront", TRUE),
NOTIFICATIONS("FEactivity", TRUE),
PLAYLISTS("FEplaylist_aggregation", TRUE),
SUBSCRIPTIONS("FEsubscriptions", TRUE), SUBSCRIPTIONS("FEsubscriptions", TRUE),
TRENDING("FEtrending", TRUE), TRENDING("FEtrending", TRUE),
YOUR_CLIPS("FEclips", TRUE),
/** /**
* Channel id, this can be used as a browseId. * Channel id, this can be used as a browseId.
*/ */
COURSES("UCtFRv9O2AHqOZjjynzrv-xg", TRUE),
FASHION("UCrpQ4p1Ql_hG8rKXIKM1MOQ", TRUE),
GAMING("UCOpNcN46UbXVtpKMrmU4Abg", TRUE), GAMING("UCOpNcN46UbXVtpKMrmU4Abg", TRUE),
LIVE("UC4R8DWoMoI7CAwX8_LjQHig", TRUE), LIVE("UC4R8DWoMoI7CAwX8_LjQHig", TRUE),
MUSIC("UC-9-kyTW8ZkZNDHQJ6FgpwQ", TRUE), MUSIC("UC-9-kyTW8ZkZNDHQJ6FgpwQ", TRUE),
NEWS("UCYfdidRxbB8Qhf0Nx7ioOYw", TRUE),
SHOPPING("UCkYQyvc_i9hXEo4xic9Hh2g", TRUE),
SPORTS("UCEgdi0XIXXZ-qJOFPf4JSKw", TRUE), SPORTS("UCEgdi0XIXXZ-qJOFPf4JSKw", TRUE),
VIRTUAL_REALITY("UCzuqhhs6NWbgTzMuM09WKDQ", TRUE),
/** /**
* Playlist id, this can be used as a browseId. * 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), SEARCH("com.google.android.youtube.action.open.search", FALSE),
SHORTS("com.google.android.youtube.action.open.shorts", FALSE); SHORTS("com.google.android.youtube.action.open.shorts", FALSE);
@Nullable
final Boolean isBrowseId;
@NonNull @NonNull
final String id; final String id;
@Nullable
final Boolean isBrowseId;
StartPage(@NonNull String id, @Nullable Boolean isBrowseId) { StartPage(@NonNull String id, @Nullable Boolean isBrowseId) {
this.id = id; this.id = id;
this.isBrowseId = isBrowseId; this.isBrowseId = isBrowseId;
@@ -122,7 +131,7 @@ public final class ChangeStartPagePatch {
} }
appLaunched = true; appLaunched = true;
final String intentAction = START_PAGE.id; String intentAction = START_PAGE.id;
Logger.printDebug(() -> "Changing intent action to " + intentAction); Logger.printDebug(() -> "Changing intent action to " + intentAction);
intent.setAction(intentAction); intent.setAction(intentAction);
} }

View File

@@ -21,6 +21,7 @@ public final class NavigationButtonsPatch {
{ {
put(NavigationButton.HOME, Settings.HIDE_HOME_BUTTON.get()); put(NavigationButton.HOME, Settings.HIDE_HOME_BUTTON.get());
put(NavigationButton.CREATE, Settings.HIDE_CREATE_BUTTON.get()); put(NavigationButton.CREATE, Settings.HIDE_CREATE_BUTTON.get());
put(NavigationButton.NOTIFICATIONS, Settings.HIDE_NOTIFICATIONS_BUTTON.get());
put(NavigationButton.SHORTS, Settings.HIDE_SHORTS_BUTTON.get()); put(NavigationButton.SHORTS, Settings.HIDE_SHORTS_BUTTON.get());
put(NavigationButton.SUBSCRIPTIONS, Settings.HIDE_SUBSCRIPTIONS_BUTTON.get()); put(NavigationButton.SUBSCRIPTIONS, Settings.HIDE_SUBSCRIPTIONS_BUTTON.get());
} }

View File

@@ -4,7 +4,6 @@ import static app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeD
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
@@ -366,9 +365,7 @@ public class ReturnYouTubeDislikePatch {
private static final List<WeakReference<TextView>> shortsTextViewRefs = new ArrayList<>(); private static final List<WeakReference<TextView>> shortsTextViewRefs = new ArrayList<>();
private static void clearRemovedShortsTextViews() { 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);
}
} }
/** /**

View File

@@ -1,9 +1,6 @@
package app.revanced.extension.youtube.patches; package app.revanced.extension.youtube.patches;
import android.app.Activity; import android.app.Activity;
import android.os.Build;
import androidx.annotation.RequiresApi;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Objects; import java.util.Objects;
@@ -58,7 +55,6 @@ public class ShortsAutoplayPatch {
/** /**
* @return If the app is currently in background PiP mode. * @return If the app is currently in background PiP mode.
*/ */
@RequiresApi(api = Build.VERSION_CODES.N)
private static boolean isAppInBackgroundPiPMode() { private static boolean isAppInBackgroundPiPMode() {
Activity activity = mainActivityRef.get(); Activity activity = mainActivityRef.get();
return activity != null && activity.isInPictureInPictureMode(); return activity != null && activity.isInPictureInPictureMode();
@@ -80,7 +76,6 @@ public class ShortsAutoplayPatch {
/** /**
* Injection point. * Injection point.
*/ */
@RequiresApi(api = Build.VERSION_CODES.N)
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) { public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
try { try {
final boolean autoplay; final boolean autoplay;

View File

@@ -7,13 +7,10 @@ import static app.revanced.extension.youtube.patches.announcements.requests.Anno
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.os.Build;
import android.text.Html; import android.text.Html;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.RequiresApi;
import org.json.JSONArray; import org.json.JSONArray;
import java.io.IOException; import java.io.IOException;
@@ -31,7 +28,6 @@ public final class AnnouncementsPatch {
private AnnouncementsPatch() { private AnnouncementsPatch() {
} }
@RequiresApi(api = Build.VERSION_CODES.O)
private static boolean isLatestAlready() throws IOException { private static boolean isLatestAlready() throws IOException {
HttpURLConnection connection = HttpURLConnection connection =
AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS); AnnouncementsRoutes.getAnnouncementsConnectionFromRoute(GET_LATEST_ANNOUNCEMENT_IDS);
@@ -70,7 +66,6 @@ public final class AnnouncementsPatch {
return Settings.ANNOUNCEMENT_LAST_ID.get() == id; return Settings.ANNOUNCEMENT_LAST_ID.get() == id;
} }
@RequiresApi(api = Build.VERSION_CODES.O)
public static void showAnnouncement(final Activity context) { public static void showAnnouncement(final Activity context) {
if (!Settings.ANNOUNCEMENTS.get()) return; if (!Settings.ANNOUNCEMENTS.get()) return;

View File

@@ -6,8 +6,12 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
final class ButtonsFilter extends Filter { final class ButtonsFilter extends Filter {
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml"; private static final String VIDEO_ACTION_BAR_PATH = "video_action_bar.eml";
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
private final StringFilterGroup likeSubscribeGlow;
private final StringFilterGroup actionBarGroup; private final StringFilterGroup actionBarGroup;
private final StringFilterGroup bufferFilterPathGroup; private final StringFilterGroup bufferFilterPathGroup;
private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList(); private final ByteArrayFilterGroupList bufferButtonsGroupList = new ByteArrayFilterGroupList();
@@ -20,11 +24,19 @@ final class ButtonsFilter extends Filter {
addIdentifierCallbacks(actionBarGroup); addIdentifierCallbacks(actionBarGroup);
likeSubscribeGlow = new StringFilterGroup(
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
"animated_button_border.eml"
);
bufferFilterPathGroup = new StringFilterGroup( bufferFilterPathGroup = new StringFilterGroup(
null, null,
"|ContainerType|button.eml|" "|ContainerType|button.eml|"
); );
addPathCallbacks( addPathCallbacks(
likeSubscribeGlow,
bufferFilterPathGroup,
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_LIKE_DISLIKE_BUTTON, Settings.HIDE_LIKE_DISLIKE_BUTTON,
"|segmented_like_dislike_button" "|segmented_like_dislike_button"
@@ -40,8 +52,7 @@ final class ButtonsFilter extends Filter {
new StringFilterGroup( new StringFilterGroup(
Settings.HIDE_CLIP_BUTTON, Settings.HIDE_CLIP_BUTTON,
"|clip_button.eml|" "|clip_button.eml|"
), )
bufferFilterPathGroup
); );
bufferButtonsGroupList.addAll( bufferButtonsGroupList.addAll(
@@ -83,6 +94,15 @@ final class ButtonsFilter extends Filter {
@Override @Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (matchedGroup == likeSubscribeGlow) {
if ((path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
&& path.contains(ANIMATED_VECTOR_TYPE_PATH)) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
// If the current matched group is the action bar group, // If the current matched group is the action bar group,
// in case every filter group is enabled, hide the action bar. // in case every filter group is enabled, hide the action bar.
if (matchedGroup == actionBarGroup) { if (matchedGroup == actionBarGroup) {

View File

@@ -1,9 +1,6 @@
package app.revanced.extension.youtube.patches.components; package app.revanced.extension.youtube.patches.components;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -44,13 +41,11 @@ abstract class FilterGroupList<V, T extends FilterGroup<V>> implements Iterable<
return filterGroups.iterator(); return filterGroups.iterator();
} }
@RequiresApi(api = Build.VERSION_CODES.N)
@Override @Override
public void forEach(@NonNull Consumer<? super T> action) { public void forEach(@NonNull Consumer<? super T> action) {
filterGroups.forEach(action); filterGroups.forEach(action);
} }
@RequiresApi(api = Build.VERSION_CODES.N)
@NonNull @NonNull
@Override @Override
public Spliterator<T> spliterator() { public Spliterator<T> spliterator() {

View File

@@ -4,11 +4,8 @@ import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import static java.lang.Character.UnicodeBlock.*; import static java.lang.Character.UnicodeBlock.*;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -44,7 +41,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
* - When using whole word syntax, some keywords may need additional pluralized variations. * - When using whole word syntax, some keywords may need additional pluralized variations.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@RequiresApi(api = Build.VERSION_CODES.N)
final class KeywordContentFilter extends Filter { final class KeywordContentFilter extends Filter {
/** /**

View File

@@ -3,11 +3,9 @@ package app.revanced.extension.youtube.patches.components;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View; import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
@@ -18,10 +16,6 @@ import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class LayoutComponentsFilter extends Filter { public final class LayoutComponentsFilter extends Filter {
private static final String COMPACT_CHANNEL_BAR_PATH_PREFIX = "compact_channel_bar.eml";
private static final String VIDEO_ACTION_BAR_PATH_PREFIX = "video_action_bar.eml";
private static final String ANIMATED_VECTOR_TYPE_PATH = "AnimatedVectorType";
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch( private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch(
"V.ED", // Playlist browse id. "V.ED", // Playlist browse id.
"java.lang.ref.WeakReference" "java.lang.ref.WeakReference"
@@ -38,16 +32,15 @@ public final class LayoutComponentsFilter extends Filter {
private final StringTrieSearch exceptions = new StringTrieSearch(); private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup inFeedSurvey; private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup notifyMe; private final StringFilterGroup notifyMe;
private final StringFilterGroup singleItemInformationPanel;
private final StringFilterGroup expandableMetadata; private final StringFilterGroup expandableMetadata;
private final ByteArrayFilterGroup searchResultRecommendations; private final ByteArrayFilterGroup searchResultRecommendations;
private final StringFilterGroup searchResultVideo; private final StringFilterGroup searchResultVideo;
private final StringFilterGroup compactChannelBarInner; private final StringFilterGroup compactChannelBarInner;
private final StringFilterGroup compactChannelBarInnerButton; private final StringFilterGroup compactChannelBarInnerButton;
private final ByteArrayFilterGroup joinMembershipButton; private final ByteArrayFilterGroup joinMembershipButton;
private final StringFilterGroup likeSubscribeGlow;
private final StringFilterGroup horizontalShelves; private final StringFilterGroup horizontalShelves;
@RequiresApi(api = Build.VERSION_CODES.N)
public LayoutComponentsFilter() { public LayoutComponentsFilter() {
exceptions.addPatterns( exceptions.addPatterns(
"home_video_with_context", "home_video_with_context",
@@ -123,8 +116,12 @@ public final class LayoutComponentsFilter extends Filter {
); );
final var infoPanel = new StringFilterGroup( final var infoPanel = new StringFilterGroup(
Settings.HIDE_HIDE_INFO_PANELS, Settings.HIDE_INFO_PANELS,
"publisher_transparency_panel", "publisher_transparency_panel"
);
singleItemInformationPanel = new StringFilterGroup(
Settings.HIDE_INFO_PANELS,
"single_item_information_panel" "single_item_information_panel"
); );
@@ -217,10 +214,6 @@ public final class LayoutComponentsFilter extends Filter {
"sponsorships" "sponsorships"
); );
likeSubscribeGlow = new StringFilterGroup(
Settings.DISABLE_LIKE_SUBSCRIBE_GLOW,
"animated_button_border.eml"
);
final var channelWatermark = new StringFilterGroup( final var channelWatermark = new StringFilterGroup(
Settings.HIDE_VIDEO_CHANNEL_WATERMARK, Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
@@ -254,7 +247,6 @@ public final class LayoutComponentsFilter extends Filter {
expandableMetadata, expandableMetadata,
inFeedSurvey, inFeedSurvey,
notifyMe, notifyMe,
likeSubscribeGlow,
compactChannelBar, compactChannelBar,
communityPosts, communityPosts,
paidPromotion, paidPromotion,
@@ -269,6 +261,7 @@ public final class LayoutComponentsFilter extends Filter {
compactChannelBarInner, compactChannelBarInner,
medicalPanel, medicalPanel,
infoPanel, infoPanel,
singleItemInformationPanel,
emergencyBox, emergencyBox,
subscribersCommunityGuidelines, subscribersCommunityGuidelines,
channelGuidelines, channelGuidelines,
@@ -285,6 +278,19 @@ public final class LayoutComponentsFilter extends Filter {
@Override @Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray, boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) { StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
// This identifier is used not only in players but also in search results:
// https://github.com/ReVanced/revanced-patches/issues/3245
// Until 2024, medical information panels such as Covid 19 also used this identifier and were shown in the search results.
// From 2025, the medical information panel is no longer shown in the search results.
// Therefore, this identifier does not filter when the search bar is activated.
if (matchedGroup == singleItemInformationPanel) {
if (PlayerType.getCurrent().isMaximizedOrFullscreen() || !NavigationBar.isSearchBarActive()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
if (matchedGroup == searchResultVideo) { if (matchedGroup == searchResultVideo) {
if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) { if (searchResultRecommendations.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex); return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
@@ -292,15 +298,6 @@ public final class LayoutComponentsFilter extends Filter {
return false; return false;
} }
if (matchedGroup == likeSubscribeGlow) {
if ((path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
&& path.contains(ANIMATED_VECTOR_TYPE_PATH)) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
// The groups are excluded from the filter due to the exceptions list below. // The groups are excluded from the filter due to the exceptions list below.
// Filter them separately here. // Filter them separately here.
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata) if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata)

View File

@@ -1,9 +1,6 @@
package app.revanced.extension.youtube.patches.components; package app.revanced.extension.youtube.patches.components;
import android.os.Build;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.PlayerType;
@@ -16,7 +13,6 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
private final ByteArrayFilterGroup exception; private final ByteArrayFilterGroup exception;
private final StringFilterGroup videoQualityMenuFooter; private final StringFilterGroup videoQualityMenuFooter;
@RequiresApi(api = Build.VERSION_CODES.N)
public PlayerFlyoutMenuItemsFilter() { public PlayerFlyoutMenuItemsFilter() {
exception = new ByteArrayFilterGroup( exception = new ByteArrayFilterGroup(
// Whitelist Quality menu item when "Hide Additional settings menu" is enabled // Whitelist Quality menu item when "Hide Additional settings menu" is enabled

View File

@@ -234,6 +234,12 @@ public class ReturnYouTubeDislike {
// example video: https://www.youtube.com/watch?v=UnrU5vxCHxw // example video: https://www.youtube.com/watch?v=UnrU5vxCHxw
// RYD data: https://returnyoutubedislikeapi.com/votes?videoId=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"); Logger.printDebug(() -> "Using estimated likes");
oldLikes = formatDislikeCount(voteData.getLikeCount()); oldLikes = formatDislikeCount(voteData.getLikeCount());
} }
@@ -346,56 +352,49 @@ public class ReturnYouTubeDislike {
} }
private static String formatDislikeCount(long dislikeCount) { 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
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize if (dislikeCountFormatter == null) {
if (dislikeCountFormatter == null) { Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale; dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
dislikeCountFormatter = CompactDecimalFormat.getInstance(locale, CompactDecimalFormat.CompactStyle.SHORT);
// YouTube disregards locale specific number characters // YouTube disregards locale specific number characters
// and instead shows english number characters everywhere. // and instead shows english number characters everywhere.
// To use the same behavior, override the digit characters to use English // To use the same behavior, override the digit characters to use English
// so languages such as Arabic will show "1.234" instead of the native "۱,۲۳٤" // so languages such as Arabic will show "1.234" instead of the native "۱,۲۳٤"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings()); symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
dislikeCountFormatter.setDecimalFormatSymbols(symbols); dislikeCountFormatter.setDecimalFormatSymbols(symbols);
}
} }
return dislikeCountFormatter.format(dislikeCount);
} }
}
// Will never be reached, as the oldest supported YouTube app requires Android N or greater. return dislikeCountFormatter.format(dislikeCount);
return String.valueOf(dislikeCount); }
} }
private static String formatDislikePercentage(float dislikePercentage) { 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.
synchronized (ReturnYouTubeDislike.class) { // number formatter is not thread safe, must synchronize if (dislikePercentageFormatter == null) {
if (dislikePercentageFormatter == null) { Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale;
Locale locale = Objects.requireNonNull(Utils.getContext()).getResources().getConfiguration().locale; dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
dislikePercentageFormatter = NumberFormat.getPercentInstance(locale);
// Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns. // Want to set the digit strings, and the simplest way is to cast to the implementation NumberFormat returns.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
&& dislikePercentageFormatter instanceof DecimalFormat) { && dislikePercentageFormatter instanceof DecimalFormat) {
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings()); symbols.setDigitStrings(DecimalFormatSymbols.getInstance(Locale.ENGLISH).getDigitStrings());
((DecimalFormat) dislikePercentageFormatter).setDecimalFormatSymbols(symbols); ((DecimalFormat) dislikePercentageFormatter).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. if (dislikePercentage >= 0.01) { // at least 1%
return String.valueOf((int) (dislikePercentage * 100)); dislikePercentageFormatter.setMaximumFractionDigits(0); // show only whole percentage points
} else {
dislikePercentageFormatter.setMaximumFractionDigits(1); // show up to 1 digit precision
}
return dislikePercentageFormatter.format(dislikePercentage);
}
} }
@NonNull @NonNull
@@ -403,15 +402,13 @@ public class ReturnYouTubeDislike {
Objects.requireNonNull(videoId); Objects.requireNonNull(videoId);
synchronized (fetchCache) { synchronized (fetchCache) {
// Remove any expired entries. // Remove any expired entries.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { final long now = System.currentTimeMillis();
final long now = System.currentTimeMillis(); fetchCache.values().removeIf(value -> {
fetchCache.values().removeIf(value -> { final boolean expired = value.isExpired(now);
final boolean expired = value.isExpired(now); if (expired)
if (expired) Logger.printDebug(() -> "Removing expired fetch: " + value.videoId);
Logger.printDebug(() -> "Removing expired fetch: " + value.videoId); return expired;
return expired; });
});
}
ReturnYouTubeDislike fetch = fetchCache.get(videoId); ReturnYouTubeDislike fetch = fetchCache.get(videoId);
if (fetch == null) { if (fetch == null) {
@@ -551,6 +548,15 @@ public class ReturnYouTubeDislike {
} }
if (spanIsForLikes) { 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, // Scrolling Shorts does not cause the Spans to be reloaded,
// so there is no need to cache the likes for this situations. // so there is no need to cache the likes for this situations.
Logger.printDebug(() -> "Creating likes span for: " + votingData.videoId); Logger.printDebug(() -> "Creating likes span for: " + votingData.videoId);

View File

@@ -5,15 +5,12 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.preference.PreferenceFragment; import android.preference.PreferenceFragment;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toolbar; import android.widget.Toolbar;
import androidx.annotation.RequiresApi;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
@@ -22,6 +19,7 @@ import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.ThemeHelper; import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.VersionCheckPatch; import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.patches.spoof.SpoofAppVersionPatch;
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment; import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment; import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment; import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
@@ -66,6 +64,10 @@ public class LicenseActivityHook {
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) { if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
return false; return false;
} }
// Spoofing can cause half broken settings menus of old and new settings.
if (SpoofAppVersionPatch.isSpoofingToLessThan("19.35.36")) {
return false;
}
// On the first launch of a clean install, forcing the cairo menu can give a // On the first launch of a clean install, forcing the cairo menu can give a
// half broken appearance because all the preference icons may not be available yet. // half broken appearance because all the preference icons may not be available yet.
@@ -79,7 +81,6 @@ public class LicenseActivityHook {
* <p> * <p>
* Hooks LicenseActivity#onCreate in order to inject our own fragment. * Hooks LicenseActivity#onCreate in order to inject our own fragment.
*/ */
@RequiresApi(api = Build.VERSION_CODES.N)
public static void initialize(Activity licenseActivity) { public static void initialize(Activity licenseActivity) {
try { try {
ThemeHelper.setActivityTheme(licenseActivity); ThemeHelper.setActivityTheme(licenseActivity);
@@ -119,15 +120,13 @@ public class LicenseActivityHook {
} }
} }
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressLint("UseCompatLoadingForDrawables") @SuppressLint("UseCompatLoadingForDrawables")
private static void createToolbar(Activity activity, String toolbarTitleResourceName) { private static void createToolbar(Activity activity, String toolbarTitleResourceName) {
// Replace dummy placeholder toolbar. // Replace dummy placeholder toolbar.
// This is required to fix submenu title alignment issue with Android ASOP 15+ // This is required to fix submenu title alignment issue with Android ASOP 15+
ViewGroup toolBarParent = activity.findViewById( ViewGroup toolBarParent = activity.findViewById(
getResourceIdentifier("revanced_toolbar_parent", "id")); getResourceIdentifier("revanced_toolbar_parent", "id"));
ViewGroup dummyToolbar = toolBarParent.findViewById(getResourceIdentifier( ViewGroup dummyToolbar = Utils.getChildViewByResourceName(toolBarParent,"revanced_toolbar");
"revanced_toolbar", "id"));
toolbarLayoutParams = dummyToolbar.getLayoutParams(); toolbarLayoutParams = dummyToolbar.getLayoutParams();
toolBarParent.removeView(dummyToolbar); toolBarParent.removeView(dummyToolbar);

View File

@@ -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 = new BooleanSetting("revanced_copy_video_url", FALSE);
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE); public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true); public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE); public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true); public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED); public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
@@ -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_EMERGENCY_BOX = new BooleanSetting("revanced_hide_emergency_box", TRUE);
public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE); public static final BooleanSetting HIDE_ENDSCREEN_CARDS = new BooleanSetting("revanced_hide_endscreen_cards", FALSE);
public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE); public static final BooleanSetting HIDE_HIDE_CHANNEL_GUIDELINES = new BooleanSetting("revanced_hide_channel_guidelines", TRUE);
public static final BooleanSetting HIDE_HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE); public static final BooleanSetting HIDE_INFO_PANELS = new BooleanSetting("revanced_hide_info_panels", TRUE);
public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE); public static final BooleanSetting HIDE_INFO_CARDS = new BooleanSetting("revanced_hide_info_cards", FALSE);
public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE); public static final BooleanSetting HIDE_JOIN_MEMBERSHIP_BUTTON = new BooleanSetting("revanced_hide_join_membership_button", TRUE);
public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE); public static final BooleanSetting HIDE_MEDICAL_PANELS = new BooleanSetting("revanced_hide_medical_panels", TRUE);
@@ -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_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE); public static final BooleanSetting HIDE_TRANSCRIPT_SECTION = new BooleanSetting("revanced_hide_transcript_section", TRUE);
// Action buttons // Action buttons
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE); public static final BooleanSetting HIDE_CLIP_BUTTON = new BooleanSetting("revanced_hide_clip_button", TRUE);
public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE); public static final BooleanSetting HIDE_DOWNLOAD_BUTTON = new BooleanSetting("revanced_hide_download_button", FALSE);
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE); public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
@@ -228,7 +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_SHORTS_BUTTON = new BooleanSetting("revanced_hide_shorts_button", TRUE, true);
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true); public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true); public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true); public static final BooleanSetting HIDE_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_hide_notifications_button", FALSE, true);
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true,
"revanced_switch_create_with_notifications_button_user_dialog_message");
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true); public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true);
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true); public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true);
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true); public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true);
@@ -306,21 +308,21 @@ public class Settings extends BaseSettings {
// Swipe controls // Swipe controls
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true); public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE); public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE, true);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE); public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE, true);
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true, public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true, public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true, public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 50, true, public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
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)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127); private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
// Debugging
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 22, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true, public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME)); parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS)); public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
@@ -333,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_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_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_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)); public static final BooleanSetting RYD_TOAST_ON_CONNECTION_ERROR = new BooleanSetting("ryd_toast_on_connection_error", TRUE, parent(RYD_ENABLED));
// SponsorBlock // SponsorBlock
@@ -344,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 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_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_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_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_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_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_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 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 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 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 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); public static final IntegerSetting SB_LOCAL_TIME_SAVED_NUMBER_SEGMENTS = new IntegerSetting("sb_local_time_saved_number_segments", 0);
@@ -401,12 +405,6 @@ public class Settings extends BaseSettings {
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER); migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
// Old spoof versions that no longer work reliably.
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. // Migrate renamed enum.
//noinspection deprecation //noinspection deprecation
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) { if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {

View File

@@ -3,18 +3,14 @@ package app.revanced.extension.youtube.settings.preference;
import static android.text.Html.FROM_HTML_MODE_COMPACT; import static android.text.Html.FROM_HTML_MODE_COMPACT;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.preference.Preference; import android.preference.Preference;
import android.text.Html; import android.text.Html;
import android.util.AttributeSet; import android.util.AttributeSet;
import androidx.annotation.RequiresApi;
/** /**
* Allows using basic html for the summary text. * Allows using basic html for the summary text.
*/ */
@SuppressWarnings({"unused", "deprecation"}) @SuppressWarnings({"unused", "deprecation"})
@RequiresApi(api = Build.VERSION_CODES.O)
public class HtmlPreference extends Preference { public class HtmlPreference extends Preference {
{ {
setSummary(Html.fromHtml(getSummary().toString(), FROM_HTML_MODE_COMPACT)); setSummary(Html.fromHtml(getSummary().toString(), FROM_HTML_MODE_COMPACT));

View File

@@ -17,8 +17,6 @@ import android.view.WindowInsets;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toolbar; import android.widget.Toolbar;
import androidx.annotation.RequiresApi;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -98,7 +96,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
listPreference.setEntryValues(sortedEntryValues); listPreference.setEntryValues(sortedEntryValues);
} }
@RequiresApi(api = Build.VERSION_CODES.O)
@Override @Override
protected void initialize() { protected void initialize() {
super.initialize(); super.initialize();
@@ -153,13 +150,10 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
toolbar.setTitle(childScreen.getTitle()); toolbar.setTitle(childScreen.getTitle());
toolbar.setNavigationIcon(getBackButtonDrawable()); toolbar.setNavigationIcon(getBackButtonDrawable());
toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss()); toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss());
final int margin = (int) TypedValue.applyDimension(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()
final int margin = (int) TypedValue.applyDimension( );
TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics() toolbar.setTitleMargin(margin, 0, margin, 0);
);
toolbar.setTitleMargin(margin, 0, margin, 0);
}
TextView toolbarTextView = Utils.getChildView(toolbar, TextView toolbarTextView = Utils.getChildView(toolbar,
true, TextView.class::isInstance); true, TextView.class::isInstance);

View File

@@ -39,6 +39,11 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
*/ */
private SwitchPreference compactLayoutPreference; private SwitchPreference compactLayoutPreference;
/**
* If hidden likes are replaced with an estimated value.
*/
private SwitchPreference estimatedLikesPreference;
/** /**
* If segmented like/dislike button uses smaller compact layout. * If segmented like/dislike button uses smaller compact layout.
*/ */
@@ -48,6 +53,7 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
shortsPreference.setEnabled(Settings.RYD_SHORTS.isAvailable()); shortsPreference.setEnabled(Settings.RYD_SHORTS.isAvailable());
percentagePreference.setEnabled(Settings.RYD_DISLIKE_PERCENTAGE.isAvailable()); percentagePreference.setEnabled(Settings.RYD_DISLIKE_PERCENTAGE.isAvailable());
compactLayoutPreference.setEnabled(Settings.RYD_COMPACT_LAYOUT.isAvailable()); compactLayoutPreference.setEnabled(Settings.RYD_COMPACT_LAYOUT.isAvailable());
estimatedLikesPreference.setEnabled(Settings.RYD_ESTIMATED_LIKE.isAvailable());
toastOnRYDNotAvailable.setEnabled(Settings.RYD_TOAST_ON_CONNECTION_ERROR.isAvailable()); toastOnRYDNotAvailable.setEnabled(Settings.RYD_TOAST_ON_CONNECTION_ERROR.isAvailable());
} }
@@ -117,6 +123,19 @@ public class ReturnYouTubeDislikePreferenceFragment extends PreferenceFragment {
}); });
preferenceScreen.addPreference(compactLayoutPreference); 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 = new SwitchPreference(context);
toastOnRYDNotAvailable.setChecked(Settings.RYD_TOAST_ON_CONNECTION_ERROR.get()); toastOnRYDNotAvailable.setChecked(Settings.RYD_TOAST_ON_CONNECTION_ERROR.get());
toastOnRYDNotAvailable.setTitle(str("revanced_ryd_toast_on_connection_error_title")); toastOnRYDNotAvailable.setTitle(str("revanced_ryd_toast_on_connection_error_title"));

View File

@@ -6,7 +6,6 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.*; import android.preference.*;
import android.text.Html; import android.text.Html;
@@ -37,8 +36,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
private SwitchPreference sbEnabled; private SwitchPreference sbEnabled;
private SwitchPreference addNewSegment; private SwitchPreference addNewSegment;
private SwitchPreference votingEnabled; private SwitchPreference votingEnabled;
private SwitchPreference compactSkipButton;
private SwitchPreference autoHideSkipSegmentButton; private SwitchPreference autoHideSkipSegmentButton;
private SwitchPreference compactSkipButton;
private SwitchPreference squareLayout;
private SwitchPreference showSkipToast; private SwitchPreference showSkipToast;
private SwitchPreference trackSkips; private SwitchPreference trackSkips;
private SwitchPreference showTimeWithoutSegments; private SwitchPreference showTimeWithoutSegments;
@@ -62,7 +62,9 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
} else if (!Settings.SB_CREATE_NEW_SEGMENT.get()) { } else if (!Settings.SB_CREATE_NEW_SEGMENT.get()) {
SponsorBlockViewController.hideNewSegmentLayout(); 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); sbEnabled.setChecked(enabled);
@@ -72,11 +74,14 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
votingEnabled.setChecked(Settings.SB_VOTING_BUTTON.get()); votingEnabled.setChecked(Settings.SB_VOTING_BUTTON.get());
votingEnabled.setEnabled(enabled); votingEnabled.setEnabled(enabled);
autoHideSkipSegmentButton.setEnabled(enabled);
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get());
compactSkipButton.setChecked(Settings.SB_COMPACT_SKIP_BUTTON.get()); compactSkipButton.setChecked(Settings.SB_COMPACT_SKIP_BUTTON.get());
compactSkipButton.setEnabled(enabled); compactSkipButton.setEnabled(enabled);
autoHideSkipSegmentButton.setChecked(Settings.SB_AUTO_HIDE_SKIP_BUTTON.get()); squareLayout.setChecked(Settings.SB_SQUARE_LAYOUT.get());
autoHideSkipSegmentButton.setEnabled(enabled); squareLayout.setEnabled(enabled);
showSkipToast.setChecked(Settings.SB_TOAST_ON_SKIP.get()); showSkipToast.setChecked(Settings.SB_TOAST_ON_SKIP.get());
showSkipToast.setEnabled(enabled); showSkipToast.setEnabled(enabled);
@@ -176,6 +181,17 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
return true; 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 = new SwitchPreference(context);
compactSkipButton.setTitle(str("revanced_sb_enable_compact_skip_button")); compactSkipButton.setTitle(str("revanced_sb_enable_compact_skip_button"));
compactSkipButton.setSummaryOn(str("revanced_sb_enable_compact_skip_button_sum_on")); compactSkipButton.setSummaryOn(str("revanced_sb_enable_compact_skip_button_sum_on"));
@@ -187,13 +203,13 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
return true; return true;
}); });
autoHideSkipSegmentButton = new SwitchPreference(context); squareLayout = new SwitchPreference(context);
autoHideSkipSegmentButton.setTitle(str("revanced_sb_enable_auto_hide_skip_segment_button")); squareLayout.setTitle(str("revanced_sb_square_layout"));
autoHideSkipSegmentButton.setSummaryOn(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_on")); squareLayout.setSummaryOn(str("revanced_sb_square_layout_sum_on"));
autoHideSkipSegmentButton.setSummaryOff(str("revanced_sb_enable_auto_hide_skip_segment_button_sum_off")); squareLayout.setSummaryOff(str("revanced_sb_square_layout_sum_off"));
category.addPreference(autoHideSkipSegmentButton); category.addPreference(squareLayout);
autoHideSkipSegmentButton.setOnPreferenceChangeListener((preference1, newValue) -> { squareLayout.setOnPreferenceChangeListener((preference1, newValue) -> {
Settings.SB_AUTO_HIDE_SKIP_BUTTON.save((Boolean) newValue); Settings.SB_SQUARE_LAYOUT.save((Boolean) newValue);
updateUI(); updateUI();
return true; return true;
}); });
@@ -393,9 +409,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT importExport.getEditText().setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_MULTI_LINE
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); | 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.getEditText().setTextSize(TypedValue.COMPLEX_UNIT_PT, 8);
importExport.setOnPreferenceClickListener(preference1 -> { importExport.setOnPreferenceClickListener(preference1 -> {
importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings()); importExport.getEditText().setText(SponsorBlockSettings.exportDesktopSettings());

View File

@@ -85,7 +85,7 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
String summary = str(key + "_summary"); String summary = str(key + "_summary");
// Android VR supports AV1 but all other clients do not. // 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_no_av1");
} }

View File

@@ -3,11 +3,9 @@ package app.revanced.extension.youtube.shared;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE; import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
import android.app.Activity; import android.app.Activity;
import android.os.Build;
import android.view.View; import android.view.View;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Arrays; import java.util.Arrays;
@@ -257,7 +255,6 @@ public final class NavigationBar {
* Injection point. * Injection point.
* Fixes missing drawable. * Fixes missing drawable.
*/ */
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) { public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
if (fillBellCairoBlack != 0) { if (fillBellCairoBlack != 0) {

View File

@@ -21,9 +21,6 @@ enum class PlayerType {
/** /**
* A regular video is minimized. * A regular video is minimized.
*
* When spoofing to 16.x YouTube and watching a short with a regular video in the background,
* the type can be this (and not [HIDDEN]).
*/ */
WATCH_WHILE_MINIMIZED, WATCH_WHILE_MINIMIZED,
WATCH_WHILE_MAXIMIZED, WATCH_WHILE_MAXIMIZED,
@@ -56,8 +53,7 @@ enum class PlayerType {
val newType = nameToPlayerType[enumName] val newType = nameToPlayerType[enumName]
if (newType == null) { if (newType == null) {
Logger.printException { "Unknown PlayerType encountered: $enumName" } Logger.printException { "Unknown PlayerType encountered: $enumName" }
} else if (current != newType) { } else {
Logger.printDebug { "PlayerType changed to: $newType" }
current = newType current = newType
} }
} }
@@ -68,9 +64,13 @@ enum class PlayerType {
@JvmStatic @JvmStatic
var current var current
get() = currentPlayerType get() = currentPlayerType
private set(value) { private set(type) {
currentPlayerType = value if (currentPlayerType != type) {
onChange(currentPlayerType) Logger.printDebug { "Changed to: $type" }
currentPlayerType = type
onChange(type)
}
} }
@Volatile // Read/write from different threads. @Volatile // Read/write from different threads.

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -2,10 +2,11 @@ package app.revanced.extension.youtube.sponsorblock.ui;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageButton; 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.youtube.sponsorblock.SponsorBlockUtils;
import app.revanced.extension.shared.Logger; 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.getResourceDimensionPixelSize;
import static app.revanced.extension.shared.Utils.getResourceIdentifier; import static app.revanced.extension.shared.Utils.getResourceIdentifier;
public final class NewSegmentLayout extends FrameLayout { public final class NewSegmentLayout extends FrameLayout {
private static final ColorStateList rippleColorStateList = new ColorStateList( private static final ColorStateList rippleColorStateList = new ColorStateList(
new int[][]{new int[]{android.R.attr.state_enabled}}, 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 defaultBottomMargin;
final int ctaBottomMargin; final int ctaBottomMargin;
@@ -47,10 +48,6 @@ public final class NewSegmentLayout extends FrameLayout {
getResourceIdentifier(context, "revanced_sb_new_segment", "layout"), this, true 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( initializeButton(
context, context,
"revanced_sb_new_segment_rewind", "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 @FunctionalInterface
private interface ButtonOnClickHandlerFunction { private interface ButtonOnClickHandlerFunction {
void apply(); void apply();

View File

@@ -8,6 +8,7 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -19,11 +20,19 @@ import androidx.annotation.NonNull;
import java.util.Objects; import java.util.Objects;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController; import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment; import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
public class SkipSponsorButton extends FrameLayout { 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 LinearLayout skipSponsorBtnContainer;
private final TextView skipSponsorTextView; private final TextView skipSponsorTextView;
private final Paint background; 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 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 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 = new Paint();
background.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color); background.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color);
background.setStyle(Paint.Style.FILL); background.setStyle(Paint.Style.FILL);
border = new Paint(); border = new Paint();
border.setColor(getResourceColor("skip_ad_button_border_color")); // color:skip_ad_button_border_color); 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.setStrokeWidth(getResourceDimension("ad_skip_ad_button_border_width")); // dimen:ad_skip_ad_button_border_width);
border.setStyle(Paint.Style.STROKE); 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 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 ctaBottomMargin = getResourceDimensionPixelSize("skip_button_cta_bottom_margin"); // dimen:skip_button_cta_bottom_margin
updateLayout();
skipSponsorBtnContainer.setOnClickListener(v -> { skipSponsorBtnContainer.setOnClickListener(v -> {
// The view controller handles hiding this button, but hide it here as well just in case something goofs. // The view controller handles hiding this button, but hide it here as well just in case something goofs.
setVisibility(View.GONE); setVisibility(View.GONE);
@@ -72,30 +86,56 @@ public class SkipSponsorButton extends FrameLayout {
protected final void dispatchDraw(Canvas canvas) { protected final void dispatchDraw(Canvas canvas) {
final int left = skipSponsorBtnContainer.getLeft(); final int left = skipSponsorBtnContainer.getLeft();
final int top = skipSponsorBtnContainer.getTop(); final int top = skipSponsorBtnContainer.getTop();
final int leftPlusWidth = (left + skipSponsorBtnContainer.getWidth()); final int right = left + skipSponsorBtnContainer.getWidth();
final int topPlusHeight = (top + skipSponsorBtnContainer.getHeight()); final int bottom = top + skipSponsorBtnContainer.getHeight();
canvas.drawRect(left, top, leftPlusWidth, topPlusHeight, background);
if (!highContrast) { // Determine corner radius for rounded button
canvas.drawLines(new float[]{ float cornerRadius = skipSponsorBtnContainer.getHeight() / 2f;
leftPlusWidth, top, left, top,
left, top, left, topPlusHeight, if (Settings.SB_SQUARE_LAYOUT.get()) {
left, topPlusHeight, leftPlusWidth, topPlusHeight}, // Square button.
border); 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); 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; this.segment = segment;
CharSequence newText = segment.getSkipButtonText(); CharSequence newText = segment.getSkipButtonText();
//noinspection StringEqualsCharSequence
if (newText.equals(skipSponsorTextView.getText())) { if (newText.equals(skipSponsorTextView.getText())) {
return false; return;
} }
skipSponsorTextView.setText(newText); skipSponsorTextView.setText(newText);
return true;
} }
} }

View File

@@ -19,8 +19,11 @@ import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType; import app.revanced.extension.youtube.shared.PlayerType;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment; import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
import kotlin.Unit;
public class SponsorBlockViewController { public class SponsorBlockViewController {
public static final int ROUNDED_LAYOUT_MARGIN = 12;
private static WeakReference<RelativeLayout> inlineSponsorOverlayRef = new WeakReference<>(null); private static WeakReference<RelativeLayout> inlineSponsorOverlayRef = new WeakReference<>(null);
private static WeakReference<ViewGroup> youtubeOverlaysLayoutRef = new WeakReference<>(null); private static WeakReference<ViewGroup> youtubeOverlaysLayoutRef = new WeakReference<>(null);
private static WeakReference<SkipSponsorButton> skipHighlightButtonRef = new WeakReference<>(null); private static WeakReference<SkipSponsorButton> skipHighlightButtonRef = new WeakReference<>(null);
@@ -36,7 +39,7 @@ public class SponsorBlockViewController {
static { static {
PlayerType.getOnChange().addObserver((PlayerType type) -> { PlayerType.getOnChange().addObserver((PlayerType type) -> {
playerTypeChanged(type); playerTypeChanged(type);
return null; return Unit.INSTANCE;
}); });
} }
@@ -80,12 +83,16 @@ public class SponsorBlockViewController {
}); });
youtubeOverlaysLayoutRef = new WeakReference<>(viewGroup); youtubeOverlaysLayoutRef = new WeakReference<>(viewGroup);
skipHighlightButtonRef = new WeakReference<>( skipHighlightButtonRef = new WeakReference<>(Objects.requireNonNull(
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_skip_highlight_button", "id")))); layout.findViewById(getResourceIdentifier("revanced_sb_skip_highlight_button", "id"))));
skipSponsorButtonRef = new WeakReference<>(
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_skip_sponsor_button", "id")))); skipSponsorButtonRef = new WeakReference<>(Objects.requireNonNull(
newSegmentLayoutRef = new WeakReference<>( layout.findViewById(getResourceIdentifier("revanced_sb_skip_sponsor_button", "id"))));
Objects.requireNonNull(layout.findViewById(getResourceIdentifier("revanced_sb_new_segment_view", "id"))));
NewSegmentLayout newSegmentLayout = Objects.requireNonNull(
layout.findViewById(getResourceIdentifier("revanced_sb_new_segment_view", "id")));
newSegmentLayoutRef = new WeakReference<>(newSegmentLayout);
newSegmentLayout.updateLayout();
newSegmentLayoutVisible = false; newSegmentLayoutVisible = false;
skipHighlight = null; skipHighlight = null;
@@ -101,6 +108,23 @@ public class SponsorBlockViewController {
hideNewSegmentLayout(); 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) { public static void showSkipHighlightButton(@NonNull SponsorSegment segment) {
skipHighlight = Objects.requireNonNull(segment); skipHighlight = Objects.requireNonNull(segment);
NewSegmentLayout newSegmentLayout = newSegmentLayoutRef.get(); 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 // but if buttons are showing when the end of the video is reached then they need
// to be forcefully hidden // to be forcefully hidden
if (!Settings.AUTO_REPEAT.get()) { if (!Settings.AUTO_REPEAT.get()) {
CreateSegmentButtonController.hide(); CreateSegmentButton.hideControls();
VotingButtonController.hide(); VotingButton.hideControls();
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "endOfVideoReached failure", ex); Logger.printException(() -> "endOfVideoReached failure", ex);

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -20,19 +20,17 @@ class SwipeControlsConfigurationProvider(
* should swipe controls be enabled? (global setting) * should swipe controls be enabled? (global setting)
*/ */
val enableSwipeControls: Boolean val enableSwipeControls: Boolean
get() = isFullscreenVideo && (enableVolumeControls || enableBrightnessControl) get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
/** /**
* should swipe controls for volume be enabled? * should swipe controls for volume be enabled?
*/ */
val enableVolumeControls: Boolean val enableVolumeControls = Settings.SWIPE_VOLUME.get()
get() = Settings.SWIPE_VOLUME.get()
/** /**
* should swipe controls for volume be enabled? * should swipe controls for volume be enabled?
*/ */
val enableBrightnessControl: Boolean val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
get() = Settings.SWIPE_BRIGHTNESS.get()
/** /**
* is the video player currently in fullscreen mode? * is the video player currently in fullscreen mode?
@@ -46,7 +44,7 @@ class SwipeControlsConfigurationProvider(
* should volume key controls be overwritten? (global setting) * should volume key controls be overwritten? (global setting)
*/ */
val overwriteVolumeKeyControls: Boolean val overwriteVolumeKeyControls: Boolean
get() = isFullscreenVideo && enableVolumeControls get() = enableVolumeControls && isFullscreenVideo
//endregion //endregion
//region gesture adjustments //region gesture adjustments
@@ -65,7 +63,6 @@ class SwipeControlsConfigurationProvider(
//endregion //endregion
//region overlay adjustments //region overlay adjustments
/** /**
* should the overlay enable haptic feedback? * should the overlay enable haptic feedback?
*/ */
@@ -79,15 +76,10 @@ class SwipeControlsConfigurationProvider(
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get() get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
/** /**
* text size for the overlay, in sp * Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency.
* If the opacity value is out of range, it resets to the default and displays a warning message.
*/ */
val overlayTextSize: Int val overlayBackgroundOpacity: Int
get() = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
/**
* get the background color for text on the overlay, as a color int
*/
val overlayTextBackgroundColor: Int
get() { get() {
var opacity = Settings.SWIPE_OVERLAY_OPACITY.get() var opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
@@ -102,11 +94,34 @@ class SwipeControlsConfigurationProvider(
} }
/** /**
* get the foreground color for text on the overlay, as a color int * The color of the progress overlay.
*/ */
val overlayForegroundColor: Int val overlayProgressColor: Int
get() = 0xBFFFFFFF.toInt()
/**
* The color used for the background of the progress overlay fill.
*/
val overlayFillBackgroundPaint: Int
get() = 0x80D3D3D3.toInt()
/**
* The color used for the text and icons in the overlay.
*/
val overlayTextColor: Int
get() = Color.WHITE get() = Color.WHITE
/**
* A flag that determines if the overlay should only show the icon.
*/
val overlayShowOverlayMinimalStyle: Boolean
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get()
/**
* A flag that determines if the progress bar should be circular.
*/
val isCircularProgressBar: Boolean
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get()
//endregion //endregion
//region behaviour //region behaviour

View File

@@ -82,11 +82,15 @@ abstract class BaseGestureController(
} }
override fun onScroll( override fun onScroll(
from: MotionEvent, from: MotionEvent?,
to: MotionEvent, to: MotionEvent,
distanceX: Float, distanceX: Float,
distanceY: Float, distanceY: Float,
): Boolean { ): Boolean {
if (from == null) {
return false
}
// submit to swipe detector // submit to swipe detector
submitForSwipe(from, to, distanceX, distanceY) submitForSwipe(from, to, distanceX, distanceY)

View File

@@ -1,138 +1,145 @@
package app.revanced.extension.youtube.swipecontrols.views package app.revanced.extension.youtube.swipecontrols.views
import android.content.Context import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.TypedValue import android.util.AttributeSet
import android.view.HapticFeedbackConstants import android.view.HapticFeedbackConstants
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.TextView
import app.revanced.extension.shared.StringRef.str
import app.revanced.extension.shared.Utils import app.revanced.extension.shared.Utils
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
import app.revanced.extension.youtube.swipecontrols.misc.applyDimension import kotlin.math.min
import kotlin.math.round import kotlin.math.round
/** /**
* main overlay layout for volume and brightness swipe controls * Main overlay layout for displaying volume and brightness level with both circular and rectangular progress bars.
*
* @param context context to create in
*/ */
class SwipeControlsOverlayLayout( class SwipeControlsOverlayLayout(
context: Context, context: Context,
private val config: SwipeControlsConfigurationProvider, private val config: SwipeControlsConfigurationProvider,
) : RelativeLayout(context), SwipeControlsOverlay { ) : RelativeLayout(context), SwipeControlsOverlay {
/**
* DO NOT use this, for tools only
*/
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context)) constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
private val feedbackTextView: TextView // Drawable icons for brightness and volume
private val autoBrightnessIcon: Drawable private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
private val manualBrightnessIcon: Drawable private val lowBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_low")
private val mutedVolumeIcon: Drawable private val mediumBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_medium")
private val normalVolumeIcon: Drawable private val highBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_high")
private val fullBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_full")
private val mutedVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_mute")
private val lowVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_low")
private val normalVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_normal")
private val fullVolumeIcon: Drawable = getDrawable("revanced_ic_sc_volume_high")
private fun getDrawable(name: String, width: Int, height: Int): Drawable { // Function to retrieve drawable resources by name
return resources.getDrawable( private fun getDrawable(name: String): Drawable {
val drawable = resources.getDrawable(
Utils.getResourceIdentifier(context, name, "drawable"), Utils.getResourceIdentifier(context, name, "drawable"),
context.theme, context.theme,
).apply { )
setTint(config.overlayForegroundColor) drawable.setTint(config.overlayTextColor)
setBounds( return drawable
0,
0,
width,
height,
)
}
} }
// Initialize progress bars
private val circularProgressView: CircularProgressView
private val horizontalProgressView: HorizontalProgressView
init { init {
// init views // Initialize circular progress bar
val feedbackTextViewPadding = 2.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP) circularProgressView = CircularProgressView(
val compoundIconPadding = 4.applyDimension(context, TypedValue.COMPLEX_UNIT_DIP) context,
feedbackTextView = TextView(context).apply { config.overlayBackgroundOpacity,
layoutParams = LayoutParams( config.overlayShowOverlayMinimalStyle,
LayoutParams.WRAP_CONTENT, config.overlayProgressColor,
LayoutParams.WRAP_CONTENT, config.overlayFillBackgroundPaint,
).apply { config.overlayTextColor
).apply {
layoutParams = LayoutParams(300, 300).apply {
addRule(CENTER_IN_PARENT, TRUE) addRule(CENTER_IN_PARENT, TRUE)
setPadding(
feedbackTextViewPadding,
feedbackTextViewPadding,
feedbackTextViewPadding,
feedbackTextViewPadding,
)
} }
background = GradientDrawable().apply { visibility = GONE // Initially hidden
cornerRadius = 8f
setColor(config.overlayTextBackgroundColor)
}
setTextColor(config.overlayForegroundColor)
setTextSize(TypedValue.COMPLEX_UNIT_SP, config.overlayTextSize.toFloat())
compoundDrawablePadding = compoundIconPadding
visibility = GONE
} }
addView(feedbackTextView) addView(circularProgressView)
// get icons scaled, assuming square icons // Initialize rectangular progress bar
val iconHeight = round(feedbackTextView.lineHeight * .8).toInt() val screenWidth = resources.displayMetrics.widthPixels
autoBrightnessIcon = getDrawable("revanced_ic_sc_brightness_auto", iconHeight, iconHeight) val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
manualBrightnessIcon = getDrawable("revanced_ic_sc_brightness_manual", iconHeight, iconHeight) horizontalProgressView = HorizontalProgressView(
mutedVolumeIcon = getDrawable("revanced_ic_sc_volume_mute", iconHeight, iconHeight) context,
normalVolumeIcon = getDrawable("revanced_ic_sc_volume_normal", iconHeight, iconHeight) config.overlayBackgroundOpacity,
config.overlayShowOverlayMinimalStyle,
config.overlayProgressColor,
config.overlayFillBackgroundPaint,
config.overlayTextColor
).apply {
layoutParams = LayoutParams(layoutWidth, 100).apply {
addRule(CENTER_HORIZONTAL)
topMargin = 40 // Top margin
}
visibility = GONE // Initially hidden
}
addView(horizontalProgressView)
} }
// Handler and callback for hiding progress bars
private val feedbackHideHandler = Handler(Looper.getMainLooper()) private val feedbackHideHandler = Handler(Looper.getMainLooper())
private val feedbackHideCallback = Runnable { private val feedbackHideCallback = Runnable {
feedbackTextView.visibility = GONE circularProgressView.visibility = GONE
horizontalProgressView.visibility = GONE
} }
/** /**
* show the feedback view for a given time * Displays the progress bar with the appropriate value, icon, and type (brightness or volume).
*
* @param message the message to show
* @param icon the icon to use
*/ */
private fun showFeedbackView(message: String, icon: Drawable) { private fun showFeedbackView(value: String, progress: Int, max: Int, icon: Drawable, isBrightness: Boolean) {
feedbackHideHandler.removeCallbacks(feedbackHideCallback) feedbackHideHandler.removeCallbacks(feedbackHideCallback)
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis) feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
feedbackTextView.apply {
text = message val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
setCompoundDrawablesRelative( viewToShow.apply {
icon, setProgress(progress, max, value, isBrightness)
null, this.icon = icon
null,
null,
)
visibility = VISIBLE visibility = VISIBLE
} }
} }
// Handle volume change
override fun onVolumeChanged(newVolume: Int, maximumVolume: Int) { override fun onVolumeChanged(newVolume: Int, maximumVolume: Int) {
showFeedbackView( val volumePercentage = (newVolume.toFloat() / maximumVolume) * 100
"$newVolume", val icon = when {
if (newVolume > 0) normalVolumeIcon else mutedVolumeIcon, newVolume == 0 -> mutedVolumeIcon
) volumePercentage < 33 -> lowVolumeIcon
volumePercentage < 66 -> normalVolumeIcon
else -> fullVolumeIcon
}
showFeedbackView("$newVolume", newVolume, maximumVolume, icon, isBrightness = false)
} }
// Handle brightness change
override fun onBrightnessChanged(brightness: Double) { override fun onBrightnessChanged(brightness: Double) {
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) { if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
showFeedbackView( showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text"), } else {
autoBrightnessIcon, val brightnessValue = round(brightness).toInt()
) val icon = when {
} else if (brightness >= 0) { brightnessValue < 25 -> lowBrightnessIcon
showFeedbackView("${round(brightness).toInt()}%", manualBrightnessIcon) brightnessValue < 50 -> mediumBrightnessIcon
brightnessValue < 75 -> highBrightnessIcon
else -> fullBrightnessIcon
}
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
} }
} }
// Begin swipe session
override fun onEnterSwipeSession() { override fun onEnterSwipeSession() {
if (config.shouldEnableHapticFeedback) { if (config.shouldEnableHapticFeedback) {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@@ -143,3 +150,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)
}
}

View File

@@ -1,38 +1,35 @@
package app.revanced.extension.youtube.videoplayer; package app.revanced.extension.youtube.videoplayer;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch; import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger; import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class CopyVideoUrlButton extends PlayerControlButton { public class CopyVideoUrlButton {
@Nullable @Nullable
private static CopyVideoUrlButton instance; private static PlayerControlButton instance;
public CopyVideoUrlButton(ViewGroup viewGroup) {
super(
viewGroup,
"revanced_copy_video_url_button",
Settings.COPY_VIDEO_URL,
view -> CopyVideoUrlPatch.copyUrl(false),
view -> {
CopyVideoUrlPatch.copyUrl(true);
return true;
}
);
}
/** /**
* Injection point. * Injection point.
*/ */
public static void initializeButton(View view) { public static void initializeButton(View controlsView) {
try { try {
instance = new CopyVideoUrlButton((ViewGroup) view); instance = new PlayerControlButton(
controlsView,
"revanced_copy_video_url_button",
"revanced_copy_video_url_button_placeholder",
Settings.COPY_VIDEO_URL::get,
view -> CopyVideoUrlPatch.copyUrl(false),
view -> {
CopyVideoUrlPatch.copyUrl(true);
return true;
}
);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex); Logger.printException(() -> "initializeButton failure", ex);
} }
@@ -41,14 +38,14 @@ public class CopyVideoUrlButton extends PlayerControlButton {
/** /**
* injection point * injection point
*/ */
public static void changeVisibilityImmediate(boolean visible) { public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible); if (instance != null) instance.setVisibilityImmediate(visible);
} }
/** /**
* injection point * injection point
*/ */
public static void changeVisibility(boolean visible, boolean animated) { public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated); if (instance != null) instance.setVisibility(visible, animated);
} }
} }

View File

@@ -1,38 +1,35 @@
package app.revanced.extension.youtube.videoplayer; package app.revanced.extension.youtube.videoplayer;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.CopyVideoUrlPatch; import app.revanced.extension.youtube.patches.CopyVideoUrlPatch;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger; import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class CopyVideoUrlTimestampButton extends PlayerControlButton { public class CopyVideoUrlTimestampButton {
@Nullable @Nullable
private static CopyVideoUrlTimestampButton instance; private static PlayerControlButton instance;
public CopyVideoUrlTimestampButton(ViewGroup bottomControlsViewGroup) {
super(
bottomControlsViewGroup,
"revanced_copy_video_url_timestamp_button",
Settings.COPY_VIDEO_URL_TIMESTAMP,
view -> CopyVideoUrlPatch.copyUrl(true),
view -> {
CopyVideoUrlPatch.copyUrl(false);
return true;
}
);
}
/** /**
* Injection point. * Injection point.
*/ */
public static void initializeButton(View bottomControlsViewGroup) { public static void initializeButton(View controlsView) {
try { try {
instance = new CopyVideoUrlTimestampButton((ViewGroup) bottomControlsViewGroup); instance = new PlayerControlButton(
controlsView,
"revanced_copy_video_url_timestamp_button",
"revanced_copy_video_url_timestamp_button_placeholder",
Settings.COPY_VIDEO_URL_TIMESTAMP::get,
view -> CopyVideoUrlPatch.copyUrl(true),
view -> {
CopyVideoUrlPatch.copyUrl(false);
return true;
}
);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex); Logger.printException(() -> "initializeButton failure", ex);
} }
@@ -41,14 +38,14 @@ public class CopyVideoUrlTimestampButton extends PlayerControlButton {
/** /**
* injection point * injection point
*/ */
public static void changeVisibilityImmediate(boolean visible) { public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible); if (instance != null) instance.setVisibilityImmediate(visible);
} }
/** /**
* injection point * injection point
*/ */
public static void changeVisibility(boolean visible, boolean animated) { public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated); if (instance != null) instance.setVisibility(visible, animated);
} }
} }

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.youtube.videoplayer; package app.revanced.extension.youtube.videoplayer;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -11,26 +10,23 @@ import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class ExternalDownloadButton extends PlayerControlButton { public class ExternalDownloadButton {
@Nullable @Nullable
private static ExternalDownloadButton instance; private static PlayerControlButton instance;
public ExternalDownloadButton(ViewGroup viewGroup) {
super(
viewGroup,
"revanced_external_download_button",
Settings.EXTERNAL_DOWNLOADER,
ExternalDownloadButton::onDownloadClick,
null
);
}
/** /**
* Injection point. * Injection point.
*/ */
public static void initializeButton(View view) { public static void initializeButton(View controlsView) {
try { try {
instance = new ExternalDownloadButton((ViewGroup) view); instance = new PlayerControlButton(
controlsView,
"revanced_external_download_button",
"revanced_external_download_button_placeholder",
Settings.EXTERNAL_DOWNLOADER::get,
ExternalDownloadButton::onDownloadClick,
null
);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex); Logger.printException(() -> "initializeButton failure", ex);
} }
@@ -39,14 +35,14 @@ public class ExternalDownloadButton extends PlayerControlButton {
/** /**
* injection point * injection point
*/ */
public static void changeVisibilityImmediate(boolean visible) { public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible); if (instance != null) instance.setVisibilityImmediate(visible);
} }
/** /**
* injection point * Injection point
*/ */
public static void changeVisibility(boolean visible, boolean animated) { public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated); if (instance != null) instance.setVisibility(visible, animated);
} }

View File

@@ -1,35 +1,31 @@
package app.revanced.extension.youtube.videoplayer; package app.revanced.extension.youtube.videoplayer;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch; import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.Settings; import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class PlaybackSpeedDialogButton extends PlayerControlButton { public class PlaybackSpeedDialogButton {
@Nullable @Nullable
private static PlaybackSpeedDialogButton instance; private static PlayerControlButton instance;
public PlaybackSpeedDialogButton(ViewGroup viewGroup) {
super(
viewGroup,
"revanced_playback_speed_dialog_button",
Settings.PLAYBACK_SPEED_DIALOG_BUTTON,
view -> CustomPlaybackSpeedPatch.showOldPlaybackSpeedMenu(),
null
);
}
/** /**
* Injection point. * Injection point.
*/ */
public static void initializeButton(View view) { public static void initializeButton(View controlsView) {
try { try {
instance = new PlaybackSpeedDialogButton((ViewGroup) view); instance = new PlayerControlButton(
controlsView,
"revanced_playback_speed_dialog_button",
"revanced_playback_speed_dialog_button_placeholder",
Settings.PLAYBACK_SPEED_DIALOG_BUTTON::get,
view -> CustomPlaybackSpeedPatch.showOldPlaybackSpeedMenu(),
null
);
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex); Logger.printException(() -> "initializeButton failure", ex);
} }
@@ -38,14 +34,14 @@ public class PlaybackSpeedDialogButton extends PlayerControlButton {
/** /**
* injection point * injection point
*/ */
public static void changeVisibilityImmediate(boolean visible) { public static void setVisibilityImmediate(boolean visible) {
if (instance != null) instance.setVisibilityImmediate(visible); if (instance != null) instance.setVisibilityImmediate(visible);
} }
/** /**
* injection point * injection point
*/ */
public static void changeVisibility(boolean visible, boolean animated) { public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) instance.setVisibility(visible, animated); if (instance != null) instance.setVisibility(visible, animated);
} }
} }

View File

@@ -1,83 +1,96 @@
package app.revanced.extension.youtube.videoplayer; package app.revanced.extension.youtube.videoplayer;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Objects;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BooleanSetting; import app.revanced.extension.youtube.shared.PlayerType;
import kotlin.Unit;
public abstract class PlayerControlButton { public class PlayerControlButton {
private static final Animation fadeIn; public interface PlayerControlButtonVisibility {
private static final Animation fadeOut; /**
* @return If the button should be shown when the player overlay is visible.
*/
boolean shouldBeShown();
}
private static final int fadeInDuration;
private static final int fadeOutDuration;
private static final Animation fadeInAnimation;
private static final Animation fadeOutAnimation;
private static final Animation fadeOutImmediate; private static final Animation fadeOutImmediate;
private final WeakReference<ImageView> buttonRef;
protected final BooleanSetting setting;
protected boolean isVisible;
static { static {
// TODO: check if these durations are correct. fadeInDuration = Utils.getResourceInteger("fade_duration_fast");
fadeIn = Utils.getResourceAnimation("fade_in"); fadeOutDuration = Utils.getResourceInteger("fade_duration_scheduled");
fadeIn.setDuration(Utils.getResourceInteger("fade_duration_fast"));
fadeOut = Utils.getResourceAnimation("fade_out"); fadeInAnimation = Utils.getResourceAnimation("fade_in");
fadeOut.setDuration(Utils.getResourceInteger("fade_duration_scheduled")); fadeInAnimation.setDuration(fadeInDuration);
fadeOutAnimation = Utils.getResourceAnimation("fade_out");
fadeOutAnimation.setDuration(fadeOutDuration);
// Animation for the fast fade out after tapping the overlay.
// Currently not used but should be.
fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out"); fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out");
fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast")); fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast"));
} }
@NonNull private final WeakReference<View> buttonRef;
public static Animation getButtonFadeIn() { /**
return fadeIn; * Empty view with the same layout size as the button. Used to fill empty space while the
} * fade out animation runs. Without this the chapter titles overlapping the button when fading out.
*/
private final WeakReference<View> placeHolderRef;
private final PlayerControlButtonVisibility visibilityCheck;
private boolean isVisible;
@NonNull public PlayerControlButton(View controlsViewGroup,
public static Animation getButtonFadeOut() { String imageViewButtonId,
return fadeOut; @Nullable String placeholderId,
} PlayerControlButtonVisibility buttonVisibility,
View.OnClickListener onClickListener,
@NonNull
public static Animation getButtonFadeOutImmediately() {
return fadeOutImmediate;
}
public PlayerControlButton(@NonNull ViewGroup bottomControlsViewGroup, @NonNull String imageViewButtonId,
@NonNull BooleanSetting booleanSetting, @NonNull View.OnClickListener onClickListener,
@Nullable View.OnLongClickListener longClickListener) { @Nullable View.OnLongClickListener longClickListener) {
Logger.printDebug(() -> "Initializing button: " + imageViewButtonId); ImageView imageView = Utils.getChildViewByResourceName(controlsViewGroup, imageViewButtonId);
ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById(
Utils.getResourceIdentifier(imageViewButtonId, "id")
));
imageView.setVisibility(View.GONE); imageView.setVisibility(View.GONE);
View tempPlaceholder = null;
if (placeholderId != null) {
tempPlaceholder = Utils.getChildViewByResourceName(controlsViewGroup, placeholderId);
tempPlaceholder.setVisibility(View.GONE);
}
placeHolderRef = new WeakReference<>(tempPlaceholder);
imageView.setOnClickListener(onClickListener); imageView.setOnClickListener(onClickListener);
if (longClickListener != null) { if (longClickListener != null) {
imageView.setOnLongClickListener(longClickListener); imageView.setOnLongClickListener(longClickListener);
} }
setting = booleanSetting; visibilityCheck = buttonVisibility;
buttonRef = new WeakReference<>(imageView); buttonRef = new WeakReference<>(imageView);
isVisible = false;
// Update the visibility after the player type changes.
// This ensures that button animations are cleared and their states are updated correctly
// when switching between states like minimized, maximized, or fullscreen, preventing
// "stuck" animations or incorrect visibility. Without this fix the issue is most noticable
// when maximizing type 3 miniplayer.
PlayerType.getOnChange().addObserver((PlayerType type) -> {
playerTypeChanged(type);
return Unit.INSTANCE;
});
} }
public void setVisibilityImmediate(boolean visible) { public void setVisibilityImmediate(boolean visible) {
if (visible) { private_setVisibility(visible, false);
// Fix button flickering, by pushing this call to the back of
// the main thread and letting other layout code run first.
Utils.runOnMainThread(() -> private_setVisibility(true, false));
} else {
private_setVisibility(false, false);
}
} }
public void setVisibility(boolean visible, boolean animated) { public void setVisibility(boolean visible, boolean animated) {
@@ -92,26 +105,80 @@ public abstract class PlayerControlButton {
if (isVisible == visible) return; if (isVisible == visible) return;
isVisible = visible; isVisible = visible;
ImageView iView = buttonRef.get(); View button = buttonRef.get();
if (iView == null) { if (button == null) return;
return;
}
if (visible && setting.get()) { View placeholder = placeHolderRef.get();
iView.clearAnimation(); final boolean shouldBeShown = visibilityCheck.shouldBeShown();
if (visible && shouldBeShown) {
button.clearAnimation();
if (animated) { if (animated) {
iView.startAnimation(PlayerControlButton.getButtonFadeIn()); button.startAnimation(PlayerControlButton.fadeInAnimation);
} }
iView.setVisibility(View.VISIBLE); button.setVisibility(View.VISIBLE);
} else if (iView.getVisibility() == View.VISIBLE) {
iView.clearAnimation(); if (placeholder != null) {
if (animated) { placeholder.setVisibility(View.GONE);
iView.startAnimation(PlayerControlButton.getButtonFadeOut()); }
} else {
if (button.getVisibility() == View.VISIBLE) {
button.clearAnimation();
if (animated) {
button.startAnimation(PlayerControlButton.fadeOutAnimation);
}
button.setVisibility(View.GONE);
}
if (placeholder != null) {
placeholder.setVisibility(shouldBeShown
? View.VISIBLE
: View.GONE);
} }
iView.setVisibility(View.GONE);
} }
} catch (Exception ex) { } catch (Exception ex) {
Logger.printException(() -> "setVisibility failure", ex); Logger.printException(() -> "private_setVisibility failure", ex);
} }
} }
}
/**
* Synchronizes the button state after the player state changes.
*/
private void playerTypeChanged(PlayerType newType) {
if (newType != PlayerType.WATCH_WHILE_MINIMIZED && !newType.isMaximizedOrFullscreen()) {
return;
}
View button = buttonRef.get();
if (button == null) return;
button.clearAnimation();
View placeholder = placeHolderRef.get();
if (visibilityCheck.shouldBeShown()) {
if (isVisible) {
button.setVisibility(View.VISIBLE);
if (placeholder != null) placeholder.setVisibility(View.GONE);
} else {
button.setVisibility(View.GONE);
if (placeholder != null) placeholder.setVisibility(View.VISIBLE);
}
} else {
button.setVisibility(View.GONE);
if (placeholder != null) placeholder.setVisibility(View.GONE);
}
}
public void hide() {
if (!isVisible) return;
Utils.verifyOnMainThread();
View view = buttonRef.get();
if (view == null) return;
view.setVisibility(View.GONE);
view = placeHolderRef.get();
if (view != null) view.setVisibility(View.GONE);
isVisible = false;
}
}

View File

@@ -4,14 +4,9 @@ plugins {
android { android {
namespace = "app.revanced.extension" namespace = "app.revanced.extension"
compileSdk = 33 compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 24 minSdk = 24
} }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
} }

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true org.gradle.parallel = true
android.useAndroidX = true android.useAndroidX = true
kotlin.code.style = official kotlin.code.style = official
version = 5.10.1-dev.3 version = 5.13.0-dev.10

View File

@@ -3,7 +3,6 @@ revanced-patcher = "21.0.0"
# Tracking https://github.com/google/smali/issues/64. # Tracking https://github.com/google/smali/issues/64.
#noinspection GradleDependency #noinspection GradleDependency
smali = "3.0.5" smali = "3.0.5"
gson = "2.11.0"
# 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818. # 8.3.0 causes java verifier error: https://github.com/ReVanced/revanced-patches/issues/2818.
#noinspection GradleDependency #noinspection GradleDependency
agp = "8.2.2" agp = "8.2.2"
@@ -11,10 +10,9 @@ annotation = "1.9.1"
appcompat = "1.7.0" appcompat = "1.7.0"
okhttp = "5.0.0-alpha.14" okhttp = "5.0.0-alpha.14"
retrofit = "2.11.0" retrofit = "2.11.0"
guava = "33.2.1-jre" guava = "33.4.0-jre"
[libraries] [libraries]
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" } annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }

24
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.10.1", "gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^24.1.2" "semantic-release": "^24.2.1"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@@ -6760,9 +6760,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/semantic-release": { "node_modules/semantic-release": {
"version": "24.1.2", "version": "24.2.1",
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.1.2.tgz", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.1.tgz",
"integrity": "sha512-hvEJ7yI97pzJuLsDZCYzJgmRxF8kiEJvNZhf0oiZQcexw+Ycjy4wbdsn/sVMURgNCu8rwbAXJdBRyIxM4pe32g==", "integrity": "sha512-z0/3cutKNkLQ4Oy0HTi3lubnjTsdjjgOqmxdPjeYWe6lhFqUPfwslZxRHv3HDZlN4MhnZitb9SLihDkZNxOXfQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -6782,7 +6782,7 @@
"git-log-parser": "^1.2.0", "git-log-parser": "^1.2.0",
"hook-std": "^3.0.0", "hook-std": "^3.0.0",
"hosted-git-info": "^8.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", "lodash-es": "^4.17.21",
"marked": "^12.0.0", "marked": "^12.0.0",
"marked-terminal": "^7.0.0", "marked-terminal": "^7.0.0",
@@ -6926,6 +6926,20 @@
"node": ">=18.18.0" "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": { "node_modules/semantic-release/node_modules/indent-string": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",

View File

@@ -4,6 +4,6 @@
"@semantic-release/changelog": "^6.0.3", "@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1", "@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.10.1", "gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^24.1.2" "semantic-release": "^24.2.1"
} }
} }

View File

@@ -13,8 +13,6 @@ patches {
} }
dependencies { dependencies {
// Used by JsonGenerator.
implementation(libs.gson)
// Required due to smali, or build fails. Can be removed once smali is bumped. // Required due to smali, or build fails. Can be removed once smali is bumped.
implementation(libs.guava) implementation(libs.guava)
// Android API stubs defined here. // Android API stubs defined here.

View File

@@ -14,7 +14,7 @@ val exportInternalDataDocumentsProviderPatch = resourcePatch(
) { ) {
dependsOn( dependsOn(
bytecodePatch { bytecodePatch {
extendWith("extensions/all/misc/directory/export-internal-data-documents-provider.rve") extendWith("extensions/all/misc/directory/documentsprovider/export-internal-data-documents-provider.rve")
}, },
) )

View File

@@ -25,7 +25,7 @@ private val removeCaptureRestrictionResourcePatch = resourcePatch(
} }
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX = 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;" private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
@Suppress("unused") @Suppress("unused")

View File

@@ -8,7 +8,12 @@ val hideVideoAdsPatch = bytecodePatch(
name = "Hide music video ads", name = "Hide music video ads",
description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.", 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 { execute {
navigate(showVideoAdsParentFingerprint.originalMethod) navigate(showVideoAdsParentFingerprint.originalMethod)

View File

@@ -1,24 +1,21 @@
package app.revanced.patches.music.audio.exclusiveaudio package app.revanced.patches.music.audio.exclusiveaudio
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused") @Suppress("unused")
val enableExclusiveAudioPlaybackPatch = bytecodePatch( val enableExclusiveAudioPlaybackPatch = bytecodePatch(
name = "Enable exclusive audio playback", name = "Enable exclusive audio playback",
description = "Enables the option to play audio without video.", 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 { execute {
allowExclusiveAudioPlaybackFingerprint.method.apply { allowExclusiveAudioPlaybackFingerprint.method.returnEarly(true)
addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
)
}
} }
} }

View File

@@ -9,16 +9,15 @@ internal val allowExclusiveAudioPlaybackFingerprint = fingerprint {
returns("Z") returns("Z")
parameters() parameters()
opcodes( opcodes(
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT, Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST, Opcode.CHECK_CAST,
Opcode.IF_NEZ, Opcode.IF_NEZ,
Opcode.IGET_OBJECT, Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT, Opcode.MOVE_RESULT
Opcode.GOTO,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.RETURN
) )
} }

View File

@@ -11,10 +11,14 @@ val permanentRepeatPatch = bytecodePatch(
description = "Permanently remember your repeating preference even if the playlist ends or another track is played.", description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
use = false, use = false,
) { ) {
compatibleWith("com.google.android.apps.youtube.music") compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
execute { execute {
val startIndex = repeatTrackFingerprint.patternMatch!!.endIndex val startIndex = repeatTrackFingerprint.patternMatch!!.endIndex
val repeatIndex = startIndex + 1 val repeatIndex = startIndex + 1

View File

@@ -11,7 +11,12 @@ val hideCategoryBar = bytecodePatch(
description = "Hides the category bar at the top of the homepage.", description = "Hides the category bar at the top of the homepage.",
use = false, use = false,
) { ) {
compatibleWith("com.google.android.apps.youtube.music") compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
execute { execute {
constructCategoryBarFingerprint.method.apply { constructCategoryBarFingerprint.method.apply {

View File

@@ -11,7 +11,12 @@ val hideGetPremiumPatch = bytecodePatch(
name = "Hide 'Get Music Premium' label", name = "Hide 'Get Music Premium' label",
description = "Hides the \"Get Music Premium\" label from the account menu and settings.", 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 { execute {
hideGetPremiumFingerprint.method.apply { hideGetPremiumFingerprint.method.apply {

View File

@@ -18,7 +18,12 @@ val removeUpgradeButtonPatch = bytecodePatch(
name = "Remove upgrade button", name = "Remove upgrade button",
description = "Removes the upgrade tab from the pivot bar.", 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 { execute {
pivotBarConstructorFingerprint.method.apply { pivotBarConstructorFingerprint.method.apply {

View File

@@ -8,7 +8,12 @@ val bypassCertificateChecksPatch = bytecodePatch(
name = "Bypass certificate checks", name = "Bypass certificate checks",
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.", 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 { execute {
checkCertificateFingerprint.method.returnEarly(true) checkCertificateFingerprint.method.returnEarly(true)

View File

@@ -4,8 +4,10 @@ import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
internal val checkCertificateFingerprint = fingerprint { internal val checkCertificateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z") returns("Z")
parameters("Ljava/lang/String;") parameters("Ljava/lang/String;")
strings("X509", "Failed to get certificate.") strings(
"X509",
"Failed to get certificate" // Partial String match.
)
} }

View File

@@ -8,7 +8,12 @@ val backgroundPlaybackPatch = bytecodePatch(
name = "Remove background playback restrictions", name = "Remove background playback restrictions",
description = "Removes restrictions on background playback, including playing kids videos in the background.", 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 { execute {
kidsBackgroundPlaybackPolicyControllerFingerprint.method.addInstruction( kidsBackgroundPlaybackPolicyControllerFingerprint.method.addInstruction(

View File

@@ -25,7 +25,12 @@ val spoofClientPatch = bytecodePatch(
name = "Spoof client", name = "Spoof client",
description = "Spoofs the client to fix playback.", 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( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,

View File

@@ -56,8 +56,9 @@ val customThemePatch = resourcePatch(
document("res/values/colors.xml").use { document -> document("res/values/colors.xml").use { document ->
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
for (i in 0 until resourcesNode.childNodes.length) { val childNodes = resourcesNode.childNodes
val node = resourcesNode.childNodes.item(i) as? Element ?: continue for (i in 0 until childNodes.length) {
val node = childNodes.item(i) as? Element ?: continue
node.textContent = node.textContent =
when (node.getAttribute("name")) { when (node.getAttribute("name")) {

View File

@@ -2,4 +2,4 @@ package app.revanced.patches.tiktok.misc.extension
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch("tiktok", initHook) val sharedExtensionPatch = sharedExtensionPatch("tiktok", initHook, jatoInitHook, storeRegionInitHook)

View File

@@ -3,12 +3,35 @@ package app.revanced.patches.tiktok.misc.extension
import app.revanced.patches.shared.misc.extension.extensionHook import app.revanced.patches.shared.misc.extension.extensionHook
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
internal val initHook = extensionHook( internal val initHook = extensionHook {
insertIndexResolver = { 1 }, // Insert after call to super class.
) {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
custom { method, classDef -> custom { method, classDef ->
classDef.endsWith("/AwemeHostApplication;") && classDef.type == "Lcom/ss/android/ugc/aweme/main/MainActivity;" &&
method.name == "<init>" method.name == "onCreate"
}
}
/**
* In some cases the extension code can be called before
* the app main activity onCreate is called.
*
* This class is called from startup code titled "BPEA RunnableGuardLancet".
*/
internal val jatoInitHook = extensionHook(
contextRegisterResolver = { "p1" }
) {
parameters("Landroid/content/Context;")
custom { method, classDef ->
classDef.type == "Lcom/ss/android/ugc/aweme/legoImp/task/JatoInitTask;" &&
method.name == "run"
}
}
internal val storeRegionInitHook = extensionHook(
contextRegisterResolver = { "p1" }
) {
parameters("Landroid/content/Context;")
custom { method, classDef ->
classDef.type == "Lcom/ss/android/ugc/aweme/legoImp/task/StoreRegionInitTask;" &&
method.name == "run"
} }
} }

View File

@@ -3,9 +3,9 @@ package app.revanced.patches.windyapp.misc.unlockpro
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
@Deprecated("This patch no longer works and will be removed in the future.")
@Suppress("unused") @Suppress("unused")
val unlockProPatch = bytecodePatch( val unlockProPatch = bytecodePatch(
name = "Unlock pro",
description = "Unlocks all pro features.", description = "Unlocks all pro features.",
) { ) {
compatibleWith("co.windyapp.android") compatibleWith("co.windyapp.android")

View File

@@ -78,8 +78,6 @@ val hideAdsPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -25,8 +25,6 @@ val hideGetPremiumPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -23,8 +23,6 @@ val videoAdsPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -53,8 +53,6 @@ val copyVideoUrlPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -24,8 +24,6 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -68,8 +68,6 @@ val downloadsPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -23,8 +23,6 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -26,8 +26,6 @@ val enableSeekbarTappingPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
// 18.38.44 patches but crashes on startup.
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -35,7 +35,6 @@ val enableSlideToSeekPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -19,7 +19,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
val seekbarThumbnailsPatch = bytecodePatch( val seekbarThumbnailsPatch = bytecodePatch(
name = "Seekbar thumbnails", name = "Seekbar thumbnails",
description = "Adds an option to use high quality fullscreen seekbar thumbnails. " + description = "Adds an option to use high quality fullscreen seekbar thumbnails. " +
"Patching 19.16.39 or lower adds an option to restore old seekbar thumbnails.", "Patching 19.16.39 adds an option to restore old seekbar thumbnails.",
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
@@ -29,8 +29,6 @@ val seekbarThumbnailsPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -42,9 +42,10 @@ private val swipeControlsResourcePatch = resourcePatch {
SwitchPreference("revanced_swipe_haptic_feedback"), SwitchPreference("revanced_swipe_haptic_feedback"),
SwitchPreference("revanced_swipe_save_and_restore_brightness"), SwitchPreference("revanced_swipe_save_and_restore_brightness"),
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"), SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER), SwitchPreference("revanced_swipe_show_circular_overlay"),
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER), SwitchPreference("revanced_swipe_overlay_minimal_style"),
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER), TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER), TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
) )
@@ -53,7 +54,12 @@ private val swipeControlsResourcePatch = resourcePatch {
ResourceGroup( ResourceGroup(
"drawable", "drawable",
"revanced_ic_sc_brightness_auto.xml", "revanced_ic_sc_brightness_auto.xml",
"revanced_ic_sc_brightness_manual.xml", "revanced_ic_sc_brightness_full.xml",
"revanced_ic_sc_brightness_high.xml",
"revanced_ic_sc_brightness_low.xml",
"revanced_ic_sc_brightness_medium.xml",
"revanced_ic_sc_volume_high.xml",
"revanced_ic_sc_volume_low.xml",
"revanced_ic_sc_volume_mute.xml", "revanced_ic_sc_volume_mute.xml",
"revanced_ic_sc_volume_normal.xml", "revanced_ic_sc_volume_normal.xml",
), ),
@@ -74,8 +80,6 @@ val swipeControlsPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

View File

@@ -22,8 +22,6 @@ val autoCaptionsPatch = bytecodePatch(
compatibleWith( compatibleWith(
"com.google.android.youtube"( "com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39", "19.16.39",
"19.25.37", "19.25.37",
"19.34.42", "19.34.42",

Some files were not shown because too many files have changed in this diff Show More