Compare commits

...

224 Commits

Author SHA1 Message Date
oSumAtrIX
32dfe48ad7 finish batch1 2026-01-11 03:09:35 +01:00
oSumAtrIX
a103eb5b7a migrate batch 2026-01-09 20:00:49 +01:00
oSumAtrIX
69a71fbd3a begin migration 2026-01-08 11:56:03 +01:00
oSumAtrIX
b6cc108fba Merge branch 'dev' into feat/modernize-api
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/strava/password/EnablePasswordLoginPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/strava/subscription/UnlockSubscriptionPatch.kt
#	patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt
2026-01-08 10:28:06 +01:00
semantic-release-bot
f4af27dfec chore: Release v5.48.0-dev.9 [skip ci]
# [5.48.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.8...v5.48.0-dev.9) (2026-01-08)

### Features

* Add `Disable Sentry telemetry` patch ([#6416](https://github.com/ReVanced/revanced-patches/issues/6416)) ([4cc3159](4cc315952d))
* Disable Play Integrity patch ([#6412](https://github.com/ReVanced/revanced-patches/issues/6412)) ([6312fe8](6312fe8d60))
2026-01-08 00:11:57 +00:00
xehpuk
4cc315952d feat: Add Disable Sentry telemetry patch (#6416) 2026-01-08 01:08:52 +01:00
1fexd
6312fe8d60 feat: Disable Play Integrity patch (#6412)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2026-01-08 01:06:05 +01:00
Ushie
3d754575a4 ci: Simplify Crowdin translation file destination path (#6463) 2026-01-07 22:31:15 +01:00
oSumAtrIX
cceb7a7c43 update to new options API 2026-01-06 16:24:54 +01:00
semantic-release-bot
a3f7609fe3 chore: Release v5.48.0-dev.8 [skip ci]
# [5.48.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.7...v5.48.0-dev.8) (2026-01-04)

### Features

* **Letterboxd:** Add `Unlock app icons` patch ([#6415](https://github.com/ReVanced/revanced-patches/issues/6415)) ([d25dcfe](d25dcfe49a))
2026-01-04 13:17:54 +00:00
Swakshan
d25dcfe49a feat(Letterboxd): Add Unlock app icons patch (#6415) 2026-01-04 14:14:29 +01:00
semantic-release-bot
1cc2cb9cb2 chore: Release v5.48.0-dev.7 [skip ci]
# [5.48.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.6...v5.48.0-dev.7) (2026-01-04)

### Features

* **Strava:** Add `Disable Quick Edit` patch ([#6452](https://github.com/ReVanced/revanced-patches/issues/6452)) ([f5cbb31](f5cbb31724))
* **Strava:** Add `Overwrite media upload parameters` patch ([#6410](https://github.com/ReVanced/revanced-patches/issues/6410)) ([b42ae27](b42ae27ce6))
2026-01-04 02:43:29 +00:00
xehpuk
f5cbb31724 feat(Strava): Add Disable Quick Edit patch (#6452)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2026-01-04 03:38:04 +01:00
xehpuk
b42ae27ce6 feat(Strava): Add Overwrite media upload parameters patch (#6410) 2026-01-04 03:36:08 +01:00
semantic-release-bot
43ab29d03d chore: Release v5.48.0-dev.6 [skip ci]
# [5.48.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.5...v5.48.0-dev.6) (2026-01-04)

### Bug Fixes

* Fix build error introduced in `4046bee` ([#6417](https://github.com/ReVanced/revanced-patches/issues/6417)) ([789f0a5](789f0a5628))
2026-01-04 02:07:07 +00:00
xehpuk
789f0a5628 fix: Fix build error introduced in 4046bee (#6417) 2026-01-04 03:03:44 +01:00
semantic-release-bot
da836b667c chore: Release v5.48.0-dev.5 [skip ci]
# [5.48.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.4...v5.48.0-dev.5) (2025-12-30)

### Bug Fixes

* **Disney+ - Skip ads:** Remove unsupported package names ([#6422](https://github.com/ReVanced/revanced-patches/issues/6422)) ([44e7dbc](44e7dbcf4d))
2025-12-30 18:37:28 +00:00
ILoveOpenSourceApplications
44e7dbcf4d fix(Disney+ - Skip ads): Remove unsupported package names (#6422) 2025-12-30 19:32:17 +01:00
oSumAtrIX
30076b7a9b update to new apply, afterDependents and Patch api 2025-12-30 01:22:15 +01:00
oSumAtrIX
bbd8ae0e24 merge dev 2025-12-30 01:14:20 +01:00
oSumAtrIX
641a23b35a temp 2025-12-30 00:58:32 +01:00
semantic-release-bot
195c239000 chore: Release v5.48.0-dev.4 [skip ci]
# [5.48.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.3...v5.48.0-dev.4) (2025-12-29)

### Features

* **Strava:** Add `Block Snowplow tracking` patch ([#6413](https://github.com/ReVanced/revanced-patches/issues/6413)) ([c47beae](c47beae213))
2025-12-29 21:25:35 +00:00
xehpuk
c47beae213 feat(Strava): Add Block Snowplow tracking patch (#6413)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-29 22:20:26 +01:00
semantic-release-bot
cebcfab86a chore: Release v5.48.0-dev.3 [skip ci]
# [5.48.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.2...v5.48.0-dev.3) (2025-12-28)

### Bug Fixes

* Fix compilation error introduced in `6bb6281` ([#6409](https://github.com/ReVanced/revanced-patches/issues/6409)) ([71c6cb5](71c6cb569e))

### Features

* **Instagram - Hides navigation buttons:** Add more buttons to hide ([#6390](https://github.com/ReVanced/revanced-patches/issues/6390)) ([6bb6281](6bb6281149))
2025-12-28 22:33:37 +00:00
xehpuk
71c6cb569e fix: Fix compilation error introduced in 6bb6281 (#6409) 2025-12-28 23:30:32 +01:00
PainfulPaladins
6bb6281149 feat(Instagram - Hides navigation buttons): Add more buttons to hide (#6390) 2025-12-27 18:50:08 +01:00
semantic-release-bot
16bd96e2bb chore: Release v5.48.0-dev.2 [skip ci]
# [5.48.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.1...v5.48.0-dev.2) (2025-12-27)

### Features

* **Strava:** Add `Enable password login` patch ([#6396](https://github.com/ReVanced/revanced-patches/issues/6396)) ([8f3f4c9](8f3f4c95bb))
2025-12-27 17:48:32 +00:00
xehpuk
8f3f4c95bb feat(Strava): Add Enable password login patch (#6396)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-27 18:45:09 +01:00
semantic-release-bot
da02d68587 chore: Release v5.48.0-dev.1 [skip ci]
# [5.48.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.47.0...v5.48.0-dev.1) (2025-12-23)

### Bug Fixes

* Fix compilation error introduced in dc69f243 ([#6392](https://github.com/ReVanced/revanced-patches/issues/6392)) ([a429824](a429824bb7))
* **YouTube - Hide layout components:** Hide new type of crowdfunding box ([#6380](https://github.com/ReVanced/revanced-patches/issues/6380)) ([dc69f24](dc69f2433e))

### Features

* **ProtonVPN:** Add `Unlock split tunneling` patch ([#6353](https://github.com/ReVanced/revanced-patches/issues/6353)) ([e0f3346](e0f33468e6))
* **SBS On Demand:** Add `Remove ads` patch ([#6378](https://github.com/ReVanced/revanced-patches/issues/6378)) ([315931c](315931cbf8))
2025-12-23 01:30:13 +00:00
xehpuk
a429824bb7 fix: Fix compilation error introduced in dc69f243 (#6392) 2025-12-23 02:26:54 +01:00
Sylvain Finot
e0f33468e6 feat(ProtonVPN): Add Unlock split tunneling patch (#6353)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-22 15:00:08 +01:00
trespyian
315931cbf8 feat(SBS On Demand): Add Remove ads patch (#6378)
Co-authored-by: Trespyian <trespyian@nowhere.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-22 14:49:19 +01:00
ILoveOpenSourceApplications
dc69f2433e fix(YouTube - Hide layout components): Hide new type of crowdfunding box (#6380) 2025-12-21 23:10:35 +01:00
semantic-release-bot
73e43b2a49 chore: Release v5.47.0 [skip ci]
# [5.47.0](https://github.com/ReVanced/revanced-patches/compare/v5.46.0...v5.47.0) (2025-12-18)

### Bug Fixes

* **Instagram - Disable signature check:** Change patch to default excluded ([#6283](https://github.com/ReVanced/revanced-patches/issues/6283)) ([bb745b5](bb745b555b))
* **Lightroom:** Add `Disable version check` patch to fix opening the app  ([#6315](https://github.com/ReVanced/revanced-patches/issues/6315)) ([018d176](018d176914))
* **Reddit - Hide ads:** Update patch for new versions of Reddit ([#6342](https://github.com/ReVanced/revanced-patches/issues/6342)) ([f8bd123](f8bd1239cc))
* **Spotify:** Make patches work with latest versions again ([#6359](https://github.com/ReVanced/revanced-patches/issues/6359)) ([34830ba](34830ba63b))
* **YouTube - Hide layout components:** Fix "Hide Subscribe button" in channel page not working ([#6363](https://github.com/ReVanced/revanced-patches/issues/6363)) ([ded8370](ded8370207))
* **YouTube - Hide player flyout menu items:** Allow hiding audio menu with 'Android No SDK' client type ([9495cf4](9495cf49ef))
* **YouTube - Sanitize sharing links:** Handle non hierarchical urls ([654d091](654d091e65))

### Features

* **Disney+ - SkipAds:** Add other package names the patch is compatible with ([#6372](https://github.com/ReVanced/revanced-patches/issues/6372)) ([1f4f252](1f4f252c81))
* **Disney+:** Add `Skip ads` patch ([#6343](https://github.com/ReVanced/revanced-patches/issues/6343)) ([6bd7dca](6bd7dca75b))
* **IdAustria - Remove device integrity check:** Update patch to work with latest version ([#6360](https://github.com/ReVanced/revanced-patches/issues/6360)) ([0ea3491](0ea3491227))
* **Instagram:** Add `Anonymous story viewing` patch ([#6263](https://github.com/ReVanced/revanced-patches/issues/6263)) ([94ae84a](94ae84ad0f))
* **Instagram:** Add `Disable auto story flipping` patch ([#6262](https://github.com/ReVanced/revanced-patches/issues/6262)) ([2f0de15](2f0de15e67))
* **Instagram:** Add `Disable Reels scrolling` patch ([#6317](https://github.com/ReVanced/revanced-patches/issues/6317)) ([0928dcd](0928dcd00d))
* **Letterboxd:** Add `Hide ads` patch ([#6309](https://github.com/ReVanced/revanced-patches/issues/6309)) ([0af0ee9](0af0ee92c4))
* **Peacock TV:** Add `Hide ads` patch ([#6348](https://github.com/ReVanced/revanced-patches/issues/6348)) ([847ee18](847ee189a9))
* **ProtonVPN:** Add `Remove delay` patch ([#6326](https://github.com/ReVanced/revanced-patches/issues/6326)) ([bbd8932](bbd8932b2e))
* **Spoof SIM provider:** Spoof additional TelephonyManager methods ([#6293](https://github.com/ReVanced/revanced-patches/issues/6293)) ([ac583d4](ac583d40d0))
* **YouTube - Hide layout components:** Add "Hide cell divider", "Hide featured links", and "Hide featured videos" options ([#6335](https://github.com/ReVanced/revanced-patches/issues/6335)) ([a5d197b](a5d197b977))
* **YouTube - Hide layout components:** Add "Hide Join button" and "Hide Subscribe button" options for channel page ([#6345](https://github.com/ReVanced/revanced-patches/issues/6345)) ([02831a6](02831a6069))
* **YouTube - Hide Shorts components:** Add "Hide auto-dubbed label" and "Hide live preview" options ([#6334](https://github.com/ReVanced/revanced-patches/issues/6334)) ([a7c220a](a7c220a4ae))
2025-12-18 12:14:21 +00:00
oSumAtrIX
918f04793f chore: Merge branch dev to main (#6282) 2025-12-18 13:10:41 +01:00
semantic-release-bot
f1a9537f01 chore: Release v5.47.0-dev.18 [skip ci]
# [5.47.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.17...v5.47.0-dev.18) (2025-12-18)

### Features

* **Disney+ - SkipAds:** Add other package names the patch is compatible with ([#6372](https://github.com/ReVanced/revanced-patches/issues/6372)) ([1f4f252](1f4f252c81))
2025-12-18 12:09:57 +00:00
vippium
1f4f252c81 feat(Disney+ - SkipAds): Add other package names the patch is compatible with (#6372) 2025-12-18 12:59:47 +01:00
semantic-release-bot
2b560f5fe9 chore: Release v5.47.0-dev.17 [skip ci]
# [5.47.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.16...v5.47.0-dev.17) (2025-12-18)

### Bug Fixes

* **Reddit - Hide ads:** Update patch for new versions of Reddit ([#6342](https://github.com/ReVanced/revanced-patches/issues/6342)) ([f8bd123](f8bd1239cc))
2025-12-18 02:05:14 +00:00
g9q
f8bd1239cc fix(Reddit - Hide ads): Update patch for new versions of Reddit (#6342)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-18 03:01:25 +01:00
semantic-release-bot
c825ebda37 chore: Release v5.47.0-dev.16 [skip ci]
# [5.47.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.15...v5.47.0-dev.16) (2025-12-15)

### Bug Fixes

* **Lightroom:** Add `Disable version check` patch to fix opening the app  ([#6315](https://github.com/ReVanced/revanced-patches/issues/6315)) ([018d176](018d176914))

### Features

* **IdAustria - Remove device integrity check:** Update patch to work with latest version ([#6360](https://github.com/ReVanced/revanced-patches/issues/6360)) ([0ea3491](0ea3491227))
2025-12-15 11:34:05 +00:00
oSumAtrIX
255c00b183 chore: Fix minor syntax error 2025-12-15 12:28:53 +01:00
Alex Katlein
0ea3491227 feat(IdAustria - Remove device integrity check): Update patch to work with latest version (#6360)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-15 12:26:59 +01:00
Pun Butrach
5d437b08dd docs: Use American spelling (#6233) 2025-12-14 16:38:55 +01:00
f1re4xx
018d176914 fix(Lightroom): Add Disable version check patch to fix opening the app (#6315)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-14 16:38:26 +01:00
semantic-release-bot
9a77beea8a chore: Release v5.47.0-dev.15 [skip ci]
# [5.47.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.14...v5.47.0-dev.15) (2025-12-13)

### Bug Fixes

* **YouTube - Hide layout components:** Fix "Hide Subscribe button" in channel page not working ([#6363](https://github.com/ReVanced/revanced-patches/issues/6363)) ([ded8370](ded8370207))
2025-12-13 20:26:05 +00:00
ILoveOpenSourceApplications
ded8370207 fix(YouTube - Hide layout components): Fix "Hide Subscribe button" in channel page not working (#6363) 2025-12-13 21:22:35 +01:00
semantic-release-bot
4d1104fc32 chore: Release v5.47.0-dev.14 [skip ci]
# [5.47.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.13...v5.47.0-dev.14) (2025-12-13)

### Bug Fixes

* **Spotify:** Make patches work with latest versions again ([#6359](https://github.com/ReVanced/revanced-patches/issues/6359)) ([34830ba](34830ba63b))
2025-12-13 08:52:03 +00:00
Cilly Leang
34830ba63b fix(Spotify): Make patches work with latest versions again (#6359) 2025-12-13 09:48:39 +01:00
github-actions[bot]
7a6894d809 chore: Sync translations (#6344)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-12-10 03:58:45 +01:00
semantic-release-bot
144e6e2694 chore: Release v5.47.0-dev.13 [skip ci]
# [5.47.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.12...v5.47.0-dev.13) (2025-12-10)

### Features

* **Peacock TV:** Add `Hide ads` patch ([#6348](https://github.com/ReVanced/revanced-patches/issues/6348)) ([847ee18](847ee189a9))
2025-12-10 02:58:06 +00:00
g9q
847ee189a9 feat(Peacock TV): Add Hide ads patch (#6348) 2025-12-10 03:55:08 +01:00
semantic-release-bot
dc813fe617 chore: Release v5.47.0-dev.12 [skip ci]
# [5.47.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.11...v5.47.0-dev.12) (2025-12-08)

### Features

* **YouTube - Hide layout components:** Add "Hide Join button" and "Hide Subscribe button" options for channel page ([#6345](https://github.com/ReVanced/revanced-patches/issues/6345)) ([02831a6](02831a6069))
2025-12-08 21:14:39 +00:00
ILoveOpenSourceApplications
02831a6069 feat(YouTube - Hide layout components): Add "Hide Join button" and "Hide Subscribe button" options for channel page (#6345) 2025-12-08 22:10:35 +01:00
semantic-release-bot
5228fd4b58 chore: Release v5.47.0-dev.11 [skip ci]
# [5.47.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.10...v5.47.0-dev.11) (2025-12-08)

### Features

* **Disney+:** Add `Skip ads` patch ([#6343](https://github.com/ReVanced/revanced-patches/issues/6343)) ([6bd7dca](6bd7dca75b))
2025-12-08 13:51:15 +00:00
g9q
6bd7dca75b feat(Disney+): Add Skip ads patch (#6343)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-08 14:45:41 +01:00
semantic-release-bot
22ed7bfbb3 chore: Release v5.47.0-dev.10 [skip ci]
# [5.47.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.9...v5.47.0-dev.10) (2025-12-08)

### Features

* **YouTube - Hide Shorts components:** Add "Hide auto-dubbed label" and "Hide live preview" options ([#6334](https://github.com/ReVanced/revanced-patches/issues/6334)) ([a7c220a](a7c220a4ae))
2025-12-08 12:57:23 +00:00
ILoveOpenSourceApplications
a7c220a4ae feat(YouTube - Hide Shorts components): Add "Hide auto-dubbed label" and "Hide live preview" options (#6334) 2025-12-08 13:51:57 +01:00
semantic-release-bot
d8ca4ee931 chore: Release v5.47.0-dev.9 [skip ci]
# [5.47.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.8...v5.47.0-dev.9) (2025-12-08)

### Features

* **YouTube - Hide layout components:** Add "Hide cell divider", "Hide featured links", and "Hide featured videos" options ([#6335](https://github.com/ReVanced/revanced-patches/issues/6335)) ([a5d197b](a5d197b977))
2025-12-08 12:06:03 +00:00
ILoveOpenSourceApplications
a5d197b977 feat(YouTube - Hide layout components): Add "Hide cell divider", "Hide featured links", and "Hide featured videos" options (#6335)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-08 13:02:22 +01:00
semantic-release-bot
a0ec4c07f7 chore: Release v5.47.0-dev.8 [skip ci]
# [5.47.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.7...v5.47.0-dev.8) (2025-12-08)

### Features

* **Instagram:** Add `Disable Reels scrolling` patch ([#6317](https://github.com/ReVanced/revanced-patches/issues/6317)) ([0928dcd](0928dcd00d))
* **ProtonVPN:** Add `Remove delay` patch ([#6326](https://github.com/ReVanced/revanced-patches/issues/6326)) ([bbd8932](bbd8932b2e))
2025-12-08 11:36:43 +00:00
Alexey Gorbachev
0928dcd00d feat(Instagram): Add Disable Reels scrolling patch (#6317)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-08 12:31:54 +01:00
Sylvain Finot
bbd8932b2e feat(ProtonVPN): Add Remove delay patch (#6326)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-12-08 12:30:47 +01:00
semantic-release-bot
300b12f948 chore: Release v5.47.0-dev.7 [skip ci]
# [5.47.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.6...v5.47.0-dev.7) (2025-12-03)

### Features

* **Spoof SIM provider:** Spoof additional TelephonyManager methods ([#6293](https://github.com/ReVanced/revanced-patches/issues/6293)) ([ac583d4](ac583d40d0))
2025-12-03 15:05:05 +00:00
rospino74
ac583d40d0 feat(Spoof SIM provider): Spoof additional TelephonyManager methods (#6293) 2025-12-03 16:01:08 +01:00
oSumAtrIX
e1e03e1b69 fix: Everything works now 2025-11-27 00:36:35 +01:00
oSumAtrIX
8c603802f7 fix: Adjusting to new API 2025-11-26 23:13:22 +01:00
semantic-release-bot
c400188c38 chore: Release v5.47.0-dev.6 [skip ci]
# [5.47.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.5...v5.47.0-dev.6) (2025-11-24)

### Features

* **Letterboxd:** Add `Hide ads` patch ([#6309](https://github.com/ReVanced/revanced-patches/issues/6309)) ([0af0ee9](0af0ee92c4))
2025-11-24 12:01:20 +00:00
Swakshan
0af0ee92c4 feat(Letterboxd): Add Hide ads patch (#6309)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-11-24 12:56:55 +01:00
oSumAtrIX
95c72ad300 feat: Use new API 2025-11-22 23:23:57 +01:00
LisoUseInAIKyrios
2f3ecab0e1 drop 20.13.41 for simplicity 2025-11-19 17:06:09 +02:00
LisoUseInAIKyrios
a5d39c3bbe fix splash screen changes not working and remove obsolete debugging code (issue was caused by bad merge of main branch) 2025-11-19 16:54:31 +02:00
LisoUseInAIKyrios
07c4dd3a55 Bump up minimum time after clean launch before bold icons can be forced on 2025-11-19 14:58:33 +02:00
LisoUseInAIKyrios
67c6c345ea Use bold icons by default with YT 20.31+ 2025-11-19 14:48:02 +02:00
LisoUseInAIKyrios
ed514d9755 Move patch warnings to individual patches if user turns off version constrains and patches anyway 2025-11-19 13:07:54 +02:00
LisoUseInAIKyrios
0e994f5bfe Add 20.21.37 to all patches, and 20.31.40 to patches that are known to completely work. 2025-11-19 12:26:20 +02:00
LisoUseInAIKyrios
8cc69fe38b Removing dev code that snuck in 2025-11-17 16:40:45 +02:00
LisoUseInAIKyrios
41b31dd56c refactor 2025-11-17 14:47:06 +02:00
LisoUseInAIKyrios
9671c7499d refactor 2025-11-16 10:25:24 +02:00
LisoUseInAIKyrios
d62d17fdeb unofficial 20.46.33 2025-11-14 08:51:20 +02:00
LisoUseInAIKyrios
c6eaba9af6 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LayoutComponentsFilter.java
2025-11-14 08:19:40 +02:00
semantic-release-bot
fff29544b9 chore: Release v5.47.0-dev.5 [skip ci]
# [5.47.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.4...v5.47.0-dev.5) (2025-11-13)

### Bug Fixes

* **YouTube - Hide player flyout menu items:** Allow hiding audio menu with 'Android No SDK' client type ([9495cf4](9495cf49ef))
2025-11-13 07:44:09 +00:00
LisoUseInAIKyrios
9495cf49ef fix(YouTube - Hide player flyout menu items): Allow hiding audio menu with 'Android No SDK' client type 2025-11-13 09:40:28 +02:00
semantic-release-bot
15675b5164 chore: Release v5.47.0-dev.4 [skip ci]
# [5.47.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.3...v5.47.0-dev.4) (2025-11-12)

### Bug Fixes

* **YouTube - Sanitize sharing links:** Handle non hierarchical urls ([654d091](654d091e65))
2025-11-12 19:01:00 +00:00
LisoUseInAIKyrios
654d091e65 fix(YouTube - Sanitize sharing links): Handle non hierarchical urls 2025-11-12 20:55:32 +02:00
semantic-release-bot
98371be33c chore: Release v5.47.0-dev.3 [skip ci]
# [5.47.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.2...v5.47.0-dev.3) (2025-11-12)

### Features

* **Instagram:** Add `Disable auto story flipping` patch ([#6262](https://github.com/ReVanced/revanced-patches/issues/6262)) ([2f0de15](2f0de15e67))
2025-11-12 07:46:04 +00:00
brosssh
2f0de15e67 feat(Instagram): Add Disable auto story flipping patch (#6262)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-11-12 08:41:15 +01:00
semantic-release-bot
df160370e2 chore: Release v5.47.0-dev.2 [skip ci]
# [5.47.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.1...v5.47.0-dev.2) (2025-11-12)

### Bug Fixes

* **Instagram - Disable signature check:** Change patch to default excluded ([#6283](https://github.com/ReVanced/revanced-patches/issues/6283)) ([bb745b5](bb745b555b))
2025-11-12 06:19:34 +00:00
LisoUseInAIKyrios
bb745b555b fix(Instagram - Disable signature check): Change patch to default excluded (#6283) 2025-11-12 08:14:16 +02:00
semantic-release-bot
8df9a46721 chore: Release v5.47.0-dev.1 [skip ci]
# [5.47.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.46.0...v5.47.0-dev.1) (2025-11-12)

### Features

* **Instagram:** Add `Anonymous story viewing` patch ([#6263](https://github.com/ReVanced/revanced-patches/issues/6263)) ([94ae84a](94ae84ad0f))
2025-11-12 05:32:16 +00:00
brosssh
94ae84ad0f feat(Instagram): Add Anonymous story viewing patch (#6263) 2025-11-12 07:29:13 +02:00
github-actions[bot]
4febb2e2e9 chore: Sync translations (#6280) 2025-11-12 07:28:43 +02:00
semantic-release-bot
b9bc7e3e58 chore: Release v5.46.0 [skip ci]
# [5.46.0](https://github.com/ReVanced/revanced-patches/compare/v5.45.0...v5.46.0) (2025-11-10)

### Bug Fixes

* **Duolingo - Disable ads:** Constrain patch to last working app target ([f238ae9](f238ae9895))
* **Instagram - Hide navigation buttons:** Constrain patch to last working app target ([e030e9c](e030e9c07a))
* **Spotify - Hide Create button:** Remove obsolete patch that is no longer needed ([#6252](https://github.com/ReVanced/revanced-patches/issues/6252)) ([59d85b2](59d85b28a7))
* **YouTube - Check watch history domain name resolution:** Fix false positive warning message if the internet connection fails halfway into the DNS check ([5726353](57263538c7))
* **YouTube - Hide layout components:** Fix "Hide Hype points" ([#6247](https://github.com/ReVanced/revanced-patches/issues/6247)) ([5821440](582144026d))
* **YouTube - Settings:** Add additional languages to ReVanced language preference ([d390b54](d390b54dab))
* **YouTube - Settings:** Resolve settings search crash when searching for specific words ([#6231](https://github.com/ReVanced/revanced-patches/issues/6231)) ([76dcfae](76dcfaefd8))

### Features

* **YouTube - Debugging:** Add setting to block experimental client flags ([#6196](https://github.com/ReVanced/revanced-patches/issues/6196)) ([2e9d695](2e9d6959c9))
* **YouTube - Hide layout components:** Add "Hide Hype points" ([#6230](https://github.com/ReVanced/revanced-patches/issues/6230)) ([a52c015](a52c0153b1))
* **YouTube - Hide layout components:** Add video description "Hide Featured content" and "Hide Subscribe button" ([#6253](https://github.com/ReVanced/revanced-patches/issues/6253)) ([da4cf94](da4cf94091))
* **YouTube - Hide player flyout menu items:** Add "Hide Listen with YouTube Music" ([#6232](https://github.com/ReVanced/revanced-patches/issues/6232)) ([858edbf](858edbf3e7))
* **YouTube Music:** Add `Change miniplayer color` patch ([#6259](https://github.com/ReVanced/revanced-patches/issues/6259)) ([ab808ae](ab808aeb77))
* **YouTube Music:** Add `Hide buttons` patch ([#6255](https://github.com/ReVanced/revanced-patches/issues/6255)) ([7a18ebc](7a18ebc7ab))
2025-11-10 10:06:40 +00:00
LisoUseInAIKyrios
9f3bb26cb9 chore: Merge branch dev to main (#6237) 2025-11-10 12:03:02 +02:00
github-actions[bot]
d64dfc2884 chore: Sync translations (#6276) 2025-11-10 12:00:41 +02:00
LisoUseInAIKyrios
db5b79ddbb Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt
2025-11-10 11:24:33 +02:00
LisoUseInAIKyrios
a39ef1e0a4 refactor(YouTube Music - Custom branding): Resolve startup app crash when patching unsupported newer app versions 2025-11-10 11:23:09 +02:00
LisoUseInAIKyrios
a1a80ebc57 fix search results patches 2025-11-10 10:43:08 +02:00
LisoUseInAIKyrios
5ccfb3cb9f Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-11-10 10:41:59 +02:00
semantic-release-bot
1d8e977a43 chore: Release v5.46.0-dev.10 [skip ci]
# [5.46.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.9...v5.46.0-dev.10) (2025-11-09)

### Features

* **YouTube - Hide layout components:** Add video description "Hide Featured content" and "Hide Subscribe button" ([#6253](https://github.com/ReVanced/revanced-patches/issues/6253)) ([da4cf94](da4cf94091))
2025-11-09 15:35:27 +00:00
ILoveOpenSourceApplications
da4cf94091 feat(YouTube - Hide layout components): Add video description "Hide Featured content" and "Hide Subscribe button" (#6253) 2025-11-09 17:30:07 +02:00
github-actions[bot]
d23fa5e3b7 chore: Sync translations (#6270) 2025-11-09 17:29:11 +02:00
LisoUseInAIKyrios
2687b3006b fix loop video not working 2025-11-09 10:16:34 +02:00
LisoUseInAIKyrios
29a86fb8ec refactor 2025-11-09 09:50:44 +02:00
semantic-release-bot
34d29abdfa chore: Release v5.46.0-dev.9 [skip ci]
# [5.46.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.8...v5.46.0-dev.9) (2025-11-09)

### Features

* **YouTube Music:** Add `Change miniplayer color` patch ([#6259](https://github.com/ReVanced/revanced-patches/issues/6259)) ([ab808ae](ab808aeb77))
2025-11-09 07:43:26 +00:00
MarcaD
ab808aeb77 feat(YouTube Music): Add Change miniplayer color patch (#6259)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-11-09 09:39:51 +02:00
github-actions[bot]
a6b07cceb1 chore: Sync translations (#6266) 2025-11-09 09:39:32 +02:00
LisoUseInAIKyrios
81a429af74 delete deprecated binary compatibility 2025-11-09 09:13:58 +02:00
LisoUseInAIKyrios
3406033732 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java
#	patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt
2025-11-09 09:11:01 +02:00
semantic-release-bot
d291881215 chore: Release v5.46.0-dev.8 [skip ci]
# [5.46.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.7...v5.46.0-dev.8) (2025-11-09)

### Features

* **YouTube Music:** Add `Hide buttons` patch ([#6255](https://github.com/ReVanced/revanced-patches/issues/6255)) ([7a18ebc](7a18ebc7ab))
2025-11-09 07:09:07 +00:00
MarcaD
7a18ebc7ab feat(YouTube Music): Add Hide buttons patch (#6255) 2025-11-09 09:05:31 +02:00
LisoUseInAIKyrios
afbcf3d90f fix exit fullscreen patch 2025-11-08 22:06:54 +02:00
LisoUseInAIKyrios
b7c995930a fix custom speeds over 2.0/4.0 not working 2025-11-08 19:47:40 +02:00
LisoUseInAIKyrios
675a2c4209 add fix content provider patch 2025-11-08 18:34:00 +02:00
semantic-release-bot
475197af45 chore: Release v5.46.0-dev.7 [skip ci]
# [5.46.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.6...v5.46.0-dev.7) (2025-11-08)

### Bug Fixes

* **YouTube - Settings:** Add additional languages to ReVanced language preference ([d390b54](d390b54dab))
2025-11-08 12:32:08 +00:00
LisoUseInAIKyrios
0855290097 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-11-08 14:29:22 +02:00
LisoUseInAIKyrios
d390b54dab fix(YouTube - Settings): Add additional languages to ReVanced language preference 2025-11-08 14:28:52 +02:00
github-actions[bot]
4d1eaa6b14 chore: Sync translations (#6260) 2025-11-08 14:26:20 +02:00
LisoUseInAIKyrios
49c925e95f remove 'by' syntax for fingerprints 2025-11-08 10:43:01 +02:00
LisoUseInAIKyrios
7499f3d19b add maven local 2025-11-08 10:35:56 +02:00
LisoUseInAIKyrios
3d55083dbc Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-11-08 10:08:45 +02:00
LisoUseInAIKyrios
c6364f5b49 chore: Fix compilation warning 2025-11-08 10:08:25 +02:00
semantic-release-bot
f177eae385 chore: Release v5.46.0-dev.6 [skip ci]
# [5.46.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.5...v5.46.0-dev.6) (2025-11-08)

### Features

* **YouTube - Debugging:** Add setting to block experimental client flags ([#6196](https://github.com/ReVanced/revanced-patches/issues/6196)) ([2e9d695](2e9d6959c9))
2025-11-08 08:07:41 +00:00
LisoUseInAIKyrios
829bfa76d1 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/NewSegmentLayout.java
#	patches/src/main/kotlin/app/revanced/patches/spotify/layout/hide/createbutton/Fingerprints.kt
2025-11-08 10:06:22 +02:00
MarcaD
2e9d6959c9 feat(YouTube - Debugging): Add setting to block experimental client flags (#6196) 2025-11-08 10:01:46 +02:00
semantic-release-bot
81f83690d6 chore: Release v5.46.0-dev.5 [skip ci]
# [5.46.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.4...v5.46.0-dev.5) (2025-11-07)

### Bug Fixes

* **Duolingo - Disable ads:** Constrain patch to last working app target ([f238ae9](f238ae9895))
* **Instagram - Hide navigation buttons:** Constrain patch to last working app target ([e030e9c](e030e9c07a))
* **Spotify - Hide Create button:** Remove obsolete patch that is no longer needed ([#6252](https://github.com/ReVanced/revanced-patches/issues/6252)) ([59d85b2](59d85b28a7))
2025-11-07 11:39:00 +00:00
LisoUseInAIKyrios
f1bd6848c9 chore(deps-dev): Revert bump semantic-release from 24.2.9 to 25.0.1 (#6204)
This reverts commit 55e1a6784b.
2025-11-07 13:36:18 +02:00
LisoUseInAIKyrios
59d85b28a7 fix(Spotify - Hide Create button): Remove obsolete patch that is no longer needed (#6252) 2025-11-07 13:29:34 +02:00
LisoUseInAIKyrios
f238ae9895 fix(Duolingo - Disable ads): Constrain patch to last working app target 2025-11-07 13:27:31 +02:00
LisoUseInAIKyrios
e030e9c07a fix(Instagram - Hide navigation buttons): Constrain patch to last working app target 2025-11-07 13:25:24 +02:00
dependabot[bot]
5029e979be chore(deps): Bump actions/upload-artifact from 4 to 5 (#6201) 2025-11-07 09:34:24 +02:00
dependabot[bot]
55e1a6784b chore(deps-dev): Bump semantic-release from 24.2.9 to 25.0.1 (#6204) 2025-11-07 09:33:23 +02:00
dependabot[bot]
0cad5e73f0 chore(deps): Bump actions/setup-node from 5 to 6 (#6200) 2025-11-07 09:33:05 +02:00
LisoUseInAIKyrios
88352d8774 Unofficial 20.45.32 2025-11-07 09:00:28 +02:00
LisoUseInAIKyrios
03ce5711de Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-11-07 08:29:54 +02:00
LisoUseInAIKyrios
fc70f852f9 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-11-02 09:46:22 +01:00
LisoUseInAIKyrios
955f7c9341 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-10-30 12:27:44 +01:00
LisoUseInAIKyrios
93160722c0 add temporary debug logging 2025-10-30 12:27:37 +01:00
LisoUseInAIKyrios
78689fde83 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch.kt
2025-10-30 09:28:16 +01:00
LisoUseInAIKyrios
260afefaab fix data migration 2025-10-28 12:57:55 +04:00
LisoUseInAIKyrios
b7be52dec6 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/ChangeHeaderPatch.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java
2025-10-27 21:15:19 +04:00
LisoUseInAIKyrios
594317e573 Change lowest supported version to 19.43.41 2025-10-27 11:18:12 +04:00
LisoUseInAIKyrios
c95170ecbd fix fullscreen button vertical alignment 2025-10-27 11:11:51 +04:00
LisoUseInAIKyrios
ecda492866 finish merge 2025-10-27 11:09:55 +04:00
LisoUseInAIKyrios
4a73671262 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/duolingo/debug/EnableDebugMenuPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/duolingo/debug/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/music/layout/compactheader/HideCategoryBar.kt
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/audio/Fingerprints.kt
2025-10-27 11:06:09 +04:00
LisoUseInAIKyrios
6d72b4a3fb unofficial 20.43.32 2025-10-23 11:37:45 +04:00
LisoUseInAIKyrios
f00c0e0d89 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt
2025-10-22 11:52:22 +04:00
LisoUseInAIKyrios
772620c8ce refactor 2025-10-21 21:41:02 +04:00
LisoUseInAIKyrios
9984e586b4 Show bold search icon 2025-10-21 11:58:14 +04:00
LisoUseInAIKyrios
9a2f23291d Don't allow bold icons if spoofing to very old targets 2025-10-21 11:42:25 +04:00
LisoUseInAIKyrios
7ef70f7823 remove bold alert icon 2025-10-21 11:07:21 +04:00
LisoUseInAIKyrios
a1fd6b13d5 reformat xml. no functional changes 2025-10-21 11:03:54 +04:00
LisoUseInAIKyrios
f30ece9287 Add SB bold icon placeholder code 2025-10-20 23:58:09 +04:00
LisoUseInAIKyrios
f2356a8be2 fix swipe bold icon 2025-10-20 23:22:22 +04:00
LisoUseInAIKyrios
47f1a5f9c9 add the last bold resources 2025-10-20 22:18:05 +04:00
LisoUseInAIKyrios
fc988fa078 fix setting availability 2025-10-20 21:57:08 +04:00
LisoUseInAIKyrios
c37527f182 Add ReVanced bold icons 2025-10-20 21:55:14 +04:00
LisoUseInAIKyrios
64334b4f79 refactor 2025-10-20 20:07:17 +04:00
LisoUseInAIKyrios
0389073600 fix minimal miniplayer using incorrectly sized bold icons 2025-10-20 11:42:20 +04:00
LisoUseInAIKyrios
5449357f7f refactor 2025-10-20 11:14:32 +04:00
LisoUseInAIKyrios
39da47e6ee Use YT notification icon that isn't associated with the navigation bar enum (YT still failed to fixed even after a second icon redesign) 2025-10-20 10:58:11 +04:00
LisoUseInAIKyrios
1356a7e5b2 add 20.31+ bold icons setting 2025-10-20 10:35:49 +04:00
LisoUseInAIKyrios
b5cda51048 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt
2025-10-18 13:22:16 +04:00
LisoUseInAIKyrios
a1ad5fea20 unofficial 20.42.36 2025-10-18 13:21:43 +04:00
LisoUseInAIKyrios
c98c73b0bc Don't turn off new Shorts player flag on newer targets since it can break the Shorts player overlay 2025-10-15 11:46:11 +04:00
LisoUseInAIKyrios
ed87bc7b7a Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/youtube/misc/playservice/VersionCheckPatch.kt
2025-10-14 16:18:30 +04:00
LisoUseInAIKyrios
1901e965e8 unofficial 20.41.33 2025-10-14 10:50:32 +04:00
LisoUseInAIKyrios
aae71e6a19 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt
2025-10-14 09:57:28 +04:00
LisoUseInAIKyrios
a1c9170cc9 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/Fingerprints.kt
2025-10-11 02:17:07 +04:00
LisoUseInAIKyrios
88b077a9b7 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/gms/GmsCoreSupportPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/shorts/HideShortsComponentsPatch.kt
2025-10-07 23:51:32 +04:00
LisoUseInAIKyrios
563f586eed fix 20.22+ litho broken for some users 2025-10-03 11:15:49 +04:00
LisoUseInAIKyrios
c6becb4044 unofficial 20.40.33 2025-10-03 11:07:34 +04:00
LisoUseInAIKyrios
2d207fccbc Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ShortsFilter.java
2025-10-03 10:55:41 +04:00
LisoUseInAIKyrios
967ef47c2d Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatch.kt
2025-10-01 19:01:29 +04:00
LisoUseInAIKyrios
240e953160 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-10-01 19:00:06 +04:00
LisoUseInAIKyrios
0f03a071e9 use navigation notification button SVG 2025-10-01 17:56:30 +04:00
LisoUseInAIKyrios
e52b33981c Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/twitter/misc/links/ChangeLinkSharingDomainPatch.kt
2025-10-01 11:58:47 +04:00
LisoUseInAIKyrios
4d9de1a81a Use custom navigation bar notification filled icon (20.39 still needs a bytecode fix) 2025-10-01 11:57:53 +04:00
LisoUseInAIKyrios
636bded69c refactor: Add main activity onCreate extension hook function 2025-09-30 22:21:35 +04:00
LisoUseInAIKyrios
2d49d76e82 finish merge 2025-09-28 16:39:28 +04:00
LisoUseInAIKyrios
934947a257 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/api/patches.api
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt
2025-09-28 16:34:51 +04:00
LisoUseInAIKyrios
8e05bb3a80 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/music/layout/castbutton/HideCastButton.kt
2025-09-27 16:31:53 +04:00
LisoUseInAIKyrios
f88ad4e4a7 modernize 2025-09-27 16:24:28 +04:00
LisoUseInAIKyrios
f252fb24b6 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-27 16:22:00 +04:00
LisoUseInAIKyrios
2f9081eb6c Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-27 16:11:54 +04:00
LisoUseInAIKyrios
a82f49aa08 fix typo 2025-09-26 19:06:51 +04:00
LisoUseInAIKyrios
823530f707 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-26 19:05:20 +04:00
LisoUseInAIKyrios
7eb78d4f2b finish merge 2025-09-26 12:31:58 +04:00
LisoUseInAIKyrios
4dec67385e Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java
#	patches/api/patches.api
#	patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/NavigationBarPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/LithoColorHookPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt
2025-09-26 12:27:58 +04:00
LisoUseInAIKyrios
edaad1a7b7 remove 20.07 (one of the more difficult and constantly changing sanitize url fingerprints no longer matches, and don't want to fix. Only the oldest and latest are what anyone cares about) 2025-09-25 22:17:01 +04:00
LisoUseInAIKyrios
d3df24977a work in progress cairo notification tab selected icon fix. Icon modified from free icon at https://fontawesome.com/icons/bell?f=classic&s=solid
No attribution required, but png metadata contains the source url
2025-09-25 22:16:37 +04:00
LisoUseInAIKyrios
7b02a31e3f unofficial 20.39 work in progress (navigation bar notification tab icon fix is TODO) 2025-09-25 22:13:12 +04:00
LisoUseInAIKyrios
41c8fbc10d Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-25 21:04:34 +04:00
LisoUseInAIKyrios
59e1321e62 debugging 2025-09-25 16:25:28 +04:00
LisoUseInAIKyrios
252f57f430 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ShortsFilter.java
2025-09-25 00:33:16 +04:00
LisoUseInAIKyrios
d6593e2acd finish merge 2025-09-23 22:10:14 +04:00
LisoUseInAIKyrios
c4e6e62e71 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/instagram/hide/navigation/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/music/misc/extension/hooks/ApplicationInitHook.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/video/quality/Fingerprints.kt
2025-09-23 22:07:21 +04:00
LisoUseInAIKyrios
ca736094e4 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-23 10:21:04 +04:00
LisoUseInAIKyrios
7e010d38cc delete deprecated dummy files 2025-09-22 22:05:54 +04:00
LisoUseInAIKyrios
a4d24ad192 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt
2025-09-22 22:02:40 +04:00
LisoUseInAIKyrios
45d42a1405 fix merge error 2025-09-22 18:48:44 +04:00
LisoUseInAIKyrios
ecf5752100 fix merge error 2025-09-22 17:39:11 +04:00
LisoUseInAIKyrios
4096b34003 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt
2025-09-22 15:05:43 +04:00
LisoUseInAIKyrios
23b200ce68 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-22 15:03:55 +04:00
LisoUseInAIKyrios
56876f336b finish merge 2025-09-21 21:21:56 +04:00
LisoUseInAIKyrios
c7a71f44df Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/RemoveTrackingQueryParameterPatch.java
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/privacy/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatch.kt
2025-09-21 21:17:44 +04:00
LisoUseInAIKyrios
a25d769f69 remove deprecated migration code 2025-09-21 19:10:34 +04:00
LisoUseInAIKyrios
c2a099d1f4 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/music/src/main/java/app/revanced/extension/music/settings/GoogleApiActivityHook.java
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/checks/Check.java
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/BaseActivityHook.java
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ColorPickerPreference.java
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/CustomDialogListPreference.java
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java
#	extensions/twitch/src/main/java/app/revanced/extension/twitch/settings/TwitchActivityHook.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/AlternativeThumbnailsPatch.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/HidePlayerOverlayButtonsPatch.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/SearchViewController.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/YouTubeActivityHook.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ExternalDownloaderPreference.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/objects/SegmentCategoryListPreference.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/NewSegmentLayout.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SkipSponsorButton.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/SponsorBlockViewController.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java
#	patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt
#	patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/music/layout/upgradebutton/HideUpgradeButtonPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/music/misc/settings/SettingsPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt
2025-09-21 18:01:08 +04:00
LisoUseInAIKyrios
d906046a52 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ButtonsFilter.java
#	patches/src/main/kotlin/app/revanced/patches/music/layout/navigationbar/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/spoofappversion/SpoofAppVersionPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/backtoexitgesture/FixBackToExitGesturePatch.kt
2025-09-20 19:32:22 +04:00
LisoUseInAIKyrios
765957f2c9 finish merge 2025-09-20 17:56:33 +04:00
LisoUseInAIKyrios
8e64416f14 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/api/patches.api
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt
2025-09-20 17:50:31 +04:00
LisoUseInAIKyrios
6a5b204f8e fix SB create/voting buttons (merge error?) 2025-09-19 11:39:59 +04:00
LisoUseInAIKyrios
b99789b1cd unofficial 20.38 2025-09-19 11:08:48 +04:00
LisoUseInAIKyrios
bb671766f6 finish merge 2025-09-18 10:18:33 +04:00
LisoUseInAIKyrios
97ce498368 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/LithoFilterPatch.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter.java
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/viber/ads/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/action/HideButtonsPatch.kt
2025-09-18 10:18:25 +04:00
LisoUseInAIKyrios
dcaa6feda0 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-16 17:27:38 +04:00
LisoUseInAIKyrios
e8d56c85cc Finish merge 2025-09-16 12:00:02 +04:00
LisoUseInAIKyrios
04401899f4 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/requests/StreamingDataRequest.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java
#	extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java
#	patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentrepeat/PermanentRepeatPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/interaction/downloads/DownloadsPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/misc/extension/hooks/ApplicationInitHook.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/youtube/shared/Fingerprints.kt
2025-09-16 11:59:40 +04:00
LisoUseInAIKyrios
e52a9509e2 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-15 11:07:48 +04:00
LisoUseInAIKyrios
c6d84744ca Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-14 18:06:59 +04:00
LisoUseInAIKyrios
d3fae2a3e7 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters 2025-09-14 15:58:19 +04:00
LisoUseInAIKyrios
6ddf0583a4 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/api/patches.api
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt
2025-09-14 15:52:54 +04:00
LisoUseInAIKyrios
77864f41f4 unofficial 20.37 support 2025-09-14 14:22:01 +04:00
LisoUseInAIKyrios
a352a05db6 Merge remote-tracking branch 'upstream/dev' into feat/patcher_instruction_filters
# Conflicts:
#	patches/api/patches.api
#	patches/src/main/kotlin/app/revanced/patches/music/interaction/permanentshuffle/PermanentShufflePatch.kt
#	patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofClientPatch.kt
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/Fingerprints.kt
#	patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt
2025-09-14 02:35:21 +04:00
LisoUseInAIKyrios
724e6d61b2 feat: Update to patcher v22 2025-09-12 20:43:20 +04:00
775 changed files with 14809 additions and 9354 deletions

View File

@@ -29,7 +29,7 @@ jobs:
run: ./gradlew :patches:buildAndroid --no-daemon
- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: revanced-patches
path: patches/build/libs

View File

@@ -35,7 +35,7 @@ jobs:
run: ./gradlew :patches:buildAndroid clean
- name: Setup Node.js
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: 'lts/*'
cache: 'npm'

View File

@@ -1,3 +1,309 @@
# [5.48.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.8...v5.48.0-dev.9) (2026-01-08)
### Features
* Add `Disable Sentry telemetry` patch ([#6416](https://github.com/ReVanced/revanced-patches/issues/6416)) ([4cc3159](https://github.com/ReVanced/revanced-patches/commit/4cc315952db557c565872de9e8484805f2e42305))
* Disable Play Integrity patch ([#6412](https://github.com/ReVanced/revanced-patches/issues/6412)) ([6312fe8](https://github.com/ReVanced/revanced-patches/commit/6312fe8d60da24465c0c1b0fa4e94ceb79873d9c))
# [5.48.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.7...v5.48.0-dev.8) (2026-01-04)
### Features
* **Letterboxd:** Add `Unlock app icons` patch ([#6415](https://github.com/ReVanced/revanced-patches/issues/6415)) ([d25dcfe](https://github.com/ReVanced/revanced-patches/commit/d25dcfe49ac331c9b3dca739ba0be95dbab669cc))
# [5.48.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.6...v5.48.0-dev.7) (2026-01-04)
### Features
* **Strava:** Add `Disable Quick Edit` patch ([#6452](https://github.com/ReVanced/revanced-patches/issues/6452)) ([f5cbb31](https://github.com/ReVanced/revanced-patches/commit/f5cbb31724d15f7e939b96ee0186fd0a108f9fdc))
* **Strava:** Add `Overwrite media upload parameters` patch ([#6410](https://github.com/ReVanced/revanced-patches/issues/6410)) ([b42ae27](https://github.com/ReVanced/revanced-patches/commit/b42ae27ce66ebad9e9cfc5b70fc121df5bad7567))
# [5.48.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.5...v5.48.0-dev.6) (2026-01-04)
### Bug Fixes
* Fix build error introduced in `4046bee` ([#6417](https://github.com/ReVanced/revanced-patches/issues/6417)) ([789f0a5](https://github.com/ReVanced/revanced-patches/commit/789f0a562861825065633d172445ebf35a1ba8d8))
# [5.48.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.4...v5.48.0-dev.5) (2025-12-30)
### Bug Fixes
* **Disney+ - Skip ads:** Remove unsupported package names ([#6422](https://github.com/ReVanced/revanced-patches/issues/6422)) ([44e7dbc](https://github.com/ReVanced/revanced-patches/commit/44e7dbcf4d7eaf94dd0164baba847d3e19250154))
# [5.48.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.3...v5.48.0-dev.4) (2025-12-29)
### Features
* **Strava:** Add `Block Snowplow tracking` patch ([#6413](https://github.com/ReVanced/revanced-patches/issues/6413)) ([c47beae](https://github.com/ReVanced/revanced-patches/commit/c47beae21376dd17ab8bc09afe73e9094481bde9))
# [5.48.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.2...v5.48.0-dev.3) (2025-12-28)
### Bug Fixes
* Fix compilation error introduced in `6bb6281` ([#6409](https://github.com/ReVanced/revanced-patches/issues/6409)) ([71c6cb5](https://github.com/ReVanced/revanced-patches/commit/71c6cb569ebf7b93cf73ee391839e5220557ce7c))
### Features
* **Instagram - Hides navigation buttons:** Add more buttons to hide ([#6390](https://github.com/ReVanced/revanced-patches/issues/6390)) ([6bb6281](https://github.com/ReVanced/revanced-patches/commit/6bb62811493da04812cc3e392e68d874f95cbef9))
# [5.48.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.1...v5.48.0-dev.2) (2025-12-27)
### Features
* **Strava:** Add `Enable password login` patch ([#6396](https://github.com/ReVanced/revanced-patches/issues/6396)) ([8f3f4c9](https://github.com/ReVanced/revanced-patches/commit/8f3f4c95bb8f151fc9a2c272bf7d0e905c2f01fc))
# [5.48.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.47.0...v5.48.0-dev.1) (2025-12-23)
### Bug Fixes
* Fix compilation error introduced in dc69f243 ([#6392](https://github.com/ReVanced/revanced-patches/issues/6392)) ([a429824](https://github.com/ReVanced/revanced-patches/commit/a429824bb77b49aea14b0b54f2204ae24d5209a1))
* **YouTube - Hide layout components:** Hide new type of crowdfunding box ([#6380](https://github.com/ReVanced/revanced-patches/issues/6380)) ([dc69f24](https://github.com/ReVanced/revanced-patches/commit/dc69f2433e2650654e2dffdd76b0b0c8a52bf515))
### Features
* **ProtonVPN:** Add `Unlock split tunneling` patch ([#6353](https://github.com/ReVanced/revanced-patches/issues/6353)) ([e0f3346](https://github.com/ReVanced/revanced-patches/commit/e0f33468e6e96b9f10cf35ec67622d6488528c90))
* **SBS On Demand:** Add `Remove ads` patch ([#6378](https://github.com/ReVanced/revanced-patches/issues/6378)) ([315931c](https://github.com/ReVanced/revanced-patches/commit/315931cbf8f61cd4b3a54ace1ff03685d748614c))
# [5.47.0](https://github.com/ReVanced/revanced-patches/compare/v5.46.0...v5.47.0) (2025-12-18)
### Bug Fixes
* **Instagram - Disable signature check:** Change patch to default excluded ([#6283](https://github.com/ReVanced/revanced-patches/issues/6283)) ([bb745b5](https://github.com/ReVanced/revanced-patches/commit/bb745b555b3808b7679c5995319aa365630fbd76))
* **Lightroom:** Add `Disable version check` patch to fix opening the app ([#6315](https://github.com/ReVanced/revanced-patches/issues/6315)) ([018d176](https://github.com/ReVanced/revanced-patches/commit/018d176914a06a30e9007a3eb2e6b0f459078413))
* **Reddit - Hide ads:** Update patch for new versions of Reddit ([#6342](https://github.com/ReVanced/revanced-patches/issues/6342)) ([f8bd123](https://github.com/ReVanced/revanced-patches/commit/f8bd1239cc0f0bd1c2dca39f846951bf512891e3))
* **Spotify:** Make patches work with latest versions again ([#6359](https://github.com/ReVanced/revanced-patches/issues/6359)) ([34830ba](https://github.com/ReVanced/revanced-patches/commit/34830ba63b436146064f0f89f948d51cd0cb9146))
* **YouTube - Hide layout components:** Fix "Hide Subscribe button" in channel page not working ([#6363](https://github.com/ReVanced/revanced-patches/issues/6363)) ([ded8370](https://github.com/ReVanced/revanced-patches/commit/ded83702077701aac8a8749d71bf7376427f37d6))
* **YouTube - Hide player flyout menu items:** Allow hiding audio menu with 'Android No SDK' client type ([9495cf4](https://github.com/ReVanced/revanced-patches/commit/9495cf49ef8a872be64de6c971c1919b4b9a8720))
* **YouTube - Sanitize sharing links:** Handle non hierarchical urls ([654d091](https://github.com/ReVanced/revanced-patches/commit/654d091e650cda37650b57cbf3ba6f1cdd6d47d3))
### Features
* **Disney+ - SkipAds:** Add other package names the patch is compatible with ([#6372](https://github.com/ReVanced/revanced-patches/issues/6372)) ([1f4f252](https://github.com/ReVanced/revanced-patches/commit/1f4f252c81e9a89267f6e37548e66027b1bc1a1a))
* **Disney+:** Add `Skip ads` patch ([#6343](https://github.com/ReVanced/revanced-patches/issues/6343)) ([6bd7dca](https://github.com/ReVanced/revanced-patches/commit/6bd7dca75bd2ea335a596aa93a8b767d39be5f83))
* **IdAustria - Remove device integrity check:** Update patch to work with latest version ([#6360](https://github.com/ReVanced/revanced-patches/issues/6360)) ([0ea3491](https://github.com/ReVanced/revanced-patches/commit/0ea3491227fc50c03555d43d3fec78eb82906b26))
* **Instagram:** Add `Anonymous story viewing` patch ([#6263](https://github.com/ReVanced/revanced-patches/issues/6263)) ([94ae84a](https://github.com/ReVanced/revanced-patches/commit/94ae84ad0fc3a9197c82d5356301d464730c3b17))
* **Instagram:** Add `Disable auto story flipping` patch ([#6262](https://github.com/ReVanced/revanced-patches/issues/6262)) ([2f0de15](https://github.com/ReVanced/revanced-patches/commit/2f0de15e67e4f99ed6ecdc136d04cceb23b0d069))
* **Instagram:** Add `Disable Reels scrolling` patch ([#6317](https://github.com/ReVanced/revanced-patches/issues/6317)) ([0928dcd](https://github.com/ReVanced/revanced-patches/commit/0928dcd00dc2a9c1eef9a23c1e26ff5dc9ee670a))
* **Letterboxd:** Add `Hide ads` patch ([#6309](https://github.com/ReVanced/revanced-patches/issues/6309)) ([0af0ee9](https://github.com/ReVanced/revanced-patches/commit/0af0ee92c48bb2ffc332197e05439e20c5c05d83))
* **Peacock TV:** Add `Hide ads` patch ([#6348](https://github.com/ReVanced/revanced-patches/issues/6348)) ([847ee18](https://github.com/ReVanced/revanced-patches/commit/847ee189a971e6d4a99823998569f8e561b8319c))
* **ProtonVPN:** Add `Remove delay` patch ([#6326](https://github.com/ReVanced/revanced-patches/issues/6326)) ([bbd8932](https://github.com/ReVanced/revanced-patches/commit/bbd8932b2e740aff96ba047332e541bff3e09436))
* **Spoof SIM provider:** Spoof additional TelephonyManager methods ([#6293](https://github.com/ReVanced/revanced-patches/issues/6293)) ([ac583d4](https://github.com/ReVanced/revanced-patches/commit/ac583d40d0f4c0e6544e3661ff3e82a25912f2b0))
* **YouTube - Hide layout components:** Add "Hide cell divider", "Hide featured links", and "Hide featured videos" options ([#6335](https://github.com/ReVanced/revanced-patches/issues/6335)) ([a5d197b](https://github.com/ReVanced/revanced-patches/commit/a5d197b9775b98d7a37bfdee9e5f726d5e04d8cf))
* **YouTube - Hide layout components:** Add "Hide Join button" and "Hide Subscribe button" options for channel page ([#6345](https://github.com/ReVanced/revanced-patches/issues/6345)) ([02831a6](https://github.com/ReVanced/revanced-patches/commit/02831a6069fc30ffa3a87f8e4de653d003a2187e))
* **YouTube - Hide Shorts components:** Add "Hide auto-dubbed label" and "Hide live preview" options ([#6334](https://github.com/ReVanced/revanced-patches/issues/6334)) ([a7c220a](https://github.com/ReVanced/revanced-patches/commit/a7c220a4aea93ea7ae7005b5760443d7571c4228))
# [5.47.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.17...v5.47.0-dev.18) (2025-12-18)
### Features
* **Disney+ - SkipAds:** Add other package names the patch is compatible with ([#6372](https://github.com/ReVanced/revanced-patches/issues/6372)) ([1f4f252](https://github.com/ReVanced/revanced-patches/commit/1f4f252c81e9a89267f6e37548e66027b1bc1a1a))
# [5.47.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.16...v5.47.0-dev.17) (2025-12-18)
### Bug Fixes
* **Reddit - Hide ads:** Update patch for new versions of Reddit ([#6342](https://github.com/ReVanced/revanced-patches/issues/6342)) ([f8bd123](https://github.com/ReVanced/revanced-patches/commit/f8bd1239cc0f0bd1c2dca39f846951bf512891e3))
# [5.47.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.15...v5.47.0-dev.16) (2025-12-15)
### Bug Fixes
* **Lightroom:** Add `Disable version check` patch to fix opening the app ([#6315](https://github.com/ReVanced/revanced-patches/issues/6315)) ([018d176](https://github.com/ReVanced/revanced-patches/commit/018d176914a06a30e9007a3eb2e6b0f459078413))
### Features
* **IdAustria - Remove device integrity check:** Update patch to work with latest version ([#6360](https://github.com/ReVanced/revanced-patches/issues/6360)) ([0ea3491](https://github.com/ReVanced/revanced-patches/commit/0ea3491227fc50c03555d43d3fec78eb82906b26))
# [5.47.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.14...v5.47.0-dev.15) (2025-12-13)
### Bug Fixes
* **YouTube - Hide layout components:** Fix "Hide Subscribe button" in channel page not working ([#6363](https://github.com/ReVanced/revanced-patches/issues/6363)) ([ded8370](https://github.com/ReVanced/revanced-patches/commit/ded83702077701aac8a8749d71bf7376427f37d6))
# [5.47.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.13...v5.47.0-dev.14) (2025-12-13)
### Bug Fixes
* **Spotify:** Make patches work with latest versions again ([#6359](https://github.com/ReVanced/revanced-patches/issues/6359)) ([34830ba](https://github.com/ReVanced/revanced-patches/commit/34830ba63b436146064f0f89f948d51cd0cb9146))
# [5.47.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.12...v5.47.0-dev.13) (2025-12-10)
### Features
* **Peacock TV:** Add `Hide ads` patch ([#6348](https://github.com/ReVanced/revanced-patches/issues/6348)) ([847ee18](https://github.com/ReVanced/revanced-patches/commit/847ee189a971e6d4a99823998569f8e561b8319c))
# [5.47.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.11...v5.47.0-dev.12) (2025-12-08)
### Features
* **YouTube - Hide layout components:** Add "Hide Join button" and "Hide Subscribe button" options for channel page ([#6345](https://github.com/ReVanced/revanced-patches/issues/6345)) ([02831a6](https://github.com/ReVanced/revanced-patches/commit/02831a6069fc30ffa3a87f8e4de653d003a2187e))
# [5.47.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.10...v5.47.0-dev.11) (2025-12-08)
### Features
* **Disney+:** Add `Skip ads` patch ([#6343](https://github.com/ReVanced/revanced-patches/issues/6343)) ([6bd7dca](https://github.com/ReVanced/revanced-patches/commit/6bd7dca75bd2ea335a596aa93a8b767d39be5f83))
# [5.47.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.9...v5.47.0-dev.10) (2025-12-08)
### Features
* **YouTube - Hide Shorts components:** Add "Hide auto-dubbed label" and "Hide live preview" options ([#6334](https://github.com/ReVanced/revanced-patches/issues/6334)) ([a7c220a](https://github.com/ReVanced/revanced-patches/commit/a7c220a4aea93ea7ae7005b5760443d7571c4228))
# [5.47.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.8...v5.47.0-dev.9) (2025-12-08)
### Features
* **YouTube - Hide layout components:** Add "Hide cell divider", "Hide featured links", and "Hide featured videos" options ([#6335](https://github.com/ReVanced/revanced-patches/issues/6335)) ([a5d197b](https://github.com/ReVanced/revanced-patches/commit/a5d197b9775b98d7a37bfdee9e5f726d5e04d8cf))
# [5.47.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.7...v5.47.0-dev.8) (2025-12-08)
### Features
* **Instagram:** Add `Disable Reels scrolling` patch ([#6317](https://github.com/ReVanced/revanced-patches/issues/6317)) ([0928dcd](https://github.com/ReVanced/revanced-patches/commit/0928dcd00dc2a9c1eef9a23c1e26ff5dc9ee670a))
* **ProtonVPN:** Add `Remove delay` patch ([#6326](https://github.com/ReVanced/revanced-patches/issues/6326)) ([bbd8932](https://github.com/ReVanced/revanced-patches/commit/bbd8932b2e740aff96ba047332e541bff3e09436))
# [5.47.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.6...v5.47.0-dev.7) (2025-12-03)
### Features
* **Spoof SIM provider:** Spoof additional TelephonyManager methods ([#6293](https://github.com/ReVanced/revanced-patches/issues/6293)) ([ac583d4](https://github.com/ReVanced/revanced-patches/commit/ac583d40d0f4c0e6544e3661ff3e82a25912f2b0))
# [5.47.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.5...v5.47.0-dev.6) (2025-11-24)
### Features
* **Letterboxd:** Add `Hide ads` patch ([#6309](https://github.com/ReVanced/revanced-patches/issues/6309)) ([0af0ee9](https://github.com/ReVanced/revanced-patches/commit/0af0ee92c48bb2ffc332197e05439e20c5c05d83))
# [5.47.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.4...v5.47.0-dev.5) (2025-11-13)
### Bug Fixes
* **YouTube - Hide player flyout menu items:** Allow hiding audio menu with 'Android No SDK' client type ([9495cf4](https://github.com/ReVanced/revanced-patches/commit/9495cf49ef8a872be64de6c971c1919b4b9a8720))
# [5.47.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.3...v5.47.0-dev.4) (2025-11-12)
### Bug Fixes
* **YouTube - Sanitize sharing links:** Handle non hierarchical urls ([654d091](https://github.com/ReVanced/revanced-patches/commit/654d091e650cda37650b57cbf3ba6f1cdd6d47d3))
# [5.47.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.2...v5.47.0-dev.3) (2025-11-12)
### Features
* **Instagram:** Add `Disable auto story flipping` patch ([#6262](https://github.com/ReVanced/revanced-patches/issues/6262)) ([2f0de15](https://github.com/ReVanced/revanced-patches/commit/2f0de15e67e4f99ed6ecdc136d04cceb23b0d069))
# [5.47.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.47.0-dev.1...v5.47.0-dev.2) (2025-11-12)
### Bug Fixes
* **Instagram - Disable signature check:** Change patch to default excluded ([#6283](https://github.com/ReVanced/revanced-patches/issues/6283)) ([bb745b5](https://github.com/ReVanced/revanced-patches/commit/bb745b555b3808b7679c5995319aa365630fbd76))
# [5.47.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.46.0...v5.47.0-dev.1) (2025-11-12)
### Features
* **Instagram:** Add `Anonymous story viewing` patch ([#6263](https://github.com/ReVanced/revanced-patches/issues/6263)) ([94ae84a](https://github.com/ReVanced/revanced-patches/commit/94ae84ad0fc3a9197c82d5356301d464730c3b17))
# [5.46.0](https://github.com/ReVanced/revanced-patches/compare/v5.45.0...v5.46.0) (2025-11-10)
### Bug Fixes
* **Duolingo - Disable ads:** Constrain patch to last working app target ([f238ae9](https://github.com/ReVanced/revanced-patches/commit/f238ae9895000f01d1dccb800cc8efde0d5362bd))
* **Instagram - Hide navigation buttons:** Constrain patch to last working app target ([e030e9c](https://github.com/ReVanced/revanced-patches/commit/e030e9c07a7748e117ac44f6776a9f6317b20623))
* **Spotify - Hide Create button:** Remove obsolete patch that is no longer needed ([#6252](https://github.com/ReVanced/revanced-patches/issues/6252)) ([59d85b2](https://github.com/ReVanced/revanced-patches/commit/59d85b28a7fcb285ff5f2bb6ae654020d76b2019))
* **YouTube - Check watch history domain name resolution:** Fix false positive warning message if the internet connection fails halfway into the DNS check ([5726353](https://github.com/ReVanced/revanced-patches/commit/57263538c79f5a561c449229ac8e068c641285d3))
* **YouTube - Hide layout components:** Fix "Hide Hype points" ([#6247](https://github.com/ReVanced/revanced-patches/issues/6247)) ([5821440](https://github.com/ReVanced/revanced-patches/commit/582144026d28e57bb7adcbba39244f3c7cdbc0f3))
* **YouTube - Settings:** Add additional languages to ReVanced language preference ([d390b54](https://github.com/ReVanced/revanced-patches/commit/d390b54dab92d75b4e0d3e38344eae489dd69d98))
* **YouTube - Settings:** Resolve settings search crash when searching for specific words ([#6231](https://github.com/ReVanced/revanced-patches/issues/6231)) ([76dcfae](https://github.com/ReVanced/revanced-patches/commit/76dcfaefd8679e45a70f265b0239436e60c055cf))
### Features
* **YouTube - Debugging:** Add setting to block experimental client flags ([#6196](https://github.com/ReVanced/revanced-patches/issues/6196)) ([2e9d695](https://github.com/ReVanced/revanced-patches/commit/2e9d6959c94df7588b9e34b18770e9f437e91926))
* **YouTube - Hide layout components:** Add "Hide Hype points" ([#6230](https://github.com/ReVanced/revanced-patches/issues/6230)) ([a52c015](https://github.com/ReVanced/revanced-patches/commit/a52c0153b12c3f6f0ad260e03d2e9850c0466392))
* **YouTube - Hide layout components:** Add video description "Hide Featured content" and "Hide Subscribe button" ([#6253](https://github.com/ReVanced/revanced-patches/issues/6253)) ([da4cf94](https://github.com/ReVanced/revanced-patches/commit/da4cf940911a4406e2c9dd558b60305385a80c61))
* **YouTube - Hide player flyout menu items:** Add "Hide Listen with YouTube Music" ([#6232](https://github.com/ReVanced/revanced-patches/issues/6232)) ([858edbf](https://github.com/ReVanced/revanced-patches/commit/858edbf3e7f394fcc766d767c8dc54cf5ba24370))
* **YouTube Music:** Add `Change miniplayer color` patch ([#6259](https://github.com/ReVanced/revanced-patches/issues/6259)) ([ab808ae](https://github.com/ReVanced/revanced-patches/commit/ab808aeb773592cb26c848d8456478a346ec3bad))
* **YouTube Music:** Add `Hide buttons` patch ([#6255](https://github.com/ReVanced/revanced-patches/issues/6255)) ([7a18ebc](https://github.com/ReVanced/revanced-patches/commit/7a18ebc7ab74ba30c5d5284a4856c55cdfc31097))
# [5.46.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.9...v5.46.0-dev.10) (2025-11-09)
### Features
* **YouTube - Hide layout components:** Add video description "Hide Featured content" and "Hide Subscribe button" ([#6253](https://github.com/ReVanced/revanced-patches/issues/6253)) ([da4cf94](https://github.com/ReVanced/revanced-patches/commit/da4cf940911a4406e2c9dd558b60305385a80c61))
# [5.46.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.8...v5.46.0-dev.9) (2025-11-09)
### Features
* **YouTube Music:** Add `Change miniplayer color` patch ([#6259](https://github.com/ReVanced/revanced-patches/issues/6259)) ([ab808ae](https://github.com/ReVanced/revanced-patches/commit/ab808aeb773592cb26c848d8456478a346ec3bad))
# [5.46.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.7...v5.46.0-dev.8) (2025-11-09)
### Features
* **YouTube Music:** Add `Hide buttons` patch ([#6255](https://github.com/ReVanced/revanced-patches/issues/6255)) ([7a18ebc](https://github.com/ReVanced/revanced-patches/commit/7a18ebc7ab74ba30c5d5284a4856c55cdfc31097))
# [5.46.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.6...v5.46.0-dev.7) (2025-11-08)
### Bug Fixes
* **YouTube - Settings:** Add additional languages to ReVanced language preference ([d390b54](https://github.com/ReVanced/revanced-patches/commit/d390b54dab92d75b4e0d3e38344eae489dd69d98))
# [5.46.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.5...v5.46.0-dev.6) (2025-11-08)
### Features
* **YouTube - Debugging:** Add setting to block experimental client flags ([#6196](https://github.com/ReVanced/revanced-patches/issues/6196)) ([2e9d695](https://github.com/ReVanced/revanced-patches/commit/2e9d6959c94df7588b9e34b18770e9f437e91926))
# [5.46.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.4...v5.46.0-dev.5) (2025-11-07)
### Bug Fixes
* **Duolingo - Disable ads:** Constrain patch to last working app target ([f238ae9](https://github.com/ReVanced/revanced-patches/commit/f238ae9895000f01d1dccb800cc8efde0d5362bd))
* **Instagram - Hide navigation buttons:** Constrain patch to last working app target ([e030e9c](https://github.com/ReVanced/revanced-patches/commit/e030e9c07a7748e117ac44f6776a9f6317b20623))
* **Spotify - Hide Create button:** Remove obsolete patch that is no longer needed ([#6252](https://github.com/ReVanced/revanced-patches/issues/6252)) ([59d85b2](https://github.com/ReVanced/revanced-patches/commit/59d85b28a7fcb285ff5f2bb6ae654020d76b2019))
# [5.46.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.46.0-dev.3...v5.46.0-dev.4) (2025-11-07)

View File

@@ -97,9 +97,9 @@ Thank you for considering contributing to ReVanced Patches. You can find the con
To build ReVanced Patches, you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
## 📜 Licence
## 📜 License
ReVanced Patches is licensed under the GPLv3 license. Please see the [license file](LICENSE) for more information.
[tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced Patches as long as you track changes/dates in source files.
Any modifications to ReVanced Patches must also be made available under the GPL,
along with build & install instructions.
along with build & install instructions.

View File

@@ -1,8 +1,9 @@
project_id_env: "CROWDIN_PROJECT_ID"
api_token_env: "CROWDIN_PERSONAL_TOKEN"
preserve_hierarchy: false
preserve_hierarchy: true
files:
- source: patches/src/main/resources/addresources/values/strings.xml
dest: patches.xml
translation: patches/src/main/resources/addresources/values-%android_code%/strings.xml
skip_untranslated_strings: true

View File

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

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,8 @@
package com.google.android.play.core.integrity.protocol;
import android.os.Bundle;
import com.google.android.play.core.integrity.protocol.IExpressIntegrityServiceCallback;
interface IExpressIntegrityService {
oneway void requestIntegrityToken(in Bundle request, IExpressIntegrityServiceCallback callback) = 2;
}

View File

@@ -0,0 +1,5 @@
package com.google.android.play.core.integrity.protocol;
interface IExpressIntegrityServiceCallback {
oneway void onRequestExpressIntegrityTokenResult(in Bundle result) = 2;
}

View File

@@ -0,0 +1,8 @@
package com.google.android.play.core.integrity.protocol;
import android.os.Bundle;
import com.google.android.play.core.integrity.protocol.IIntegrityServiceCallback;
interface IIntegrityService {
oneway void requestIntegrityToken(in Bundle request, IIntegrityServiceCallback callback) = 1;
}

View File

@@ -0,0 +1,7 @@
package com.google.android.play.core.integrity.protocol;
import android.os.Bundle;
interface IIntegrityServiceCallback {
oneway void onResult(in Bundle result) = 1;
}

View File

@@ -0,0 +1,10 @@
package android.ext;
/** @hide */
// Int values that are assigned to packages in this interface can be retrieved at runtime from
// ApplicationInfo.ext().getPackageId() or from AndroidPackage.ext().getPackageId() (in system_server).
//
// PackageIds are assigned to parsed APKs only after they are verified, either by a certificate check
// or by a check that the APK is stored on an immutable OS partition.
public interface PackageId {
String PLAY_STORE_NAME = "com.android.vending";
}

View File

@@ -0,0 +1,62 @@
package android.os;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.FileDescriptor;
/** @hide */
public class BinderWrapper implements IBinder {
protected final IBinder base;
public BinderWrapper(IBinder base) {
this.base = base;
}
@Override
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
return base.transact(code, data, reply, flags);
}
@Nullable
@Override
public IInterface queryLocalInterface(@NonNull String descriptor) {
return base.queryLocalInterface(descriptor);
}
@Nullable
@Override
public String getInterfaceDescriptor() throws RemoteException {
return base.getInterfaceDescriptor();
}
@Override
public boolean pingBinder() {
return base.pingBinder();
}
@Override
public boolean isBinderAlive() {
return base.isBinderAlive();
}
@Override
public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException {
base.dump(fd, args);
}
@Override
public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException {
base.dumpAsync(fd, args);
}
@Override
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException {
base.linkToDeath(recipient, flags);
}
@Override
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
return base.unlinkToDeath(recipient, flags);
}
}

View File

@@ -0,0 +1,41 @@
package app.grapheneos.gmscompat.lib.playintegrity;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.os.FakeBackgroundHandler;
import com.google.android.play.core.integrity.protocol.IIntegrityService;
import com.google.android.play.core.integrity.protocol.IIntegrityServiceCallback;
class ClassicPlayIntegrityServiceWrapper extends PlayIntegrityServiceWrapper {
ClassicPlayIntegrityServiceWrapper(IBinder base) {
super(base);
requestIntegrityTokenTxnCode = 2; // IIntegrityService.Stub.TRANSACTION_requestIntegrityToken
}
static class TokenRequestStub extends IIntegrityService.Stub {
public void requestIntegrityToken(Bundle request, IIntegrityServiceCallback callback) {
Runnable r = () -> {
var result = new Bundle();
// https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityErrorCode.html#API_NOT_AVAILABLE
final int API_NOT_AVAILABLE = -1;
result.putInt("error", API_NOT_AVAILABLE);
try {
callback.onResult(result);
} catch (RemoteException e) {
Log.e("IIntegrityService.Stub", "", e);
}
};
FakeBackgroundHandler.getHandler().postDelayed(r, getTokenRequestResultDelay());
}
};
@Override
protected Binder createTokenRequestStub() {
return new TokenRequestStub();
}
}

View File

@@ -0,0 +1,48 @@
package app.grapheneos.gmscompat.lib.playintegrity;
import android.os.Binder;
import android.os.BinderWrapper;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Nullable;
abstract class PlayIntegrityServiceWrapper extends BinderWrapper {
final String TAG;
protected int requestIntegrityTokenTxnCode;
public PlayIntegrityServiceWrapper(IBinder base) {
super(base);
TAG = getClass().getSimpleName();
}
protected abstract Binder createTokenRequestStub();
@Override
public boolean transact(int code, Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (code == requestIntegrityTokenTxnCode) {
if (maybeStubOutIntegrityTokenRequest(code, data, reply, flags)) {
return true;
}
}
return super.transact(code, data, reply, flags);
}
private boolean maybeStubOutIntegrityTokenRequest(int code, Parcel data, @Nullable Parcel reply, int flags) {
Log.d(TAG, "integrity token request detected");
try {
createTokenRequestStub().transact(code, data, reply, flags);
} catch (RemoteException e) {
// this is a local call
throw new IllegalStateException(e);
}
return true;
}
protected static long getTokenRequestResultDelay() {
return 500L;
}
}

View File

@@ -0,0 +1,35 @@
package app.grapheneos.gmscompat.lib.playintegrity;
import android.content.Intent;
import android.content.ServiceConnection;
import android.ext.PackageId;
import android.os.IBinder;
import androidx.annotation.Nullable;
import app.grapheneos.gmscompat.lib.util.ServiceConnectionWrapper;
import java.util.function.UnaryOperator;
public class PlayIntegrityUtils {
public static @Nullable ServiceConnection maybeReplaceServiceConnection(Intent service, ServiceConnection orig) {
if (PackageId.PLAY_STORE_NAME.equals(service.getPackage())) {
UnaryOperator<IBinder> binderOverride = null;
final String CLASSIC_SERVICE =
"com.google.android.play.core.integrityservice.BIND_INTEGRITY_SERVICE";
final String STANDARD_SERVICE =
"com.google.android.play.core.expressintegrityservice.BIND_EXPRESS_INTEGRITY_SERVICE";
String action = service.getAction();
if (STANDARD_SERVICE.equals(action)) {
binderOverride = StandardPlayIntegrityServiceWrapper::new;
} else if (CLASSIC_SERVICE.equals(action)) {
binderOverride = ClassicPlayIntegrityServiceWrapper::new;
}
if (binderOverride != null) {
return new ServiceConnectionWrapper(orig, binderOverride);
}
}
return null;
}
}

View File

@@ -0,0 +1,42 @@
package app.grapheneos.gmscompat.lib.playintegrity;
import android.annotation.SuppressLint;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.os.FakeBackgroundHandler;
import com.google.android.play.core.integrity.protocol.IExpressIntegrityService;
import com.google.android.play.core.integrity.protocol.IExpressIntegrityServiceCallback;
@SuppressLint("LongLogTag")
class StandardPlayIntegrityServiceWrapper extends PlayIntegrityServiceWrapper {
StandardPlayIntegrityServiceWrapper(IBinder base) {
super(base);
requestIntegrityTokenTxnCode = 3; // IExpressIntegrityService.Stub.TRANSACTION_requestIntegrityToken
}
static class TokenRequestStub extends IExpressIntegrityService.Stub {
public void requestIntegrityToken(Bundle request, IExpressIntegrityServiceCallback callback) {
Runnable r = () -> {
var result = new Bundle();
// https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/StandardIntegrityErrorCode.html#API_NOT_AVAILABLE
final int API_NOT_AVAILABLE = -1;
result.putInt("error", API_NOT_AVAILABLE);
try {
callback.onRequestExpressIntegrityTokenResult(result);
} catch (RemoteException e) {
Log.e("IExpressIntegrityService.Stub", "", e);
}
};
FakeBackgroundHandler.getHandler().postDelayed(r, getTokenRequestResultDelay());
}
};
@Override
protected Binder createTokenRequestStub() {
return new TokenRequestStub();
}
}

View File

@@ -0,0 +1,49 @@
package app.grapheneos.gmscompat.lib.util;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.IBinder;
import java.util.function.UnaryOperator;
public class ServiceConnectionWrapper implements ServiceConnection {
private final ServiceConnection base;
private final UnaryOperator<IBinder> binderOverride;
public ServiceConnectionWrapper(ServiceConnection base, UnaryOperator<IBinder> binderOverride) {
this.base = base;
this.binderOverride = binderOverride;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
IBinder override = binderOverride.apply(service);
if (override != null) {
service = override;
}
}
base.onServiceConnected(name, service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
base.onServiceDisconnected(name);
}
@Override
public void onBindingDied(ComponentName name) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
base.onBindingDied(name);
}
}
@Override
public void onNullBinding(ComponentName name) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
base.onNullBinding(name);
}
}
}

View File

@@ -0,0 +1,17 @@
package app.revanced.extension.playintegrity;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import app.grapheneos.gmscompat.lib.playintegrity.PlayIntegrityUtils;
public class DisablePlayIntegrityPatch {
public static boolean bindService(Context context, Intent service, ServiceConnection conn, int flags) {
ServiceConnection override = PlayIntegrityUtils.maybeReplaceServiceConnection(service, conn);
if (override != null) {
conn = override;
}
return context.bindService(service, conn, flags);
}
}

View File

@@ -0,0 +1,11 @@
package com.android.internal.os;
import android.os.Handler;
import android.os.Looper;
public class FakeBackgroundHandler {
public static Handler getHandler() {
return new Handler(Looper.getMainLooper());
}
}

View File

@@ -0,0 +1,14 @@
package app.revanced.extension.music.patches;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class ChangeMiniplayerColorPatch {
/**
* Injection point
*/
public static boolean changeMiniplayerColor() {
return Settings.CHANGE_MINIPLAYER_COLOR.get();
}
}

View File

@@ -0,0 +1,49 @@
package app.revanced.extension.music.patches;
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
import android.view.View;
import android.view.ViewGroup;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class HideButtonsPatch {
/**
* Injection point
*/
public static int hideCastButton(int original) {
return Settings.HIDE_CAST_BUTTON.get() ? View.GONE : original;
}
/**
* Injection point
*/
public static void hideCastButton(View view) {
hideViewBy0dpUnderCondition(Settings.HIDE_CAST_BUTTON, view);
}
/**
* Injection point
*/
public static boolean hideHistoryButton(boolean original) {
return original && !Settings.HIDE_HISTORY_BUTTON.get();
}
/**
* Injection point
*/
public static void hideNotificationButton(View view) {
if (view.getParent() instanceof ViewGroup viewGroup) {
hideViewBy0dpUnderCondition(Settings.HIDE_NOTIFICATION_BUTTON, viewGroup);
}
}
/**
* Injection point
*/
public static void hideSearchButton(View view) {
hideViewBy0dpUnderCondition(Settings.HIDE_SEARCH_BUTTON, view);
}
}

View File

@@ -1,24 +0,0 @@
package app.revanced.extension.music.patches;
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
import android.view.View;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")
public class HideCastButtonPatch {
/**
* Injection point
*/
public static int hideCastButton(int original) {
return Settings.HIDE_CAST_BUTTON.get() ? View.GONE : original;
}
/**
* Injection point
*/
public static void hideCastButton(View view) {
hideViewBy0dpUnderCondition(Settings.HIDE_CAST_BUTTON, view);
}
}

View File

@@ -3,6 +3,7 @@ package app.revanced.extension.music.patches;
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
import android.view.View;
import app.revanced.extension.music.settings.Settings;
@SuppressWarnings("unused")

View File

@@ -11,6 +11,7 @@ import android.widget.Toolbar;
import app.revanced.extension.music.settings.preference.MusicPreferenceFragment;
import app.revanced.extension.music.settings.search.MusicSearchViewController;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseActivityHook;
@@ -46,15 +47,7 @@ public class MusicActivityHook extends BaseActivityHook {
// Override the default YouTube Music theme to increase start padding of list items.
// Custom style located in resources/music/values/style.xml
activity.setTheme(Utils.getResourceIdentifierOrThrow(
"Theme.ReVanced.YouTubeMusic.Settings", "style"));
}
/**
* Returns the resource ID for the YouTube Music settings layout.
*/
@Override
protected int getContentViewResourceId() {
return LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR;
ResourceType.STYLE, "Theme.ReVanced.YouTubeMusic.Settings"));
}
/**

View File

@@ -16,8 +16,11 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_GET_PREMIUM_LABEL = new BooleanSetting("revanced_music_hide_get_premium_label", TRUE, true);
// General
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_music_hide_cast_button", TRUE, false);
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_music_hide_cast_button", TRUE, true);
public static final BooleanSetting HIDE_CATEGORY_BAR = new BooleanSetting("revanced_music_hide_category_bar", FALSE, true);
public static final BooleanSetting HIDE_HISTORY_BUTTON = new BooleanSetting("revanced_music_hide_history_button", FALSE, true);
public static final BooleanSetting HIDE_SEARCH_BUTTON = new BooleanSetting("revanced_music_hide_search_button", FALSE, true);
public static final BooleanSetting HIDE_NOTIFICATION_BUTTON = new BooleanSetting("revanced_music_hide_notification_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_HOME_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_home_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_SAMPLES_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_samples_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BAR_EXPLORE_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_explore_button", FALSE, true);
@@ -27,6 +30,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_NAVIGATION_BAR_LABEL = new BooleanSetting("revanced_music_hide_navigation_bar_labels", FALSE, true);
// Player
public static final BooleanSetting CHANGE_MINIPLAYER_COLOR = new BooleanSetting("revanced_music_change_miniplayer_color", FALSE, true);
public static final BooleanSetting PERMANENT_REPEAT = new BooleanSetting("revanced_music_play_permanent_repeat", FALSE, true);
// Miscellaneous

View File

@@ -16,6 +16,7 @@ import java.util.Arrays;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.ui.Dim;
import com.amazon.video.sdk.player.Player;
@@ -64,9 +65,8 @@ public class PlaybackSpeedPatch {
SpeedIconDrawable speedIcon = new SpeedIconDrawable();
speedButton.setImageDrawable(speedIcon);
int buttonSize = Utils.dipToPixels(48);
speedButton.setMinimumWidth(buttonSize);
speedButton.setMinimumHeight(buttonSize);
speedButton.setMinimumWidth(Dim.dp48);
speedButton.setMinimumHeight(Dim.dp48);
return speedButton;
}
@@ -197,11 +197,11 @@ class SpeedIconDrawable extends Drawable {
@Override
public int getIntrinsicWidth() {
return Utils.dipToPixels(32);
return Dim.dp32;
}
@Override
public int getIntrinsicHeight() {
return Utils.dipToPixels(32);
return Dim.dp32;
}
}
}

View File

@@ -0,0 +1,57 @@
package app.revanced.extension.shared;
import java.util.HashMap;
import java.util.Map;
public enum ResourceType {
ANIM("anim"),
ANIMATOR("animator"),
ARRAY("array"),
ATTR("attr"),
BOOL("bool"),
COLOR("color"),
DIMEN("dimen"),
DRAWABLE("drawable"),
FONT("font"),
FRACTION("fraction"),
ID("id"),
INTEGER("integer"),
INTERPOLATOR("interpolator"),
LAYOUT("layout"),
MENU("menu"),
MIPMAP("mipmap"),
NAVIGATION("navigation"),
PLURALS("plurals"),
RAW("raw"),
STRING("string"),
STYLE("style"),
STYLEABLE("styleable"),
TRANSITION("transition"),
VALUES("values"),
XML("xml");
private static final Map<String, ResourceType> VALUE_MAP;
static {
ResourceType[] values = values();
VALUE_MAP = new HashMap<>(2 * values.length);
for (ResourceType type : values) {
VALUE_MAP.put(type.value, type);
}
}
public final String value;
public static ResourceType fromValue(String value) {
ResourceType type = VALUE_MAP.get(value);
if (type == null) {
throw new IllegalArgumentException("Unknown resource type: " + value);
}
return type;
}
ResourceType(String value) {
this.value = value;
}
}

View File

@@ -23,9 +23,7 @@ import android.os.Looper;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -49,8 +47,10 @@ import java.text.Collator;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
@@ -63,6 +63,7 @@ import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
import app.revanced.extension.shared.ui.Dim;
@SuppressWarnings("NewApi")
public class Utils {
@@ -81,6 +82,8 @@ public class Utils {
@Nullable
private static Boolean isDarkModeEnabled;
private static boolean appIsUsingBoldIcons;
// Cached Collator instance with its locale.
@Nullable
private static Locale cachedCollatorLocale;
@@ -153,12 +156,12 @@ public class Utils {
/**
* Hide a view by setting its layout height and width to 1dp.
*
* @param condition The setting to check for hiding the view.
* @param setting The setting to check for hiding the view.
* @param view The view to hide.
*/
public static void hideViewBy0dpUnderCondition(BooleanSetting condition, View view) {
if (hideViewBy0dpUnderCondition(condition.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + condition);
public static void hideViewBy0dpUnderCondition(BooleanSetting setting, View view) {
if (hideViewBy0dpUnderCondition(setting.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + setting);
}
}
@@ -170,22 +173,47 @@ public class Utils {
*/
public static boolean hideViewBy0dpUnderCondition(boolean condition, View view) {
if (condition) {
hideViewByLayoutParams(view);
hideViewBy0dp(view);
return true;
}
return false;
}
/**
* Hide a view by setting its layout params to 0x0
* @param view The view to hide.
*/
public static void hideViewBy0dp(View view) {
if (view instanceof LinearLayout) {
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams);
} else if (view instanceof FrameLayout) {
FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams2);
} else if (view instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams3);
} else if (view instanceof Toolbar) {
Toolbar.LayoutParams layoutParams4 = new Toolbar.LayoutParams(0, 0);
view.setLayoutParams(layoutParams4);
} else {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = 0;
params.height = 0;
view.setLayoutParams(params);
}
}
/**
* Hide a view by setting its visibility to GONE.
*
* @param condition The setting to check for hiding the view.
* @param setting The setting to check for hiding the view.
* @param view The view to hide.
*/
public static void hideViewUnderCondition(BooleanSetting condition, View view) {
if (hideViewUnderCondition(condition.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + condition);
public static void hideViewUnderCondition(BooleanSetting setting, View view) {
if (hideViewUnderCondition(setting.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + setting);
}
}
@@ -204,14 +232,14 @@ public class Utils {
return false;
}
public static void hideViewByRemovingFromParentUnderCondition(BooleanSetting condition, View view) {
if (hideViewByRemovingFromParentUnderCondition(condition.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + condition);
public static void hideViewByRemovingFromParentUnderCondition(BooleanSetting setting, View view) {
if (hideViewByRemovingFromParentUnderCondition(setting.get(), view)) {
Logger.printDebug(() -> "View hidden by setting: " + setting);
}
}
public static boolean hideViewByRemovingFromParentUnderCondition(boolean setting, View view) {
if (setting) {
public static boolean hideViewByRemovingFromParentUnderCondition(boolean condition, View view) {
if (condition) {
ViewParent parent = view.getParent();
if (parent instanceof ViewGroup parentGroup) {
parentGroup.removeView(view);
@@ -283,12 +311,13 @@ public class Utils {
* @return zero, if the resource is not found.
*/
@SuppressLint("DiscouragedApi")
public static int getResourceIdentifier(Context context, String resourceIdentifierName, @Nullable String type) {
return context.getResources().getIdentifier(resourceIdentifierName, type, context.getPackageName());
public static int getResourceIdentifier(Context context, @Nullable ResourceType type, String resourceIdentifierName) {
return context.getResources().getIdentifier(resourceIdentifierName,
type == null ? null : type.value, context.getPackageName());
}
public static int getResourceIdentifierOrThrow(Context context, String resourceIdentifierName, @Nullable String type) {
final int resourceId = getResourceIdentifier(context, resourceIdentifierName, type);
public static int getResourceIdentifierOrThrow(Context context, @Nullable ResourceType type, String resourceIdentifierName) {
final int resourceId = getResourceIdentifier(context, type, resourceIdentifierName);
if (resourceId == 0) {
throw new Resources.NotFoundException("No resource id exists with name: " + resourceIdentifierName
+ " type: " + type);
@@ -298,48 +327,44 @@ public class Utils {
/**
* @return zero, if the resource is not found.
* @see #getResourceIdentifierOrThrow(String, String)
* @see #getResourceIdentifierOrThrow(ResourceType, String)
*/
public static int getResourceIdentifier(String resourceIdentifierName, @Nullable String type) {
return getResourceIdentifier(getContext(), resourceIdentifierName, type);
public static int getResourceIdentifier(@Nullable ResourceType type, String resourceIdentifierName) {
return getResourceIdentifier(getContext(), type, resourceIdentifierName);
}
/**
* @return The resource identifier, or throws an exception if not found.
* @return zero, if the resource is not found.
* @see #getResourceIdentifier(ResourceType, String)
*/
public static int getResourceIdentifierOrThrow(String resourceIdentifierName, @Nullable String type) {
final int resourceId = getResourceIdentifier(getContext(), resourceIdentifierName, type);
if (resourceId == 0) {
throw new Resources.NotFoundException("No resource id exists with name: " + resourceIdentifierName
+ " type: " + type);
}
return resourceId;
public static int getResourceIdentifierOrThrow(@Nullable ResourceType type, String resourceIdentifierName) {
return getResourceIdentifierOrThrow(getContext(), type, resourceIdentifierName);
}
public static int getResourceInteger(String resourceIdentifierName) throws Resources.NotFoundException {
return getContext().getResources().getInteger(getResourceIdentifierOrThrow(resourceIdentifierName, "integer"));
return getContext().getResources().getInteger(getResourceIdentifierOrThrow(ResourceType.INTEGER, resourceIdentifierName));
}
public static Animation getResourceAnimation(String resourceIdentifierName) throws Resources.NotFoundException {
return AnimationUtils.loadAnimation(getContext(), getResourceIdentifierOrThrow(resourceIdentifierName, "anim"));
return AnimationUtils.loadAnimation(getContext(), getResourceIdentifierOrThrow(ResourceType.ANIM, resourceIdentifierName));
}
@ColorInt
public static int getResourceColor(String resourceIdentifierName) throws Resources.NotFoundException {
//noinspection deprecation
return getContext().getResources().getColor(getResourceIdentifierOrThrow(resourceIdentifierName, "color"));
return getContext().getResources().getColor(getResourceIdentifierOrThrow(ResourceType.COLOR, resourceIdentifierName));
}
public static int getResourceDimensionPixelSize(String resourceIdentifierName) throws Resources.NotFoundException {
return getContext().getResources().getDimensionPixelSize(getResourceIdentifierOrThrow(resourceIdentifierName, "dimen"));
return getContext().getResources().getDimensionPixelSize(getResourceIdentifierOrThrow(ResourceType.DIMEN, resourceIdentifierName));
}
public static float getResourceDimension(String resourceIdentifierName) throws Resources.NotFoundException {
return getContext().getResources().getDimension(getResourceIdentifierOrThrow(resourceIdentifierName, "dimen"));
return getContext().getResources().getDimension(getResourceIdentifierOrThrow(ResourceType.DIMEN, resourceIdentifierName));
}
public static String[] getResourceStringArray(String resourceIdentifierName) throws Resources.NotFoundException {
return getContext().getResources().getStringArray(getResourceIdentifierOrThrow(resourceIdentifierName, "array"));
return getContext().getResources().getStringArray(getResourceIdentifierOrThrow(ResourceType.ARRAY, resourceIdentifierName));
}
public interface MatchFilter<T> {
@@ -350,7 +375,7 @@ public class Utils {
* Includes sub children.
*/
public static <R extends View> R getChildViewByResourceName(View view, String str) {
var child = view.findViewById(Utils.getResourceIdentifierOrThrow(str, "id"));
var child = view.findViewById(Utils.getResourceIdentifierOrThrow(ResourceType.ID, str));
//noinspection unchecked
return (R) child;
}
@@ -760,31 +785,25 @@ public class Utils {
}
/**
* Hide a view by setting its layout params to 0x0
* @param view The view to hide.
* Hides a view by setting its layout width and height to 0dp.
* Handles null layout params safely.
*
* @param view The view to hide. If null, does nothing.
*/
public static void hideViewByLayoutParams(View view) {
if (view instanceof LinearLayout) {
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams);
} else if (view instanceof FrameLayout) {
FrameLayout.LayoutParams layoutParams2 = new FrameLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams2);
} else if (view instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(0, 0);
view.setLayoutParams(layoutParams3);
} else if (view instanceof Toolbar) {
Toolbar.LayoutParams layoutParams4 = new Toolbar.LayoutParams(0, 0);
view.setLayoutParams(layoutParams4);
} else if (view instanceof ViewGroup) {
ViewGroup.LayoutParams layoutParams5 = new ViewGroup.LayoutParams(0, 0);
view.setLayoutParams(layoutParams5);
public static void hideViewByLayoutParams(@Nullable View view) {
if (view == null) return;
ViewGroup.LayoutParams params = view.getLayoutParams();
if (params == null) {
// Create generic 0x0 layout params accepted by all ViewGroups.
params = new ViewGroup.LayoutParams(0, 0);
} else {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = 0;
params.height = 0;
view.setLayoutParams(params);
}
view.setLayoutParams(params);
}
/**
@@ -801,13 +820,10 @@ public class Utils {
public static void setDialogWindowParameters(Window window, int gravity, int yOffsetDip, int widthPercentage, boolean dimAmount) {
WindowManager.LayoutParams params = window.getAttributes();
DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
int portraitWidth = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels);
params.width = (int) (portraitWidth * (widthPercentage / 100.0f)); // Set width based on parameters.
params.width = Dim.pctPortraitWidth(widthPercentage);
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.gravity = gravity;
params.y = yOffsetDip > 0 ? dipToPixels(yOffsetDip) : 0;
params.y = yOffsetDip > 0 ? Dim.dp(yOffsetDip) : 0;
if (dimAmount) {
params.dimAmount = 0f;
}
@@ -817,15 +833,18 @@ public class Utils {
}
/**
* Creates an array of corner radii for a rounded rectangle shape.
*
* @param dp Radius in density-independent pixels (dip) to apply to all corners.
* @return An array of eight float values representing the corner radii
* (top-left, top-right, bottom-right, bottom-left).
* @return If the unpatched app is currently using bold icons.
*/
public static float[] createCornerRadii(float dp) {
final float radius = dipToPixels(dp);
return new float[]{radius, radius, radius, radius, radius, radius, radius, radius};
public static boolean appIsUsingBoldIcons() {
return appIsUsingBoldIcons;
}
/**
* Controls if ReVanced bold icons are shown in various places.
* @param boldIcons If the app is currently using bold icons.
*/
public static void setAppIsUsingBoldIcons(boolean boldIcons) {
appIsUsingBoldIcons = boldIcons;
}
/**
@@ -1132,42 +1151,6 @@ public class Utils {
return getResourceColor(colorString);
}
/**
* Converts dip value to actual device pixels.
*
* @param dip The density-independent pixels value.
* @return The device pixel value.
*/
public static int dipToPixels(float dip) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dip,
Resources.getSystem().getDisplayMetrics()
);
}
/**
* Converts a percentage of the screen height to actual device pixels.
*
* @param percentage The percentage of the screen height (e.g., 30 for 30%).
* @return The device pixel value corresponding to the percentage of screen height.
*/
public static int percentageHeightToPixels(int percentage) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int) (metrics.heightPixels * (percentage / 100.0f));
}
/**
* Converts a percentage of the screen width to actual device pixels.
*
* @param percentage The percentage of the screen width (e.g., 30 for 30%).
* @return The device pixel value corresponding to the percentage of screen width.
*/
public static int percentageWidthToPixels(int percentage) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int) (metrics.widthPixels * (percentage / 100.0f));
}
/**
* Uses {@link #adjustColorBrightness(int, float)} depending if light or dark mode is active.
*/
@@ -1225,4 +1208,18 @@ public class Utils {
public static float clamp(float value, float lower, float upper) {
return Math.max(lower, Math.min(value, upper));
}
/**
* @param maxSize The maximum number of elements to keep in the map.
* @return A {@link LinkedHashMap} that automatically evicts the oldest entry
* when the size exceeds {@code maxSize}.
*/
public static <T, V> Map<T, V> createSizeRestrictedMap(int maxSize) {
return new LinkedHashMap<>(2 * maxSize) {
@Override
protected boolean removeEldestEntry(Entry eldest) {
return size() > maxSize;
}
};
}
}

View File

@@ -23,6 +23,7 @@ import androidx.annotation.Nullable;
import java.util.Collection;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.ui.CustomDialog;
@@ -128,7 +129,7 @@ abstract class Check {
// Add icon to the dialog.
ImageView iconView = new ImageView(activity);
iconView.setImageResource(Utils.getResourceIdentifierOrThrow(
"revanced_ic_dialog_alert", "drawable"));
ResourceType.DRAWABLE, "revanced_ic_dialog_alert"));
iconView.setColorFilter(Utils.getAppForegroundColor(), PorterDuff.Mode.SRC_IN);
iconView.setPadding(0, 0, 0, 0);
LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(

View File

@@ -15,7 +15,6 @@ import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public abstract class BaseFixRedgifsApiPatch implements Interceptor {
protected static BaseFixRedgifsApiPatch INSTANCE;
public abstract String getDefaultUserAgent();

View File

@@ -5,6 +5,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
@@ -12,6 +13,7 @@ import java.util.Locale;
import app.revanced.extension.shared.GmsCoreSupport;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
@@ -64,13 +66,24 @@ public class CustomBrandingPatch {
iconName += "_custom";
}
notificationSmallIcon = Utils.getResourceIdentifier(iconName, "drawable");
notificationSmallIcon = Utils.getResourceIdentifier(ResourceType.DRAWABLE, iconName);
if (notificationSmallIcon == 0) {
Logger.printException(() -> "Could not load notification small icon");
}
}
}
/**
* Injection point.
*/
public static View getLottieViewOrNull(View lottieStartupView) {
if (BaseSettings.CUSTOM_BRANDING_ICON.get() == BrandingTheme.ORIGINAL) {
return lottieStartupView;
}
return null;
}
/**
* Injection point.
*/

View File

@@ -1,5 +1,9 @@
package app.revanced.extension.shared.patches;
import static java.lang.Boolean.TRUE;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -21,12 +25,28 @@ public final class EnableDebuggingPatch {
? new ConcurrentHashMap<>(800, 0.5f, 1)
: null;
private static final Set<Long> DISABLED_FEATURE_FLAGS = parseFlags(BaseSettings.DISABLED_FEATURE_FLAGS.get());
// Log all disabled flags on app startup.
static {
if (LOG_FEATURE_FLAGS && !DISABLED_FEATURE_FLAGS.isEmpty()) {
StringBuilder sb = new StringBuilder("Disabled feature flags:\n");
for (Long flag : DISABLED_FEATURE_FLAGS) {
sb.append(" ").append(flag).append('\n');
}
Logger.printDebug(sb::toString);
}
}
/**
* Injection point.
*/
public static boolean isBooleanFeatureFlagEnabled(boolean value, Long flag) {
if (LOG_FEATURE_FLAGS && value) {
if (featureFlags.putIfAbsent(flag, true) == null) {
if (DISABLED_FEATURE_FLAGS.contains(flag)) {
return false;
}
if (featureFlags.putIfAbsent(flag, TRUE) == null) {
Logger.printDebug(() -> "boolean feature is enabled: " + flag);
}
}
@@ -70,10 +90,44 @@ public final class EnableDebuggingPatch {
if (LOG_FEATURE_FLAGS && !defaultValue.equals(value)) {
if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> " string feature is enabled: " + flag
+ " value: " + value + (defaultValue.isEmpty() ? "" : " default: " + defaultValue));
+ " value: " + value + (defaultValue.isEmpty() ? "" : " default: " + defaultValue));
}
}
return value;
}
/**
* Get all logged feature flags.
* @return Set of all known flags
*/
public static Set<Long> getAllLoggedFlags() {
if (featureFlags != null) {
return new HashSet<>(featureFlags.keySet());
}
return new HashSet<>();
}
/**
* Public method for parsing flags.
* @param flags String containing newline-separated flag IDs
* @return Set of parsed flag IDs
*/
public static Set<Long> parseFlags(String flags) {
Set<Long> parsedFlags = new HashSet<>();
if (!flags.isBlank()) {
for (String flag : flags.split("\n")) {
String trimmedFlag = flag.trim();
if (trimmedFlag.isEmpty()) continue; // Skip empty lines.
try {
parsedFlags.add(Long.parseLong(trimmedFlag));
} catch (NumberFormatException e) {
Logger.printException(() -> "Invalid flag ID: " + flag);
}
}
}
return parsedFlags;
}
}

View File

@@ -35,6 +35,15 @@ public class LinkSanitizer {
public Uri sanitizeUri(Uri uri) {
try {
String scheme = uri.getScheme();
if (scheme == null || !(scheme.equals("http") || scheme.equals("https"))) {
// Opening YouTube share sheet 'other' option passes the video title as a URI.
// Checking !uri.isHierarchical() works for all cases, except if the
// video title starts with / and then it's hierarchical but still an invalid URI.
Logger.printDebug(() -> "Ignoring uri: " + uri);
return uri;
}
Uri.Builder builder = uri.buildUpon().clearQuery();
if (!parametersToRemove.isEmpty()) {

View File

@@ -7,15 +7,16 @@ import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceFragment;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toolbar;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment;
import app.revanced.extension.shared.ui.Dim;
/**
* Base class for hooking activities to inject a custom PreferenceFragment with a toolbar.
@@ -25,13 +26,13 @@ import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragme
public abstract class BaseActivityHook extends Activity {
private static final int ID_REVANCED_SETTINGS_FRAGMENTS =
getResourceIdentifierOrThrow("revanced_settings_fragments", "id");
getResourceIdentifierOrThrow(ResourceType.ID, "revanced_settings_fragments");
private static final int ID_REVANCED_TOOLBAR_PARENT =
getResourceIdentifierOrThrow("revanced_toolbar_parent", "id");
getResourceIdentifierOrThrow(ResourceType.ID, "revanced_toolbar_parent");
public static final int LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR =
getResourceIdentifierOrThrow("revanced_settings_with_toolbar", "layout");
getResourceIdentifierOrThrow(ResourceType.LAYOUT, "revanced_settings_with_toolbar");
private static final int STRING_REVANCED_SETTINGS_TITLE =
getResourceIdentifierOrThrow("revanced_settings_title", "string");
getResourceIdentifierOrThrow(ResourceType.STRING, "revanced_settings_title");
/**
* Layout parameters for the toolbar, extracted from the dummy toolbar.
@@ -109,13 +110,12 @@ public abstract class BaseActivityHook extends Activity {
toolbar.setNavigationOnClickListener(getNavigationClickListener(activity));
toolbar.setTitle(STRING_REVANCED_SETTINGS_TITLE);
final int margin = Utils.dipToPixels(16);
toolbar.setTitleMarginStart(margin);
toolbar.setTitleMarginEnd(margin);
toolbar.setTitleMarginStart(Dim.dp16);
toolbar.setTitleMarginEnd(Dim.dp16);
TextView toolbarTextView = Utils.getChildView(toolbar, false, view -> view instanceof TextView);
if (toolbarTextView != null) {
toolbarTextView.setTextColor(Utils.getAppForegroundColor());
toolbarTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
toolbarTextView.setTextSize(20);
}
setToolbarLayoutParams(toolbar);
@@ -124,16 +124,18 @@ public abstract class BaseActivityHook extends Activity {
toolBarParent.addView(toolbar, 0);
}
/**
* Returns the resource ID for the content view layout.
*/
protected int getContentViewResourceId() {
return LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR;
}
/**
* Customizes the activity's theme.
*/
protected abstract void customizeActivityTheme(Activity activity);
/**
* Returns the resource ID for the content view layout.
*/
protected abstract int getContentViewResourceId();
/**
* Returns the background color for the toolbar.
*/

View File

@@ -5,6 +5,8 @@ import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.patches.CustomBrandingPatch.BrandingTheme;
import static app.revanced.extension.shared.settings.Setting.parent;
import app.revanced.extension.shared.Logger;
/**
* Settings shared across multiple apps.
* <p>
@@ -24,10 +26,19 @@ public class BaseSettings {
* Use the icons declared in the preferences created during patching. If no icons or styles are declared then this setting does nothing.
*/
public static final BooleanSetting SHOW_MENU_ICONS = new BooleanSetting("revanced_show_menu_icons", TRUE, true);
/**
* Do not use this setting directly. Instead use {@link app.revanced.extension.shared.Utils#appIsUsingBoldIcons()}
*/
public static final BooleanSetting SETTINGS_DISABLE_BOLD_ICONS = new BooleanSetting("revanced_settings_disable_bold_icons", FALSE, true);
public static final BooleanSetting SETTINGS_SEARCH_HISTORY = new BooleanSetting("revanced_settings_search_history", TRUE, true);
public static final StringSetting SETTINGS_SEARCH_ENTRIES = new StringSetting("revanced_settings_search_entries", "");
/**
* The first time the app was launched with no previous app data (either a clean install, or after wiping app data).
*/
public static final LongSetting FIRST_TIME_APP_LAUNCHED = new LongSetting("revanced_last_time_app_was_launched", -1L, false, false);
//
// Settings shared by YouTube and YouTube Music.
//
@@ -42,4 +53,15 @@ public class BaseSettings {
public static final EnumSetting<BrandingTheme> CUSTOM_BRANDING_ICON = new EnumSetting<>("revanced_custom_branding_icon", BrandingTheme.ORIGINAL, true);
public static final IntegerSetting CUSTOM_BRANDING_NAME = new IntegerSetting("revanced_custom_branding_name", 1, true);
public static final StringSetting DISABLED_FEATURE_FLAGS = new StringSetting("revanced_disabled_feature_flags", "", true, parent(DEBUG));
static {
final long now = System.currentTimeMillis();
if (FIRST_TIME_APP_LAUNCHED.get() < 0) {
Logger.printInfo(() -> "First launch of installation with no prior app data");
FIRST_TIME_APP_LAUNCHED.save(now);
}
}
}

View File

@@ -58,6 +58,23 @@ public abstract class Setting<T> {
};
}
/**
* Availability based on a single parent setting being disabled.
*/
public static Availability parentNot(BooleanSetting parent) {
return new Availability() {
@Override
public boolean isAvailable() {
return !parent.get();
}
@Override
public List<Setting<?>> getParentSettings() {
return Collections.singletonList(parent);
}
};
}
/**
* Availability based on all parents being enabled.
*/

View File

@@ -23,6 +23,7 @@ import androidx.annotation.Nullable;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
@@ -103,10 +104,16 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
* so all app specific {@link Setting} instances are loaded before this method returns.
*/
protected void initialize() {
String preferenceResourceName = BaseSettings.SHOW_MENU_ICONS.get()
? "revanced_prefs_icons"
: "revanced_prefs";
final var identifier = Utils.getResourceIdentifier(preferenceResourceName, "xml");
String preferenceResourceName;
if (BaseSettings.SHOW_MENU_ICONS.get()) {
preferenceResourceName = Utils.appIsUsingBoldIcons()
? "revanced_prefs_icons_bold"
: "revanced_prefs_icons";
} else {
preferenceResourceName = "revanced_prefs";
}
final var identifier = Utils.getResourceIdentifier(ResourceType.XML, preferenceResourceName);
if (identifier == 0) return;
addPreferencesFromResource(identifier);

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.shared.Utils.getResourceIdentifierOrThrow;
import android.app.Dialog;
@@ -32,11 +31,13 @@ import java.util.Locale;
import java.util.regex.Pattern;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.shared.ui.ColorDot;
import app.revanced.extension.shared.ui.CustomDialog;
import app.revanced.extension.shared.ui.Dim;
/**
* A custom preference for selecting a color via a hexadecimal code or a color picker dialog.
@@ -81,13 +82,13 @@ public class ColorPickerPreference extends EditTextPreference {
private boolean opacitySliderEnabled = false;
public static final int ID_REVANCED_COLOR_PICKER_VIEW =
getResourceIdentifierOrThrow("revanced_color_picker_view", "id");
getResourceIdentifierOrThrow(ResourceType.ID, "revanced_color_picker_view");
public static final int ID_PREFERENCE_COLOR_DOT =
getResourceIdentifierOrThrow("preference_color_dot", "id");
getResourceIdentifierOrThrow(ResourceType.ID, "preference_color_dot");
public static final int LAYOUT_REVANCED_COLOR_DOT_WIDGET =
getResourceIdentifierOrThrow("revanced_color_dot_widget", "layout");
getResourceIdentifierOrThrow(ResourceType.LAYOUT, "revanced_color_dot_widget");
public static final int LAYOUT_REVANCED_COLOR_PICKER =
getResourceIdentifierOrThrow("revanced_color_picker", "layout");
getResourceIdentifierOrThrow(ResourceType.LAYOUT, "revanced_color_picker");
/**
* Removes non valid hex characters, converts to all uppercase,
@@ -310,11 +311,8 @@ public class ColorPickerPreference extends EditTextPreference {
inputLayout.setGravity(Gravity.CENTER_VERTICAL);
dialogColorDot = new View(context);
LinearLayout.LayoutParams previewParams = new LinearLayout.LayoutParams(
dipToPixels(20),
dipToPixels(20)
);
previewParams.setMargins(dipToPixels(16), 0, dipToPixels(10), 0);
LinearLayout.LayoutParams previewParams = new LinearLayout.LayoutParams(Dim.dp20,Dim.dp20);
previewParams.setMargins(Dim.dp16, 0, Dim.dp10, 0);
dialogColorDot.setLayoutParams(previewParams);
inputLayout.addView(dialogColorDot);
updateDialogColorDot();

View File

@@ -1,6 +1,5 @@
package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.shared.settings.preference.ColorPickerPreference.getColorString;
import android.annotation.SuppressLint;
@@ -21,6 +20,7 @@ import androidx.annotation.ColorInt;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.ui.Dim;
/**
* A custom color picker view that allows the user to select a color using a hue slider, a saturation-value selector
@@ -54,28 +54,28 @@ public class ColorPickerView extends View {
}
/** Expanded touch area for the hue and opacity bars to increase the touch-sensitive area. */
public static final float TOUCH_EXPANSION = dipToPixels(20f);
public static final float TOUCH_EXPANSION = Dim.dp20;
/** Margin between different areas of the view (saturation-value selector, hue bar, and opacity slider). */
private static final float MARGIN_BETWEEN_AREAS = dipToPixels(24);
private static final float MARGIN_BETWEEN_AREAS = Dim.dp24;
/** Padding around the view. */
private static final float VIEW_PADDING = dipToPixels(16);
private static final float VIEW_PADDING = Dim.dp16;
/** Height of the hue bar. */
private static final float HUE_BAR_HEIGHT = dipToPixels(12);
private static final float HUE_BAR_HEIGHT = Dim.dp12;
/** Height of the opacity slider. */
private static final float OPACITY_BAR_HEIGHT = dipToPixels(12);
private static final float OPACITY_BAR_HEIGHT = Dim.dp12;
/** Corner radius for the hue bar. */
private static final float HUE_CORNER_RADIUS = dipToPixels(6);
private static final float HUE_CORNER_RADIUS = Dim.dp6;
/** Corner radius for the opacity slider. */
private static final float OPACITY_CORNER_RADIUS = dipToPixels(6);
private static final float OPACITY_CORNER_RADIUS = Dim.dp6;
/** Radius of the selector handles. */
private static final float SELECTOR_RADIUS = dipToPixels(12);
private static final float SELECTOR_RADIUS = Dim.dp12;
/** Stroke width for the selector handle outlines. */
private static final float SELECTOR_STROKE_WIDTH = 8;
@@ -202,7 +202,7 @@ public class ColorPickerView extends View {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final float DESIRED_ASPECT_RATIO = 0.8f; // height = width * 0.8
final int minWidth = dipToPixels(250);
final int minWidth = Dim.dp(250);
final int minHeight = (int) (minWidth * DESIRED_ASPECT_RATIO) + (int) (HUE_BAR_HEIGHT + MARGIN_BETWEEN_AREAS)
+ (opacitySliderEnabled ? (int) (OPACITY_BAR_HEIGHT + MARGIN_BETWEEN_AREAS) : 0);

View File

@@ -20,6 +20,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.ui.CustomDialog;
@@ -30,14 +31,18 @@ import app.revanced.extension.shared.ui.CustomDialog;
@SuppressWarnings({"unused", "deprecation"})
public class CustomDialogListPreference extends ListPreference {
public static final int ID_REVANCED_CHECK_ICON =
getResourceIdentifierOrThrow("revanced_check_icon", "id");
public static final int ID_REVANCED_CHECK_ICON_PLACEHOLDER =
getResourceIdentifierOrThrow("revanced_check_icon_placeholder", "id");
public static final int ID_REVANCED_ITEM_TEXT =
getResourceIdentifierOrThrow("revanced_item_text", "id");
public static final int LAYOUT_REVANCED_CUSTOM_LIST_ITEM_CHECKED =
getResourceIdentifierOrThrow("revanced_custom_list_item_checked", "layout");
public static final int ID_REVANCED_CHECK_ICON = getResourceIdentifierOrThrow(
ResourceType.ID, "revanced_check_icon");
public static final int ID_REVANCED_CHECK_ICON_PLACEHOLDER = getResourceIdentifierOrThrow(
ResourceType.ID, "revanced_check_icon_placeholder");
public static final int ID_REVANCED_ITEM_TEXT = getResourceIdentifierOrThrow(
ResourceType.ID, "revanced_item_text");
public static final int LAYOUT_REVANCED_CUSTOM_LIST_ITEM_CHECKED = getResourceIdentifierOrThrow(
ResourceType.LAYOUT, "revanced_custom_list_item_checked");
public static final int DRAWABLE_CHECKMARK = getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, "revanced_settings_custom_checkmark");
public static final int DRAWABLE_CHECKMARK_BOLD = getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, "revanced_settings_custom_checkmark_bold");
private String staticSummary = null;
private CharSequence[] highlightedEntriesForDialog = null;
@@ -125,9 +130,13 @@ public class CustomDialogListPreference extends ListPreference {
LayoutInflater inflater = LayoutInflater.from(getContext());
view = inflater.inflate(layoutResourceId, parent, false);
holder = new SubViewDataContainer();
holder.checkIcon = view.findViewById(ID_REVANCED_CHECK_ICON);
holder.placeholder = view.findViewById(ID_REVANCED_CHECK_ICON_PLACEHOLDER);
holder.itemText = view.findViewById(ID_REVANCED_ITEM_TEXT);
holder.checkIcon = view.findViewById(ID_REVANCED_CHECK_ICON);
holder.checkIcon.setImageResource(Utils.appIsUsingBoldIcons()
? DRAWABLE_CHECKMARK_BOLD
: DRAWABLE_CHECKMARK
);
view.setTag(holder);
} else {
holder = (SubViewDataContainer) view.getTag();

View File

@@ -0,0 +1,625 @@
package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.getResourceIdentifierOrThrow;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.preference.Preference;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.SparseBooleanArray;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Space;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.patches.EnableDebuggingPatch;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.ui.CustomDialog;
import app.revanced.extension.shared.ui.Dim;
/**
* A custom preference that opens a dialog for managing feature flags.
* Allows moving boolean flags between active and blocked states with advanced selection.
*/
@SuppressWarnings({"deprecation", "unused"})
public class FeatureFlagsManagerPreference extends Preference {
private static final int DRAWABLE_REVANCED_SETTINGS_SELECT_ALL =
getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_select_all");
private static final int DRAWABLE_REVANCED_SETTINGS_DESELECT_ALL =
getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_deselect_all");
private static final int DRAWABLE_REVANCED_SETTINGS_COPY_ALL =
getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_copy_all");
private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_ONE =
getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_arrow_right_one");
private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_DOUBLE =
getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_arrow_right_double");
private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_ONE =
getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_arrow_left_one");
private static final int DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_DOUBLE =
getResourceIdentifierOrThrow(ResourceType.DRAWABLE, "revanced_settings_arrow_left_double");
/**
* Flags to hide from the UI.
*/
private static final Set<Long> FLAGS_TO_IGNORE = Set.of(
45386834L, // 'You' tab settings icon.
45685201L // Bold icons. Forcing off interferes with patch changes and YT icons are broken.
);
/**
* Tracks state for range selection in ListView.
*/
private static class ListViewSelectionState {
int lastClickedPosition = -1; // Position of the last clicked item.
boolean isRangeSelecting = false; // True while a range is being selected.
}
/**
* Helper class to pass ListView and Adapter together.
*/
private record ColumnViews(ListView listView, FlagAdapter adapter) {}
{
setOnPreferenceClickListener(pref -> {
showFlagsManagerDialog();
return true;
});
}
public FeatureFlagsManagerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public FeatureFlagsManagerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FeatureFlagsManagerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FeatureFlagsManagerPreference(Context context) {
super(context);
}
/**
* Shows the main dialog for managing feature flags.
*/
private void showFlagsManagerDialog() {
if (!BaseSettings.DEBUG.get()) {
Utils.showToastShort(str("revanced_debug_logs_disabled"));
return;
}
Context context = getContext();
// Load all known and disabled flags.
TreeSet<Long> allKnownFlags = new TreeSet<>(EnableDebuggingPatch.getAllLoggedFlags());
allKnownFlags.removeAll(FLAGS_TO_IGNORE);
TreeSet<Long> disabledFlags = new TreeSet<>(EnableDebuggingPatch.parseFlags(
BaseSettings.DISABLED_FEATURE_FLAGS.get()));
disabledFlags.removeAll(FLAGS_TO_IGNORE);
if (allKnownFlags.isEmpty() && disabledFlags.isEmpty()) {
// String does not need to be localized because it's basically impossible
// to reach the settings menu without encountering at least 1 flag.
Utils.showToastShort("No feature flags logged yet");
return;
}
TreeSet<Long> availableFlags = new TreeSet<>(allKnownFlags);
availableFlags.removeAll(disabledFlags);
TreeSet<Long> blockedFlags = new TreeSet<>(disabledFlags);
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
context,
getTitle() != null ? getTitle().toString() : "",
null,
null,
str("revanced_settings_save"),
() -> saveFlags(blockedFlags),
() -> {},
str("revanced_settings_reset"),
this::resetFlags,
true
);
LinearLayout mainLayout = dialogPair.second;
LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.0f);
// Insert content before the dialog button row.
View contentView = createContentView(context, availableFlags, blockedFlags);
mainLayout.addView(contentView, mainLayout.getChildCount() - 1, contentParams);
Dialog dialog = dialogPair.first;
dialog.show();
Window window = dialog.getWindow();
if (window != null) {
Utils.setDialogWindowParameters(window, Gravity.CENTER, 0, 100, false);
}
}
/**
* Creates the main content view with two columns.
*/
private View createContentView(Context context, TreeSet<Long> availableFlags, TreeSet<Long> blockedFlags) {
LinearLayout contentLayout = new LinearLayout(context);
contentLayout.setOrientation(LinearLayout.VERTICAL);
// Headers.
TextView availableHeader = createHeader(context, "revanced_debug_feature_flags_manager_active_header");
TextView blockedHeader = createHeader(context, "revanced_debug_feature_flags_manager_blocked_header");
LinearLayout headersLayout = new LinearLayout(context);
headersLayout.setOrientation(LinearLayout.HORIZONTAL);
headersLayout.addView(availableHeader, new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
headersLayout.addView(blockedHeader, new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
// Columns.
View leftColumn = createColumn(context, availableFlags, availableHeader);
View rightColumn = createColumn(context, blockedFlags, blockedHeader);
ColumnViews leftViews = (ColumnViews) leftColumn.getTag();
ColumnViews rightViews = (ColumnViews) rightColumn.getTag();
updateHeaderCount(availableHeader, leftViews.adapter);
updateHeaderCount(blockedHeader, rightViews.adapter);
// Main columns layout.
LinearLayout columnsLayout = new LinearLayout(context);
columnsLayout.setOrientation(LinearLayout.HORIZONTAL);
columnsLayout.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
columnsLayout.addView(leftColumn, new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
Space spaceBetweenColumns = new Space(context);
spaceBetweenColumns.setLayoutParams(new LinearLayout.LayoutParams(Dim.dp8, ViewGroup.LayoutParams.MATCH_PARENT));
columnsLayout.addView(spaceBetweenColumns);
columnsLayout.addView(rightColumn, new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
// Move buttons below columns.
Pair<LinearLayout, LinearLayout> moveButtons = createMoveButtons(context,
leftViews.listView, rightViews.listView,
availableFlags, blockedFlags, availableHeader, blockedHeader);
// Layout for buttons row.
LinearLayout buttonsRow = new LinearLayout(context);
buttonsRow.setOrientation(LinearLayout.HORIZONTAL);
buttonsRow.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
buttonsRow.addView(moveButtons.first, new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
Space spaceBetweenButtons = new Space(context);
spaceBetweenButtons.setLayoutParams(new LinearLayout.LayoutParams(Dim.dp8, ViewGroup.LayoutParams.WRAP_CONTENT));
buttonsRow.addView(spaceBetweenButtons);
buttonsRow.addView(moveButtons.second, new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
contentLayout.addView(headersLayout);
contentLayout.addView(columnsLayout);
contentLayout.addView(buttonsRow);
return contentLayout;
}
/**
* Creates a header TextView.
*/
private TextView createHeader(Context context, String tag) {
TextView textview = new TextView(context);
textview.setTag(tag);
textview.setTextSize(16);
textview.setTextColor(Utils.getAppForegroundColor());
textview.setGravity(Gravity.CENTER);
return textview;
}
/**
* Creates a single column (search + buttons + list).
*/
private View createColumn(Context context, TreeSet<Long> flags, TextView countText) {
LinearLayout wrapper = new LinearLayout(context);
wrapper.setOrientation(LinearLayout.VERTICAL);
Pair<ListView, FlagAdapter> pair = createListView(context, flags, countText);
ListView listView = pair.first;
FlagAdapter adapter = pair.second;
EditText search = createSearchBox(context, adapter, listView, countText);
LinearLayout buttons = createActionButtons(context, listView, adapter);
listView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
ShapeDrawable background = new ShapeDrawable(new RoundRectShape(
Dim.roundedCorners(10), null, null));
background.getPaint().setColor(Utils.getEditTextBackground());
listView.setPadding(0, Dim.dp4, 0, Dim.dp4);
listView.setBackground(background);
listView.setOverScrollMode(View.OVER_SCROLL_NEVER);
wrapper.addView(search);
wrapper.addView(buttons);
wrapper.addView(listView);
// Save references for move buttons.
wrapper.setTag(new ColumnViews(listView, adapter));
return wrapper;
}
/**
* Updates the header text with the current count.
*/
private void updateHeaderCount(TextView header, FlagAdapter adapter) {
header.setText(str((String) header.getTag(), adapter.getCount()));
}
/**
* Creates a search box that filters the list.
*/
@SuppressLint("ClickableViewAccessibility")
private EditText createSearchBox(Context context, FlagAdapter adapter, ListView listView, TextView countText) {
EditText search = new EditText(context);
search.setInputType(InputType.TYPE_CLASS_NUMBER);
search.setTextSize(16);
search.setHint(str("revanced_debug_feature_flags_manager_search_hint"));
search.setHapticFeedbackEnabled(false);
search.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
search.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.setSearchQuery(s.toString());
listView.clearChoices();
updateHeaderCount(countText, adapter);
Drawable clearIcon = context.getResources().getDrawable(android.R.drawable.ic_menu_close_clear_cancel);
clearIcon.setBounds(0, 0, Dim.dp20, Dim.dp20);
search.setCompoundDrawables(null, null, TextUtils.isEmpty(s) ? null : clearIcon, null);
}
@Override public void afterTextChanged(Editable s) {}
});
search.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_UP) {
Drawable[] compoundDrawables = search.getCompoundDrawables();
if (compoundDrawables[2] != null &&
event.getRawX() >= (search.getRight() - compoundDrawables[2].getBounds().width())) {
search.setText("");
return true;
}
}
return false;
});
return search;
}
/**
* Creates action buttons.
*/
private LinearLayout createActionButtons(Context context, ListView listView, FlagAdapter adapter) {
LinearLayout row = new LinearLayout(context);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER);
row.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
ImageButton selectAll = createButton(context, DRAWABLE_REVANCED_SETTINGS_SELECT_ALL,
() -> {
for (int i = 0, count = adapter.getCount(); i < count; i++) {
listView.setItemChecked(i, true);
}
});
ImageButton clearAll = createButton(context, DRAWABLE_REVANCED_SETTINGS_DESELECT_ALL,
() -> {
listView.clearChoices();
adapter.notifyDataSetChanged();
});
ImageButton copy = createButton(context, DRAWABLE_REVANCED_SETTINGS_COPY_ALL,
() -> {
List<String> items = new ArrayList<>();
SparseBooleanArray checked = listView.getCheckedItemPositions();
if (checked.size() > 0) {
for (int i = 0, count = adapter.getCount(); i < count; i++) {
if (checked.get(i)) {
items.add(adapter.getItem(i));
}
}
} else {
for (Long flag : adapter.getFullFlags()) {
items.add(String.valueOf(flag));
}
}
Utils.setClipboard(TextUtils.join("\n", items));
Utils.showToastShort(str("revanced_debug_feature_flags_manager_toast_copied"));
});
row.addView(selectAll);
row.addView(clearAll);
row.addView(copy);
return row;
}
/**
* Creates the move buttons (left and right groups).
*/
private Pair<LinearLayout, LinearLayout> createMoveButtons(Context context,
ListView availableListView, ListView blockedListView,
TreeSet<Long> availableFlags, TreeSet<Long> blockedFlags,
TextView availableCountText, TextView blockedCountText) {
// Left group: >> >
LinearLayout leftButtons = new LinearLayout(context);
leftButtons.setOrientation(LinearLayout.HORIZONTAL);
leftButtons.setGravity(Gravity.CENTER);
ImageButton moveAllRight = createButton(context, DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_DOUBLE,
() -> moveFlags(availableListView, blockedListView, availableFlags, blockedFlags,
availableCountText, blockedCountText, true));
ImageButton moveOneRight = createButton(context, DRAWABLE_REVANCED_SETTINGS_ARROW_RIGHT_ONE,
() -> moveFlags(availableListView, blockedListView, availableFlags, blockedFlags,
availableCountText, blockedCountText, false));
leftButtons.addView(moveAllRight);
leftButtons.addView(moveOneRight);
// Right group: < <<
LinearLayout rightButtons = new LinearLayout(context);
rightButtons.setOrientation(LinearLayout.HORIZONTAL);
rightButtons.setGravity(Gravity.CENTER);
ImageButton moveOneLeft = createButton(context, DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_ONE,
() -> moveFlags(blockedListView, availableListView, blockedFlags, availableFlags,
blockedCountText, availableCountText, false));
ImageButton moveAllLeft = createButton(context, DRAWABLE_REVANCED_SETTINGS_ARROW_LEFT_DOUBLE,
() -> moveFlags(blockedListView, availableListView, blockedFlags, availableFlags,
blockedCountText, availableCountText, true));
rightButtons.addView(moveOneLeft);
rightButtons.addView(moveAllLeft);
return new Pair<>(leftButtons, rightButtons);
}
/**
* Creates a styled ImageButton.
*/
@SuppressLint("ResourceType")
private ImageButton createButton(Context context, int drawableResId, Runnable action) {
ImageButton button = new ImageButton(context);
button.setImageResource(drawableResId);
button.setScaleType(ImageView.ScaleType.CENTER);
int[] attrs = {android.R.attr.selectableItemBackgroundBorderless};
//noinspection Recycle
TypedArray ripple = context.obtainStyledAttributes(attrs);
button.setBackgroundDrawable(ripple.getDrawable(0));
ripple.close();
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(Dim.dp32, Dim.dp32);
params.setMargins(Dim.dp8, Dim.dp8, Dim.dp8, Dim.dp8);
button.setLayoutParams(params);
button.setOnClickListener(v -> action.run());
return button;
}
/**
* Custom adapter with search filtering.
*/
private static class FlagAdapter extends ArrayAdapter<String> {
private final TreeSet<Long> fullFlags;
private String searchQuery = "";
public FlagAdapter(Context context, TreeSet<Long> fullFlags) {
super(context, android.R.layout.simple_list_item_multiple_choice, new ArrayList<>());
this.fullFlags = fullFlags;
updateFiltered();
}
public void setSearchQuery(String query) {
searchQuery = query == null ? "" : query.trim();
updateFiltered();
}
private void updateFiltered() {
clear();
for (Long flag : fullFlags) {
String flagString = String.valueOf(flag);
if (searchQuery.isEmpty() || flagString.contains(searchQuery)) {
add(flagString);
}
}
notifyDataSetChanged();
}
public void refresh() {
updateFiltered();
}
public List<Long> getFullFlags() {
return new ArrayList<>(fullFlags);
}
}
/**
* Creates a ListView with filtering, multi-select, and range selection.
*/
@SuppressLint("ClickableViewAccessibility")
private Pair<ListView, FlagAdapter> createListView(Context context,
TreeSet<Long> flags, TextView countText) {
ListView listView = new ListView(context);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setDividerHeight(0);
FlagAdapter adapter = new FlagAdapter(context, flags);
listView.setAdapter(adapter);
final ListViewSelectionState state = new ListViewSelectionState();
listView.setOnItemClickListener((parent, view, position, id) -> {
if (!state.isRangeSelecting) {
state.lastClickedPosition = position;
} else {
state.isRangeSelecting = false;
}
});
listView.setOnItemLongClickListener((parent, view, position, id) -> {
if (state.lastClickedPosition == -1) {
listView.setItemChecked(position, true);
state.lastClickedPosition = position;
} else {
int start = Math.min(state.lastClickedPosition, position);
int end = Math.max(state.lastClickedPosition, position);
for (int i = start; i <= end; i++) {
listView.setItemChecked(i, true);
}
state.isRangeSelecting = true;
}
return true;
});
listView.setOnTouchListener((view, event) -> {
if (event.getAction() == MotionEvent.ACTION_UP && state.isRangeSelecting) {
state.isRangeSelecting = false;
}
return false;
});
return new Pair<>(listView, adapter);
}
/**
* Moves selected or all flags from one list to another.
*
* @param fromListView Source ListView.
* @param toListView Destination ListView.
* @param fromFlags Source flag set.
* @param toFlags Destination flag set.
* @param fromCountText Header showing count of source items.
* @param toCountText Header showing count of destination items.
* @param moveAll If true, move all items; if false, move only selected.
*/
private void moveFlags(ListView fromListView, ListView toListView,
TreeSet<Long> fromFlags, TreeSet<Long> toFlags,
TextView fromCountText, TextView toCountText,
boolean moveAll) {
if (fromListView == null || toListView == null) return;
List<Long> flagsToMove = new ArrayList<>();
FlagAdapter fromAdapter = (FlagAdapter) fromListView.getAdapter();
if (moveAll) {
flagsToMove.addAll(fromFlags);
} else {
SparseBooleanArray checked = fromListView.getCheckedItemPositions();
for (int i = 0, count = fromAdapter.getCount(); i < count; i++) {
if (checked.get(i)) {
String item = fromAdapter.getItem(i);
if (item != null) {
flagsToMove.add(Long.parseLong(item));
}
}
}
}
if (flagsToMove.isEmpty()) return;
for (Long flag : flagsToMove) {
fromFlags.remove(flag);
toFlags.add(flag);
}
// Clear selections before refreshing.
fromListView.clearChoices();
toListView.clearChoices();
// Refresh both adapters.
fromAdapter.refresh();
((FlagAdapter) toListView.getAdapter()).refresh();
// Update headers.
updateHeaderCount(fromCountText, fromAdapter);
updateHeaderCount(toCountText, (FlagAdapter) toListView.getAdapter());
}
/**
* Saves blocked flags to settings.
*/
private void saveFlags(TreeSet<Long> blockedFlags) {
StringBuilder flagsString = new StringBuilder();
for (Long flag : blockedFlags) {
if (flagsString.length() > 0) {
flagsString.append("\n");
}
flagsString.append(flag);
}
BaseSettings.DISABLED_FEATURE_FLAGS.save(flagsString.toString());
Utils.showToastShort(str("revanced_debug_feature_flags_manager_toast_saved"));
Logger.printDebug(() -> "Feature flags saved. Blocked: " + blockedFlags.size());
AbstractPreferenceFragment.showRestartDialog(getContext());
}
/**
* Resets all blocked flags.
*/
private void resetFlags() {
BaseSettings.DISABLED_FEATURE_FLAGS.save("");
Utils.showToastShort(str("revanced_debug_feature_flags_manager_toast_reset"));
AbstractPreferenceFragment.showRestartDialog(getContext());
}
}

View File

@@ -11,7 +11,6 @@ import android.preference.Preference;
import android.text.InputType;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -35,7 +34,7 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
editText.setAutofillHints((String) null);
}
editText.setInputType(editText.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
editText.setTextSize(TypedValue.COMPLEX_UNIT_PT, 7); // Use a smaller font to reduce text wrap.
editText.setTextSize(14);
setOnPreferenceClickListener(this);
}

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.shared.requests.Route.Method.GET;
import android.annotation.SuppressLint;
@@ -41,6 +40,7 @@ import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.requests.Requester;
import app.revanced.extension.shared.requests.Route;
import app.revanced.extension.shared.ui.Dim;
/**
* Opens a dialog showing official links.
@@ -222,11 +222,10 @@ class WebViewDialog extends Dialog {
LinearLayout mainLayout = new LinearLayout(getContext());
mainLayout.setOrientation(LinearLayout.VERTICAL);
final int padding = dipToPixels(10);
mainLayout.setPadding(padding, padding, padding, padding);
mainLayout.setPadding(Dim.dp10, Dim.dp10, Dim.dp10, Dim.dp10);
// Set rounded rectangle background.
ShapeDrawable mainBackground = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(28), null, null));
Dim.roundedCorners(28), null, null));
mainBackground.getPaint().setColor(Utils.getDialogBackgroundColor());
mainLayout.setBackground(mainBackground);

View File

@@ -8,7 +8,6 @@ import android.os.Build;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
@@ -18,8 +17,11 @@ import android.widget.Toolbar;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseActivityHook;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.shared.settings.BaseSettings;
@SuppressWarnings({"deprecation", "NewApi"})
public class ToolbarPreferenceFragment extends AbstractPreferenceFragment {
@@ -88,14 +90,13 @@ public class ToolbarPreferenceFragment extends AbstractPreferenceFragment {
toolbar.setNavigationIcon(getBackButtonDrawable());
toolbar.setNavigationOnClickListener(view -> preferenceScreenDialog.dismiss());
final int margin = Utils.dipToPixels(16);
toolbar.setTitleMargin(margin, 0, margin, 0);
toolbar.setTitleMargin(Dim.dp16, 0, Dim.dp16, 0);
TextView toolbarTextView = Utils.getChildView(toolbar,
true, TextView.class::isInstance);
if (toolbarTextView != null) {
toolbarTextView.setTextColor(Utils.getAppForegroundColor());
toolbarTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
toolbarTextView.setTextSize(20);
}
// Allow package-specific toolbar customization.
@@ -134,8 +135,10 @@ public class ToolbarPreferenceFragment extends AbstractPreferenceFragment {
*/
@SuppressLint("UseCompatLoadingForDrawables")
public static Drawable getBackButtonDrawable() {
final int backButtonResource = Utils.getResourceIdentifierOrThrow(
"revanced_settings_toolbar_arrow_left", "drawable");
final int backButtonResource = Utils.getResourceIdentifierOrThrow(ResourceType.DRAWABLE,
Utils.appIsUsingBoldIcons()
? "revanced_settings_toolbar_arrow_left_bold"
: "revanced_settings_toolbar_arrow_left");
Drawable drawable = Utils.getContext().getResources().getDrawable(backButtonResource);
customizeBackButtonDrawable(drawable);
return drawable;

View File

@@ -16,6 +16,7 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.ColorPickerPreference;
import app.revanced.extension.shared.settings.preference.CustomDialogListPreference;
@@ -38,18 +39,18 @@ public abstract class BaseSearchResultItem {
// Get the corresponding layout resource ID.
public int getLayoutResourceId() {
return switch (this) {
case REGULAR, URL_LINK -> getResourceIdentifier("revanced_preference_search_result_regular");
case SWITCH -> getResourceIdentifier("revanced_preference_search_result_switch");
case LIST -> getResourceIdentifier("revanced_preference_search_result_list");
case COLOR_PICKER -> getResourceIdentifier("revanced_preference_search_result_color");
case GROUP_HEADER -> getResourceIdentifier("revanced_preference_search_result_group_header");
case NO_RESULTS -> getResourceIdentifier("revanced_preference_search_no_result");
case REGULAR, URL_LINK -> getResourceIdentifier("revanced_preference_search_result_regular");
case SWITCH -> getResourceIdentifier("revanced_preference_search_result_switch");
case LIST -> getResourceIdentifier("revanced_preference_search_result_list");
case COLOR_PICKER -> getResourceIdentifier("revanced_preference_search_result_color");
case GROUP_HEADER -> getResourceIdentifier("revanced_preference_search_result_group_header");
case NO_RESULTS -> getResourceIdentifier("revanced_preference_search_no_result");
};
}
private static int getResourceIdentifier(String name) {
// Placeholder for actual resource identifier retrieval.
return Utils.getResourceIdentifierOrThrow(name, "layout");
return Utils.getResourceIdentifierOrThrow(ResourceType.LAYOUT, name);
}
}

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.shared.settings.search;
import static app.revanced.extension.shared.Utils.getResourceIdentifierOrThrow;
import static app.revanced.extension.shared.settings.search.BaseSearchViewController.DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
@@ -33,6 +32,7 @@ import java.lang.reflect.Method;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.ColorPickerPreference;
import app.revanced.extension.shared.settings.preference.CustomDialogListPreference;
@@ -54,15 +54,15 @@ public abstract class BaseSearchResultsAdapter extends ArrayAdapter<BaseSearchRe
protected static final int PAUSE_BETWEEN_BLINKS = 100;
protected static final int ID_PREFERENCE_TITLE = getResourceIdentifierOrThrow(
"preference_title", "id");
ResourceType.ID, "preference_title");
protected static final int ID_PREFERENCE_SUMMARY = getResourceIdentifierOrThrow(
"preference_summary", "id");
ResourceType.ID, "preference_summary");
protected static final int ID_PREFERENCE_PATH = getResourceIdentifierOrThrow(
"preference_path", "id");
ResourceType.ID, "preference_path");
protected static final int ID_PREFERENCE_SWITCH = getResourceIdentifierOrThrow(
"preference_switch", "id");
ResourceType.ID, "preference_switch");
protected static final int ID_PREFERENCE_COLOR_DOT = getResourceIdentifierOrThrow(
"preference_color_dot", "id");
ResourceType.ID, "preference_color_dot");
protected static class RegularViewHolder {
TextView titleView;
@@ -275,7 +275,7 @@ public abstract class BaseSearchResultsAdapter extends ArrayAdapter<BaseSearchRe
holder.titleView.setText(item.highlightedTitle);
holder.summaryView.setText(item.highlightedSummary);
holder.summaryView.setVisibility(TextUtils.isEmpty(item.highlightedSummary) ? View.GONE : View.VISIBLE);
holder.iconView.setImageResource(DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON);
holder.iconView.setImageResource(BaseSearchViewController.getSearchIcon());
}
/**
@@ -484,7 +484,7 @@ public abstract class BaseSearchResultsAdapter extends ArrayAdapter<BaseSearchRe
return -1;
}
for (int i = 0; i < adapter.getCount(); i++) {
for (int i = 0, count = adapter.getCount(); i < count; i++) {
Object item = adapter.getItem(i);
if (item == targetPreference) {
return i;
@@ -522,8 +522,8 @@ public abstract class BaseSearchResultsAdapter extends ArrayAdapter<BaseSearchRe
if (currentAnimator != null && currentAnimator.isRunning()) {
currentAnimator.cancel();
}
int startColor = Utils.getAppBackgroundColor();
int highlightColor = Utils.adjustColorBrightness(
final int startColor = Utils.getAppBackgroundColor();
final int highlightColor = Utils.adjustColorBrightness(
startColor,
Utils.isDarkModeEnabled() ? 1.25f : 0.8f
);
@@ -566,7 +566,7 @@ public abstract class BaseSearchResultsAdapter extends ArrayAdapter<BaseSearchRe
}
// First search on current level.
for (int i = 0; i < group.getPreferenceCount(); i++) {
for (int i = 0, count = group.getPreferenceCount(); i < count; i++) {
Preference pref = group.getPreference(i);
if (key.equals(pref.getKey())) {
return pref;

View File

@@ -13,8 +13,8 @@ import android.preference.PreferenceCategory;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
@@ -38,6 +38,7 @@ import java.util.Set;
import java.util.regex.Pattern;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
@@ -45,6 +46,7 @@ import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.preference.ColorPickerPreference;
import app.revanced.extension.shared.settings.preference.CustomDialogListPreference;
import app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory;
import app.revanced.extension.shared.ui.Dim;
/**
* Abstract controller for managing the overlay search view in ReVanced settings.
@@ -70,14 +72,29 @@ public abstract class BaseSearchViewController {
protected static final int MAX_SEARCH_RESULTS = 50; // Maximum number of search results displayed.
protected static final int ID_REVANCED_SEARCH_VIEW = getResourceIdentifierOrThrow("revanced_search_view", "id");
protected static final int ID_REVANCED_SEARCH_VIEW_CONTAINER = getResourceIdentifierOrThrow("revanced_search_view_container", "id");
protected static final int ID_ACTION_SEARCH = getResourceIdentifierOrThrow("action_search", "id");
protected static final int ID_REVANCED_SETTINGS_FRAGMENTS = getResourceIdentifierOrThrow("revanced_settings_fragments", "id");
public static final int DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON =
getResourceIdentifierOrThrow("revanced_settings_search_icon", "drawable");
protected static final int MENU_REVANCED_SEARCH_MENU =
getResourceIdentifierOrThrow("revanced_search_menu", "menu");
protected static final int ID_REVANCED_SEARCH_VIEW = getResourceIdentifierOrThrow(
ResourceType.ID, "revanced_search_view");
protected static final int ID_REVANCED_SEARCH_VIEW_CONTAINER = getResourceIdentifierOrThrow(
ResourceType.ID, "revanced_search_view_container");
protected static final int ID_ACTION_SEARCH = getResourceIdentifierOrThrow(
ResourceType.ID, "action_search");
protected static final int ID_REVANCED_SETTINGS_FRAGMENTS = getResourceIdentifierOrThrow(
ResourceType.ID, "revanced_settings_fragments");
private static final int DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON = getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, "revanced_settings_search_icon");
private static final int DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON_BOLD = getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, "revanced_settings_search_icon_bold");
protected static final int MENU_REVANCED_SEARCH_MENU = getResourceIdentifierOrThrow(
ResourceType.MENU, "revanced_search_menu");
/**
* @return The search icon, either bold or not bold, depending on the ReVanced UI setting.
*/
public static int getSearchIcon() {
return Utils.appIsUsingBoldIcons()
? DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON_BOLD
: DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON;
}
/**
* Constructs a new BaseSearchViewController instance.
@@ -112,7 +129,7 @@ public abstract class BaseSearchViewController {
// Retrieve SearchView and container from XML.
searchView = activity.findViewById(ID_REVANCED_SEARCH_VIEW);
EditText searchEditText = searchView.findViewById(Utils.getResourceIdentifierOrThrow(
"android:id/search_src_text", null));
null, "android:id/search_src_text"));
// Disable fullscreen keyboard mode.
searchEditText.setImeOptions(searchEditText.getImeOptions() | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
@@ -123,7 +140,7 @@ public abstract class BaseSearchViewController {
searchView.setQueryHint(str("revanced_settings_search_hint"));
// Set text size.
searchEditText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
searchEditText.setTextSize(16);
// Set cursor color.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@@ -149,7 +166,7 @@ public abstract class BaseSearchViewController {
// Create cursor drawable.
GradientDrawable cursorDrawable = new GradientDrawable();
cursorDrawable.setShape(GradientDrawable.RECTANGLE);
cursorDrawable.setSize(Utils.dipToPixels(2), -1); // Width: 2dp, Height: match text height.
cursorDrawable.setSize(Dim.dp2, -1); // Width: 2dp, Height: match text height.
cursorDrawable.setColor(cursorColor);
// Set cursor drawable.
@@ -164,7 +181,7 @@ public abstract class BaseSearchViewController {
overlayContainer = new FrameLayout(activity);
overlayContainer.setVisibility(View.GONE);
overlayContainer.setBackgroundColor(Utils.getAppBackgroundColor());
overlayContainer.setElevation(Utils.dipToPixels(8));
overlayContainer.setElevation(Dim.dp8);
// Container for search results.
FrameLayout searchResultsContainer = new FrameLayout(activity);
@@ -248,6 +265,10 @@ public abstract class BaseSearchViewController {
}
return false;
});
// Set bold icon if needed.
MenuItem search = toolbar.getMenu().findItem(ID_ACTION_SEARCH);
search.setIcon(getSearchIcon());
}
/**
@@ -524,7 +545,7 @@ public abstract class BaseSearchViewController {
noResultsPreference.setTitle(str("revanced_settings_search_no_results_title", query));
noResultsPreference.setSummary(str("revanced_settings_search_no_results_summary"));
noResultsPreference.setSelectable(false);
noResultsPreference.setIcon(DRAWABLE_REVANCED_SETTINGS_SEARCH_ICON);
noResultsPreference.setIcon(getSearchIcon());
filteredSearchItems.add(new BaseSearchResultItem.PreferenceSearchItem(noResultsPreference, "", Collections.emptyList()));
}
@@ -669,7 +690,7 @@ public abstract class BaseSearchViewController {
protected static GradientDrawable createBackgroundDrawable() {
GradientDrawable background = new GradientDrawable();
background.setShape(GradientDrawable.RECTANGLE);
background.setCornerRadius(Utils.dipToPixels(28));
background.setCornerRadius(Dim.dp28);
background.setColor(getSearchViewBackground());
return background;
}

View File

@@ -24,6 +24,8 @@ import java.util.Deque;
import java.util.LinkedList;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.BulletPointPreference;
import app.revanced.extension.shared.ui.CustomDialog;
@@ -37,25 +39,35 @@ public class SearchHistoryManager {
private static final int MAX_HISTORY_SIZE = 5; // Maximum history items stored.
private static final int ID_CLEAR_HISTORY_BUTTON = getResourceIdentifierOrThrow(
"clear_history_button", "id");
ResourceType.ID, "clear_history_button");
private static final int ID_HISTORY_TEXT = getResourceIdentifierOrThrow(
"history_text", "id");
ResourceType.ID, "history_text");
private static final int ID_HISTORY_ICON = getResourceIdentifierOrThrow(
ResourceType.ID, "history_icon");
private static final int ID_DELETE_ICON = getResourceIdentifierOrThrow(
"delete_icon", "id");
ResourceType.ID, "delete_icon");
private static final int ID_EMPTY_HISTORY_TITLE = getResourceIdentifierOrThrow(
"empty_history_title", "id");
ResourceType.ID, "empty_history_title");
private static final int ID_EMPTY_HISTORY_SUMMARY = getResourceIdentifierOrThrow(
"empty_history_summary", "id");
ResourceType.ID, "empty_history_summary");
private static final int ID_SEARCH_HISTORY_HEADER = getResourceIdentifierOrThrow(
"search_history_header", "id");
ResourceType.ID, "search_history_header");
private static final int ID_SEARCH_TIPS_SUMMARY = getResourceIdentifierOrThrow(
"revanced_settings_search_tips_summary", "id");
ResourceType.ID, "revanced_settings_search_tips_summary");
private static final int LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_SCREEN = getResourceIdentifierOrThrow(
"revanced_preference_search_history_screen", "layout");
ResourceType.LAYOUT, "revanced_preference_search_history_screen");
private static final int LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_ITEM = getResourceIdentifierOrThrow(
"revanced_preference_search_history_item", "layout");
ResourceType.LAYOUT, "revanced_preference_search_history_item");
private static final int ID_SEARCH_HISTORY_LIST = getResourceIdentifierOrThrow(
"search_history_list", "id");
ResourceType.ID, "search_history_list");
private static final int ID_SEARCH_REMOVE_ICON = getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, "revanced_settings_search_remove");
private static final int ID_SEARCH_REMOVE_ICON_BOLD = getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, "revanced_settings_search_remove_bold");
private static final int ID_SEARCH_ARROW_TIME_ICON = getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, "revanced_settings_arrow_time");
private static final int ID_SEARCH_ARROW_TIME_ICON_BOLD = getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, "revanced_settings_arrow_time_bold");
private final Deque<String> searchHistory;
private final Activity activity;
@@ -97,7 +109,8 @@ public class SearchHistoryManager {
// Inflate search history layout.
LayoutInflater inflater = LayoutInflater.from(activity);
View historyView = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_SCREEN, searchHistoryContainer, false);
View historyView = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_SCREEN,
searchHistoryContainer, false);
searchHistoryContainer.addView(historyView, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
@@ -320,17 +333,29 @@ public class SearchHistoryManager {
public void notifyDataSetChanged() {
container.removeAllViews();
for (String query : history) {
View view = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_ITEM, container, false);
TextView historyText = view.findViewById(ID_HISTORY_TEXT);
ImageView deleteIcon = view.findViewById(ID_DELETE_ICON);
historyText.setText(query);
View view = inflater.inflate(LAYOUT_REVANCED_PREFERENCE_SEARCH_HISTORY_ITEM,
container, false);
// Set click listener for main item (select query).
view.setOnClickListener(v -> onSelectHistoryItemListener.onSelectHistoryItem(query));
// Set history icon.
ImageView historyIcon = view.findViewById(ID_HISTORY_ICON);
historyIcon.setImageResource(Utils.appIsUsingBoldIcons()
? ID_SEARCH_ARROW_TIME_ICON_BOLD
: ID_SEARCH_ARROW_TIME_ICON
);
TextView historyText = view.findViewById(ID_HISTORY_TEXT);
historyText.setText(query);
// Set click listener for delete icon.
ImageView deleteIcon = view.findViewById(ID_DELETE_ICON);
deleteIcon.setImageResource(Utils.appIsUsingBoldIcons()
? ID_SEARCH_REMOVE_ICON_BOLD
: ID_SEARCH_REMOVE_ICON
);
deleteIcon.setOnClickListener(v -> createAndShowDialog(
query,
str("revanced_settings_search_remove_message"),

View File

@@ -72,7 +72,7 @@ public class SpoofVideoStreamsPatch {
public static boolean spoofingToClientWithNoMultiAudioStreams() {
return isPatchIncluded()
&& SPOOF_STREAMING_DATA
&& preferredClient != ClientType.IPADOS;
&& !preferredClient.supportsMultiAudioTracks;
}
/**

View File

@@ -16,7 +16,6 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -83,22 +82,15 @@ public class StreamingDataRequest {
*/
private static final int MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000;
/**
* Cache limit must be greater than the maximum number of videos open at once,
* which theoretically is more than 4 (3 Shorts + one regular minimized video).
* But instead use a much larger value, to handle if a video viewed a while ago
* is somehow still referenced. Each stream is a small array of Strings
* so memory usage is not a concern.
*/
private static final Map<String, StreamingDataRequest> cache = Collections.synchronizedMap(
new LinkedHashMap<>(100) {
/**
* Cache limit must be greater than the maximum number of videos open at once,
* which theoretically is more than 4 (3 Shorts + one regular minimized video).
* But instead use a much larger value, to handle if a video viewed a while ago
* is somehow still referenced. Each stream is a small array of Strings
* so memory usage is not a concern.
*/
private static final int CACHE_LIMIT = 50;
@Override
protected boolean removeEldestEntry(Entry eldest) {
return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit.
}
});
Utils.createSizeRestrictedMap(50));
/**
* Strings found in the response if the video is a livestream.

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.shared.ui;
import static app.revanced.extension.shared.Utils.adjustColorBrightness;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.shared.Utils.getAppBackgroundColor;
import static app.revanced.extension.shared.Utils.isDarkModeEnabled;
import static app.revanced.extension.shared.settings.preference.ColorPickerPreference.DISABLED_ALPHA;
@@ -13,7 +12,7 @@ import android.view.View;
import androidx.annotation.ColorInt;
public class ColorDot {
private static final int STROKE_WIDTH = dipToPixels(1.5f); // Stroke width in dp.
private static final int STROKE_WIDTH = Dim.dp(1.5f);
/**
* Creates a circular drawable with a main fill and a stroke.
@@ -55,7 +54,7 @@ public class ColorDot {
targetView.setAlpha(enabled ? 1.0f : DISABLED_ALPHA);
if (!isDarkModeEnabled()) {
targetView.setClipToOutline(true);
targetView.setElevation(dipToPixels(2));
targetView.setElevation(Dim.dp2);
}
}
}

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.shared.ui;
import static app.revanced.extension.shared.Utils.dipToPixels;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
@@ -37,7 +35,6 @@ public class CustomDialog {
private final Context context;
private final Dialog dialog;
private final LinearLayout mainLayout;
private final int dip4, dip8, dip16, dip24, dip36;
/**
* Creates a custom dialog with a styled layout, including a title, message, buttons, and an optional EditText.
@@ -93,13 +90,6 @@ public class CustomDialog {
this.dialog = new Dialog(context);
this.dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); // Remove default title bar.
// Preset size constants.
dip4 = dipToPixels(4);
dip8 = dipToPixels(8);
dip16 = dipToPixels(16);
dip24 = dipToPixels(24);
dip36 = dipToPixels(36);
// Create main layout.
mainLayout = createMainLayout();
addTitle(title);
@@ -122,11 +112,11 @@ public class CustomDialog {
private LinearLayout createMainLayout() {
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(dip24, dip16, dip24, dip24);
layout.setPadding(Dim.dp24, Dim.dp16, Dim.dp24, Dim.dp24);
// Set rounded rectangle background.
ShapeDrawable background = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(28), null, null));
Dim.roundedCorners(28), null, null));
// Dialog background.
background.getPaint().setColor(Utils.getDialogBackgroundColor());
layout.setBackground(background);
@@ -152,7 +142,7 @@ public class CustomDialog {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, dip16);
params.setMargins(0, 0, 0, Dim.dp16);
titleView.setLayoutParams(params);
mainLayout.addView(titleView);
@@ -180,9 +170,9 @@ public class CustomDialog {
// EditText (if provided).
if (editText != null) {
ShapeDrawable background = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(10), null, null));
Dim.roundedCorners(10), null, null));
background.getPaint().setColor(Utils.getEditTextBackground());
scrollView.setPadding(dip8, dip8, dip8, dip8);
scrollView.setPadding(Dim.dp8, Dim.dp8, Dim.dp8, Dim.dp8);
scrollView.setBackground(background);
scrollView.setClipToOutline(true);
@@ -241,7 +231,7 @@ public class CustomDialog {
LinearLayout.LayoutParams buttonContainerParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
buttonContainerParams.setMargins(0, dip16, 0, 0);
buttonContainerParams.setMargins(0, Dim.dp16, 0, 0);
buttonContainer.setLayoutParams(buttonContainerParams);
List<Button> buttons = new ArrayList<>();
@@ -289,12 +279,12 @@ public class CustomDialog {
button.setEllipsize(TextUtils.TruncateAt.END);
button.setGravity(Gravity.CENTER);
// Set internal padding.
button.setPadding(dip16, 0, dip16, 0);
button.setPadding(Dim.dp16, 0, Dim.dp16, 0);
// Background color for OK button (inversion).
// Background color for Cancel or Neutral buttons.
ShapeDrawable background = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(20), null, null));
Dim.roundedCorners(20), null, null));
background.getPaint().setColor(isOkButton
? Utils.getOkButtonBackgroundColor()
: Utils.getCancelOrNeutralButtonBackgroundColor());
@@ -331,20 +321,19 @@ public class CustomDialog {
if (buttons.isEmpty()) return;
// Check if buttons fit in one row.
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
int totalWidth = 0;
for (Integer width : buttonWidths) {
totalWidth += width;
}
if (buttonWidths.size() > 1) {
// Add margins for gaps.
totalWidth += (buttonWidths.size() - 1) * dip8;
totalWidth += (buttonWidths.size() - 1) * Dim.dp8;
}
// Single button: stretch to full width.
if (buttons.size() == 1) {
layoutSingleButton(buttonContainer, buttons.get(0));
} else if (totalWidth <= screenWidth * 0.8) {
} else if (totalWidth <= Dim.pctWidth(80)) {
// Single row: Neutral, Cancel, OK.
layoutButtonsInRow(buttonContainer, buttons, buttonWidths);
} else {
@@ -369,7 +358,7 @@ public class CustomDialog {
button.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
dip36));
Dim.dp36));
singleContainer.addView(button);
buttonContainer.addView(singleContainer);
}
@@ -405,17 +394,17 @@ public class CustomDialog {
if (parent != null) parent.removeView(button);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
0, dip36, buttonWidths.get(i));
0, Dim.dp36, buttonWidths.get(i));
// Set margins based on button type and combination.
if (buttons.size() == 2) {
// Neutral + OK or Cancel + OK.
params.setMargins(i == 0 ? 0 : dip4, 0, i == 0 ? dip4 : 0, 0);
params.setMargins(i == 0 ? 0 : Dim.dp4, 0, i == 0 ? Dim.dp4 : 0, 0);
} else if (buttons.size() == 3) {
// Neutral.
// Cancel.
// OK.
params.setMargins(i == 0 ? 0 : dip4, 0, i == 2 ? 0 : dip4, 0);
params.setMargins(i == 0 ? 0 : Dim.dp4, 0, i == 2 ? 0 : Dim.dp4, 0);
}
button.setLayoutParams(params);
@@ -447,14 +436,14 @@ public class CustomDialog {
singleContainer.setGravity(Gravity.CENTER);
singleContainer.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
dip36));
Dim.dp36));
ViewGroup parent = (ViewGroup) button.getParent();
if (parent != null) parent.removeView(button);
button.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
dip36));
Dim.dp36));
singleContainer.addView(button);
buttonContainer.addView(singleContainer);
@@ -463,7 +452,7 @@ public class CustomDialog {
View spacer = new View(context);
LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
dip8);
Dim.dp8);
spacer.setLayoutParams(spacerParams);
buttonContainer.addView(spacer);
}

View File

@@ -0,0 +1,89 @@
package app.revanced.extension.shared.ui;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.util.TypedValue;
/**
* Utility class for converting design units (dp) and screen percentages to pixels.
*/
public final class Dim {
private Dim() {} // Prevent instantiation.
private static final DisplayMetrics METRICS = Resources.getSystem().getDisplayMetrics();
public static final int SCREEN_WIDTH = METRICS.widthPixels;
public static final int SCREEN_HEIGHT = METRICS.heightPixels;
// DP constants (density-independent pixels).
public static final int dp1 = dp(1);
public static final int dp2 = dp(2);
public static final int dp4 = dp(4);
public static final int dp6 = dp(6);
public static final int dp7 = dp(7);
public static final int dp8 = dp(8);
public static final int dp10 = dp(10);
public static final int dp12 = dp(12);
public static final int dp16 = dp(16);
public static final int dp20 = dp(20);
public static final int dp24 = dp(24);
public static final int dp28 = dp(28);
public static final int dp32 = dp(32);
public static final int dp36 = dp(36);
public static final int dp40 = dp(40);
public static final int dp48 = dp(48);
/**
* Converts dp (density-independent pixels) to actual device pixels.
* Uses Android's official TypedValue.applyDimension() for accurate rounding.
*
* @param dp The dp value to convert (supports float, e.g. 1.2f).
* @return The equivalent pixel value as int.
*/
public static int dp(float dp) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp, METRICS);
}
/**
* Converts a percentage of the screen height to pixels.
*
* @param percent The percentage (0100).
* @return The pixel value corresponding to the percentage of screen height.
*/
public static int pctHeight(int percent) {
return (SCREEN_HEIGHT * percent) / 100;
}
/**
* Converts a percentage of the screen width to pixels.
*
* @param percent The percentage (0100).
* @return The pixel value corresponding to the percentage of screen width.
*/
public static int pctWidth(int percent) {
return (SCREEN_WIDTH * percent) / 100;
}
/**
* Converts a percentage of the screen's portrait width (min side) to pixels.
*
* @param percent The percentage (0100).
* @return The pixel value.
*/
public static int pctPortraitWidth(int percent) {
final int portraitWidth = Math.min(SCREEN_WIDTH, SCREEN_HEIGHT);
return (int) (portraitWidth * (percent / 100.0f));
}
/**
* Creates an array of corner radii for a rounded rectangle.
* All corners use the same radius.
*
* @param dp radius in density-independent pixels
* @return array of 8 floats: [top-left-x, top-left-y, top-right-x, top-right-y, ...]
*/
public static float[] roundedCorners(float dp) {
final float r = dp(dp);
return new float[]{r, r, r, r, r, r, r, r};
}
}

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.shared.ui;
import static app.revanced.extension.shared.Utils.dipToPixels;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -63,9 +61,8 @@ public class SheetBottomDialog {
// Add top spacer.
View spacer = new View(context);
final int dip40 = dipToPixels(40);
LinearLayout.LayoutParams spacerParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, dip40);
LinearLayout.LayoutParams.MATCH_PARENT, Dim.dp40);
spacer.setLayoutParams(spacerParams);
spacer.setClickable(true);
dragContainer.addView(spacer);
@@ -105,20 +102,15 @@ public class SheetBottomDialog {
* @return A configured {@link DraggableLinearLayout} with a handle bar and styled background.
*/
public static DraggableLinearLayout createMainLayout(@NonNull Context context, @Nullable Integer backgroundColor) {
// Preset size constants.
final int dip4 = dipToPixels(4); // Handle bar height.
final int dip8 = dipToPixels(8); // Dialog padding.
final int dip40 = dipToPixels(40); // Handle bar width.
DraggableLinearLayout mainLayout = new DraggableLinearLayout(context);
mainLayout.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(dip8, 0, dip8, dip8);
layoutParams.setMargins(Dim.dp8, 0, Dim.dp8, Dim.dp8);
mainLayout.setLayoutParams(layoutParams);
ShapeDrawable background = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(12), null, null));
Dim.roundedCorners(12), null, null));
int color = (backgroundColor != null) ? backgroundColor : Utils.getDialogBackgroundColor();
background.getPaint().setColor(color);
mainLayout.setBackground(background);
@@ -127,14 +119,14 @@ public class SheetBottomDialog {
LinearLayout handleContainer = new LinearLayout(context);
LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
containerParams.setMargins(0, dip8, 0, 0);
containerParams.setMargins(0, Dim.dp8, 0, 0);
handleContainer.setLayoutParams(containerParams);
handleContainer.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
View handleBar = new View(context);
ShapeDrawable handleBackground = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(4), null, null));
Dim.roundedCorners(4), null, null));
handleBackground.getPaint().setColor(Utils.adjustColorBrightness(color, 0.9f, 1.25f));
LinearLayout.LayoutParams handleParams = new LinearLayout.LayoutParams(dip40, dip4);
LinearLayout.LayoutParams handleParams = new LinearLayout.LayoutParams(Dim.dp40, Dim.dp4);
handleBar.setLayoutParams(handleParams);
handleBar.setBackground(handleBackground);

View File

@@ -1,12 +1,14 @@
package app.revanced.extension.spotify.layout.hide.createbutton;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.spotify.shared.ComponentFilters.ComponentFilter;
import app.revanced.extension.spotify.shared.ComponentFilters.ResourceIdComponentFilter;
import app.revanced.extension.spotify.shared.ComponentFilters.StringComponentFilter;
import java.util.List;
@Deprecated(forRemoval = true)
@SuppressWarnings("unused")
public final class HideCreateButtonPatch {
@@ -15,7 +17,7 @@ public final class HideCreateButtonPatch {
* The main approach used is matching the resource id for the Create button title.
*/
private static final List<ComponentFilter> CREATE_BUTTON_COMPONENT_FILTERS = List.of(
new ResourceIdComponentFilter("navigationbar_musicappitems_create_title", "string"),
new ResourceIdComponentFilter(ResourceType.STRING, "navigationbar_musicappitems_create_title"),
// Temporary fallback and fix for APKs merged with AntiSplit-M not having resources properly encoded,
// and thus getting the resource identifier for the Create button title always return 0.
// FIXME: Remove this once the above issue is no longer relevant.
@@ -27,7 +29,7 @@ public final class HideCreateButtonPatch {
* Used in older versions of the app.
*/
private static final ResourceIdComponentFilter OLD_CREATE_BUTTON_COMPONENT_FILTER =
new ResourceIdComponentFilter("bottom_navigation_bar_create_tab_title", "string");
new ResourceIdComponentFilter(ResourceType.STRING, "bottom_navigation_bar_create_tab_title");
/**
* Injection point. This method is called on every navigation bar item to check whether it is the Create button.

View File

@@ -3,6 +3,7 @@ package app.revanced.extension.spotify.shared;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
public final class ComponentFilters {
@@ -19,21 +20,26 @@ public final class ComponentFilters {
public static final class ResourceIdComponentFilter implements ComponentFilter {
public final String resourceName;
public final String resourceType;
public final ResourceType resourceType;
// Android resources are always positive, so -1 is a valid sentinel value to indicate it has not been loaded.
// 0 is returned when a resource has not been found.
private int resourceId = -1;
@Nullable
private String stringfiedResourceId;
@Deprecated
public ResourceIdComponentFilter(String resourceName, String resourceType) {
this(ResourceType.valueOf(resourceType), resourceName);
}
public ResourceIdComponentFilter(ResourceType resourceType, String resourceName) {
this.resourceName = resourceName;
this.resourceType = resourceType;
}
public int getResourceId() {
if (resourceId == -1) {
resourceId = Utils.getResourceIdentifier(resourceName, resourceType);
resourceId = Utils.getResourceIdentifier(resourceType, resourceName);
}
return resourceId;
}

View File

@@ -1,14 +1,18 @@
package app.revanced.extension.twitch;
import app.revanced.extension.shared.ResourceType;
public class Utils {
/* Called from SettingsPatch smali */
public static int getStringId(String name) {
return app.revanced.extension.shared.Utils.getResourceIdentifier(name, "string");
return app.revanced.extension.shared.Utils.getResourceIdentifier(
ResourceType.STRING, name);
}
/* Called from SettingsPatch smali */
public static int getDrawableId(String name) {
return app.revanced.extension.shared.Utils.getResourceIdentifier(name, "drawable");
return app.revanced.extension.shared.Utils.getResourceIdentifier(
ResourceType.DRAWABLE, name);
}
}

View File

@@ -4,19 +4,21 @@ import static app.revanced.extension.twitch.Utils.getStringId;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.twitch.settings.preference.TwitchPreferenceFragment;
import tv.twitch.android.feature.settings.menu.SettingsMenuGroup;
import tv.twitch.android.settings.SettingsActivity;
import java.util.ArrayList;
import java.util.List;
/**
* Hooks AppCompatActivity to inject a custom {@link TwitchPreferenceFragment}.
*/
@@ -108,7 +110,7 @@ public class TwitchActivityHook {
base.getFragmentManager()
.beginTransaction()
.replace(Utils.getResourceIdentifier("fragment_container", "id"), fragment)
.replace(Utils.getResourceIdentifier(ResourceType.ID, "fragment_container"), fragment)
.commit();
return true;
}

View File

@@ -528,14 +528,8 @@ public final class AlternativeThumbnailsPatch {
* Cache used to verify if an alternative thumbnails exists for a given video id.
*/
@GuardedBy("itself")
private static final Map<String, VerifiedQualities> altVideoIdLookup = new LinkedHashMap<>(100) {
private static final int CACHE_LIMIT = 1000;
@Override
protected boolean removeEldestEntry(Entry eldest) {
return size() > CACHE_LIMIT; // Evict the oldest entry if over the cache limit.
}
};
private static final Map<String, VerifiedQualities> altVideoIdLookup =
Utils.createSizeRestrictedMap(1000);
private static VerifiedQualities getVerifiedQualities(@NonNull String videoId, boolean returnNullIfDoesNotExist) {
synchronized (altVideoIdLookup) {

View File

@@ -7,6 +7,7 @@ import androidx.annotation.Nullable;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@@ -50,7 +51,7 @@ public class ChangeHeaderPatch {
return null;
}
final int identifier = Utils.getResourceIdentifier(attributeName, "attr");
final int identifier = Utils.getResourceIdentifier(ResourceType.ATTR, attributeName);
if (identifier == 0) {
// Should never happen.
Logger.printException(() -> "Could not find attribute: " + drawableName);
@@ -71,7 +72,7 @@ public class ChangeHeaderPatch {
? "_dark"
: "_light");
final int identifier = Utils.getResourceIdentifier(drawableFullName, "drawable");
final int identifier = Utils.getResourceIdentifier(ResourceType.DRAWABLE, drawableFullName);
if (identifier != 0) {
return Utils.getContext().getDrawable(identifier);
}

View File

@@ -21,7 +21,7 @@ public final class DownloadsPatch {
/**
* Injection point.
*/
public static void activityCreated(Activity mainActivity) {
public static void setMainActivity(Activity mainActivity) {
activityRef = new WeakReference<>(mainActivity);
}

View File

@@ -0,0 +1,24 @@
package app.revanced.extension.youtube.patches;
import java.util.Map;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class FixContentProviderPatch {
/**
* Injection point.
*/
public static void removeNullMapEntries(Map<?, ?> map) {
map.entrySet().removeIf(entry -> {
Object value = entry.getValue();
if (value == null) {
Logger.printDebug(() -> "Removing content provider key with null value: " + entry.getKey());
return true;
}
return false;
});
}
}

View File

@@ -7,6 +7,7 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@@ -29,6 +30,15 @@ public final class HidePlayerOverlayButtonsPatch {
return Settings.HIDE_CAST_BUTTON.get() ? View.GONE : original;
}
/**
* Injection point.
*/
public static boolean getCastButtonOverrideV2(boolean original) {
if (Settings.HIDE_CAST_BUTTON.get()) return false;
return original;
}
/**
* Injection point.
*/
@@ -40,10 +50,10 @@ public final class HidePlayerOverlayButtonsPatch {
= Settings.HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS.get();
private static final int PLAYER_CONTROL_PREVIOUS_BUTTON_TOUCH_AREA_ID = getResourceIdentifierOrThrow(
"player_control_previous_button_touch_area", "id");
ResourceType.ID, "player_control_previous_button_touch_area");
private static final int PLAYER_CONTROL_NEXT_BUTTON_TOUCH_AREA_ID = getResourceIdentifierOrThrow(
"player_control_next_button_touch_area", "id");
ResourceType.ID, "player_control_next_button_touch_area");
/**
* Injection point.

View File

@@ -4,7 +4,17 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class HideSeekbarPatch {
/**
* Injection point.
*/
public static boolean hideSeekbar() {
return Settings.HIDE_SEEKBAR.get();
}
/**
* Injection point.
*/
public static boolean useFullscreenLargeSeekbar(boolean original) {
return Settings.FULLSCREEN_LARGE_SEEKBAR.get();
}
}

View File

@@ -15,6 +15,7 @@ import androidx.annotation.Nullable;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.youtube.settings.Settings;
@@ -115,7 +116,7 @@ public final class MiniplayerPatch {
* Resource is not present in older targets, and this field will be zero.
*/
private static final int MODERN_OVERLAY_SUBTITLE_TEXT
= Utils.getResourceIdentifier("modern_miniplayer_subtitle_text", "id");
= Utils.getResourceIdentifier(ResourceType.ID, "modern_miniplayer_subtitle_text");
private static final MiniplayerType CURRENT_TYPE = Settings.MINIPLAYER_TYPE.get();
@@ -378,6 +379,19 @@ public final class MiniplayerPatch {
return original;
}
/**
* Injection point.
*/
public static boolean allowBoldIcons(boolean original) {
if (CURRENT_TYPE == MINIMAL) {
// Minimal player does not have the correct pause/play icon (it's too large).
// Use the non bold icons instead.
return false;
}
return original;
}
/**
* Injection point.
*/

View File

@@ -5,12 +5,11 @@ import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButt
import android.os.Build;
import android.view.View;
import android.widget.TextView;
import java.util.EnumMap;
import java.util.Map;
import android.widget.TextView;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@@ -30,13 +29,13 @@ public final class NavigationButtonsPatch {
private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON
= Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get();
private static final Boolean DISABLE_TRANSLUCENT_STATUS_BAR
private static final boolean DISABLE_TRANSLUCENT_STATUS_BAR
= Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get();
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT
private static final boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT.get();
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
private static final boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK.get();
/**
@@ -62,6 +61,13 @@ public final class NavigationButtonsPatch {
hideViewUnderCondition(Settings.HIDE_NAVIGATION_BUTTON_LABELS, navigationLabelsView);
}
/**
* Injection point.
*/
public static boolean useAnimatedNavigationButtons(boolean original) {
return Settings.NAVIGATION_BAR_ANIMATIONS.get();
}
/**
* Injection point.
*/

View File

@@ -20,15 +20,6 @@ public class OpenShortsInRegularPlayerPatch {
REGULAR_PLAYER_FULLSCREEN
}
static {
if (!VersionCheckPatch.IS_19_46_OR_GREATER
&& Settings.SHORTS_PLAYER_TYPE.get() == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN) {
// User imported newer settings to an older app target.
Logger.printInfo(() -> "Resetting " + Settings.SHORTS_PLAYER_TYPE);
Settings.SHORTS_PLAYER_TYPE.resetToDefault();
}
}
private static WeakReference<Activity> mainActivityRef = new WeakReference<>(null);
private static volatile boolean overrideBackPressToExit;

View File

@@ -24,18 +24,20 @@ public class OpenVideosFullscreenHookPatch {
/**
* Injection point.
*
* Returns negated value.
*/
public static boolean openVideoFullscreenPortrait(boolean original) {
public static boolean doNotOpenVideoFullscreenPortrait(boolean original) {
Boolean openFullscreen = openNextVideoFullscreen;
if (openFullscreen != null) {
openNextVideoFullscreen = null;
return openFullscreen;
return !openFullscreen;
}
if (!isFullScreenPatchIncluded()) {
return original;
}
return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
return !Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
}
}

View File

@@ -42,7 +42,7 @@ public class PlayerControlsPatch {
Logger.printDebug(() -> "fullscreen button visibility: "
+ (visibility == View.VISIBLE ? "VISIBLE" :
visibility == View.GONE ? "GONE" : "INVISIBLE"));
visibility == View.GONE ? "GONE" : "INVISIBLE"));
fullscreenButtonVisibilityChanged(visibility == View.VISIBLE);
}

View File

@@ -1,19 +1,29 @@
package app.revanced.extension.youtube.patches;
import android.app.AlertDialog;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
/** @noinspection unused*/
@SuppressWarnings("unused")
public class RemoveViewerDiscretionDialogPatch {
/**
* Injection point.
*/
public static void confirmDialog(AlertDialog dialog) {
if (!Settings.REMOVE_VIEWER_DISCRETION_DIALOG.get()) {
// Since the patch replaces the AlertDialog#show() method, we need to call the original method here.
dialog.show();
if (Settings.REMOVE_VIEWER_DISCRETION_DIALOG.get()) {
Logger.printDebug(() -> "Clicking alert dialog dismiss button");
final var button = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
button.setSoundEffectsEnabled(false);
button.performClick();
return;
}
final var button = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
button.setSoundEffectsEnabled(false);
button.performClick();
// Since the patch replaces the AlertDialog#show() method, we need to call the original method here.
Logger.printDebug(() -> "Showing alert dialog");
dialog.show();
}
}

View File

@@ -13,9 +13,11 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects;
import java.util.Set;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilter;
import app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.extension.youtube.settings.Settings;
@@ -131,6 +133,10 @@ public class ReturnYouTubeDislikePatch {
String conversionContextString = conversionContext.toString();
if (Settings.RYD_ENABLED.get()) { // FIXME: Remove this.
Logger.printDebug(() -> "RYD conversion context: " + conversionContext);
}
if (isRollingNumber && !conversionContextString.contains("video_action_bar.e")) {
return original;
}

View File

@@ -2,8 +2,6 @@ package app.revanced.extension.youtube.patches;
import android.app.Activity;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -78,7 +76,7 @@ public class ShortsAutoplayPatch {
/**
* Injection point.
*/
public static Enum<?> changeShortsRepeatBehavior(@Nullable Enum<?> original) {
public static Enum<?> changeShortsRepeatBehavior(Enum<?> original) {
try {
final boolean autoplay;
@@ -95,19 +93,19 @@ public class ShortsAutoplayPatch {
autoplay = Settings.SHORTS_AUTOPLAY.get();
}
final ShortsLoopBehavior behavior = autoplay
Enum<?> overrideBehavior = (autoplay
? ShortsLoopBehavior.SINGLE_PLAY
: ShortsLoopBehavior.REPEAT;
: ShortsLoopBehavior.REPEAT).ytEnumValue;
if (behavior.ytEnumValue != null) {
if (overrideBehavior != null) {
Logger.printDebug(() -> {
String name = (original == null ? "unknown (null)" : original.name());
return behavior == original
return overrideBehavior == original
? "Behavior setting is same as original. Using original: " + name
: "Changing Shorts repeat behavior from: " + name + " to: " + behavior.name();
: "Changing Shorts repeat behavior from: " + name + " to: " + overrideBehavior.name();
});
return behavior.ytEnumValue;
return overrideBehavior;
}
if (original == null) {
@@ -118,13 +116,12 @@ public class ShortsAutoplayPatch {
return unknown;
}
} catch (Exception ex) {
Logger.printException(() -> "changeShortsRepeatBehavior failure", ex);
Logger.printException(() -> "changeShortsRepeatState failure", ex);
}
return original;
}
/**
* Injection point.
*/

View File

@@ -19,5 +19,12 @@ public class VersionCheckPatch {
public static final boolean IS_19_29_OR_GREATER = isVersionOrGreater("19.29.00");
@Deprecated
public static final boolean IS_19_34_OR_GREATER = isVersionOrGreater("19.34.00");
public static final boolean IS_19_46_OR_GREATER = isVersionOrGreater("19.46.00");
public static final boolean IS_20_21_OR_GREATER = isVersionOrGreater("20.21.00");
public static final boolean IS_20_22_OR_GREATER = isVersionOrGreater("20.22.00");
public static final boolean IS_20_31_OR_GREATER = isVersionOrGreater("20.31.00");
public static final boolean IS_20_37_OR_GREATER = isVersionOrGreater("20.37.00");
}

View File

@@ -4,6 +4,7 @@ import android.view.View;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -31,7 +32,7 @@ public final class WideSearchbarPatch {
final int paddingRight = searchBarView.getPaddingRight();
final int paddingTop = searchBarView.getPaddingTop();
final int paddingBottom = searchBarView.getPaddingBottom();
final int paddingStart = Utils.dipToPixels(8);
final int paddingStart = Dim.dp8;
if (Utils.isRightToLeftLocale()) {
searchBarView.setPadding(paddingLeft, paddingTop, paddingStart, paddingBottom);

View File

@@ -2,7 +2,6 @@ package app.revanced.extension.youtube.patches.announcements;
import static android.text.Html.FROM_HTML_MODE_COMPACT;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.youtube.patches.announcements.requests.AnnouncementsRoutes.GET_LATEST_ANNOUNCEMENTS;
import static app.revanced.extension.youtube.patches.announcements.requests.AnnouncementsRoutes.GET_LATEST_ANNOUNCEMENT_IDS;
@@ -24,6 +23,7 @@ import java.time.LocalDateTime;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.requests.Requester;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.youtube.patches.announcements.requests.AnnouncementsRoutes;
import app.revanced.extension.youtube.settings.Settings;
@@ -148,7 +148,7 @@ public final class AnnouncementsPatch {
if (child instanceof TextView childTextView && finalTitle.equals(childTextView.getText().toString())) {
childTextView.setCompoundDrawablesWithIntrinsicBounds(
finalLevel.icon, 0, 0, 0);
childTextView.setCompoundDrawablePadding(dipToPixels(8));
childTextView.setCompoundDrawablePadding(Dim.dp8);
}
}

View File

@@ -1,5 +1,6 @@
package app.revanced.extension.youtube.patches.components;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -38,7 +39,6 @@ final class ButtonsFilter extends Filter {
addPathCallbacks(
likeSubscribeGlow,
bufferFilterPathGroup,
new StringFilterGroup(
Settings.HIDE_LIKE_DISLIKE_BUTTON,
"|segmented_like_dislike_button"
@@ -57,6 +57,12 @@ final class ButtonsFilter extends Filter {
)
);
// FIXME: 20.22+ filtering of the action buttons doesn't work because
// the buffer is the same for all buttons.
if (!VersionCheckPatch.IS_20_22_OR_GREATER) {
addPathCallbacks(bufferFilterPathGroup);
}
bufferButtonsGroupList.addAll(
new ByteArrayFilterGroup(
Settings.HIDE_REPORT_BUTTON,
@@ -108,11 +114,13 @@ final class ButtonsFilter extends Filter {
}
private boolean isEveryFilterGroupEnabled() {
for (var group : pathCallbacks)
for (var group : pathCallbacks) {
if (!group.isEnabled()) return false;
}
for (var group : bufferButtonsGroupList)
for (var group : bufferButtonsGroupList) {
if (!group.isEnabled()) return false;
}
return true;
}

View File

@@ -7,15 +7,15 @@ import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
final class DescriptionComponentsFilter extends Filter {
private static final String INFOCARDS_SECTION_PATH = "infocards_section.e";
private final StringTrieSearch exceptions = new StringTrieSearch();
private final ByteArrayFilterGroupList macroMarkersCarouselGroupList = new ByteArrayFilterGroupList();
private final StringFilterGroup macroMarkersCarousel;
private final ByteArrayFilterGroupList macroMarkersCarouselGroupList = new ByteArrayFilterGroupList();
private final StringFilterGroup horizontalShelf;
private final ByteArrayFilterGroup cellVideoAttribute;
private final StringFilterGroup infoCardsSection;
private final StringFilterGroup subscribeButton;
private final StringFilterGroup aiGeneratedVideoSummarySection;
private final StringFilterGroup hypePoints;
@@ -44,9 +44,14 @@ final class DescriptionComponentsFilter extends Filter {
"video_attributes_section"
);
final StringFilterGroup infoCardsSection = new StringFilterGroup(
Settings.HIDE_INFO_CARDS_SECTION,
"infocards_section"
final StringFilterGroup featuredLinksSection = new StringFilterGroup(
Settings.HIDE_FEATURED_LINKS_SECTION,
"media_lockup"
);
final StringFilterGroup featuredVideosSection = new StringFilterGroup(
Settings.HIDE_FEATURED_VIDEOS_SECTION,
"structured_description_video_lockup"
);
final StringFilterGroup podcastSection = new StringFilterGroup(
@@ -69,6 +74,16 @@ final class DescriptionComponentsFilter extends Filter {
"hype_points_factoid"
);
infoCardsSection = new StringFilterGroup(
Settings.HIDE_INFO_CARDS_SECTION,
INFOCARDS_SECTION_PATH
);
subscribeButton = new StringFilterGroup(
Settings.HIDE_SUBSCRIBE_BUTTON,
"subscribe_button"
);
macroMarkersCarousel = new StringFilterGroup(
null,
"macro_markers_carousel.e"
@@ -99,12 +114,15 @@ final class DescriptionComponentsFilter extends Filter {
aiGeneratedVideoSummarySection,
askSection,
attributesSection,
infoCardsSection,
featuredLinksSection,
featuredVideosSection,
horizontalShelf,
howThisWasMadeSection,
hypePoints,
infoCardsSection,
macroMarkersCarousel,
podcastSection,
subscribeButton,
transcriptSection
);
}
@@ -118,6 +136,10 @@ final class DescriptionComponentsFilter extends Filter {
return PlayerType.getCurrent().isMaximizedOrFullscreen();
}
if (matchedGroup == subscribeButton) {
return path.startsWith(INFOCARDS_SECTION_PATH);
}
if (exceptions.matches(path)) return false;
if (matchedGroup == macroMarkersCarousel) {

View File

@@ -1,5 +1,6 @@
package app.revanced.extension.youtube.patches.components;
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_20_21_OR_GREATER;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.graphics.drawable.Drawable;
@@ -21,22 +22,26 @@ import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public final class LayoutComponentsFilter extends Filter {
private static final StringTrieSearch mixPlaylistsExceptions = new StringTrieSearch(
private static final StringTrieSearch mixPlaylistsContextExceptions = new StringTrieSearch(
"V.ED", // Playlist browse id.
"java.lang.ref.WeakReference"
);
private static final ByteArrayFilterGroup mixPlaylistsExceptions2 = new ByteArrayFilterGroup(
private static final ByteArrayFilterGroup mixPlaylistsBufferExceptions = new ByteArrayFilterGroup(
null,
"cell_description_body"
"cell_description_body",
"channel_profile"
);
private static final ByteArrayFilterGroup mixPlaylists = new ByteArrayFilterGroup(
null,
"&list="
);
private static final String PAGE_HEADER_PATH = "page_header.e";
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup communityPosts;
private final StringFilterGroup surveys;
private final StringFilterGroup subscribeButton;
private final StringFilterGroup notifyMe;
private final StringFilterGroup singleItemInformationPanel;
private final StringFilterGroup expandableMetadata;
@@ -67,8 +72,14 @@ public final class LayoutComponentsFilter extends Filter {
"chips_shelf"
);
final var visualSpacer = new StringFilterGroup(
Settings.HIDE_VISUAL_SPACER,
"cell_divider"
);
addIdentifierCallbacks(
chipsShelf
chipsShelf,
visualSpacer
);
// Paths.
@@ -237,8 +248,13 @@ public final class LayoutComponentsFilter extends Filter {
"sponsorships"
);
final var crowdfundingBox = new StringFilterGroup(
Settings.HIDE_CROWDFUNDING_BOX,
"donation_shelf"
);
final var channelWatermark = new StringFilterGroup(
Settings.HIDE_VIDEO_CHANNEL_WATERMARK,
Settings.HIDE_CHANNEL_WATERMARK,
"featured_channel_watermark_overlay"
);
@@ -255,19 +271,28 @@ public final class LayoutComponentsFilter extends Filter {
channelProfile = new StringFilterGroup(
null,
"channel_profile.e",
"page_header.e"
PAGE_HEADER_PATH
);
channelProfileBuffer = new ByteArrayFilterGroupList();
channelProfileBuffer.addAll(new ByteArrayFilterGroup(
Settings.HIDE_VISIT_STORE_BUTTON,
"header_store_button"
Settings.HIDE_STORE_BUTTON,
"store_button"
),
new ByteArrayFilterGroup(
Settings.HIDE_VISIT_COMMUNITY_BUTTON,
Settings.HIDE_COMMUNITY_BUTTON,
"community_button"
),
new ByteArrayFilterGroup(
Settings.HIDE_JOIN_BUTTON,
"sponsor_button"
)
);
subscribeButton = new StringFilterGroup(
Settings.HIDE_SUBSCRIBE_BUTTON_IN_CHANNEL_PAGE,
"subscribe_button"
);
horizontalShelves = new StringFilterGroup(
Settings.HIDE_HORIZONTAL_SHELVES,
"horizontal_video_shelf.e",
@@ -293,6 +318,7 @@ public final class LayoutComponentsFilter extends Filter {
compactChannelBar,
compactChannelBarInner,
communityPosts,
crowdfundingBox,
emergencyBox,
expandableMetadata,
forYouShelf,
@@ -307,6 +333,7 @@ public final class LayoutComponentsFilter extends Filter {
quickActions,
relatedVideos,
singleItemInformationPanel,
subscribeButton,
subscribersCommunityGuidelines,
subscriptionsChipBar,
surveys,
@@ -337,6 +364,10 @@ public final class LayoutComponentsFilter extends Filter {
return channelProfileBuffer.check(buffer).isFiltered();
}
if (matchedGroup == subscribeButton) {
return path.startsWith(PAGE_HEADER_PATH);
}
if (matchedGroup == communityPosts && NavigationBar.isBackButtonVisible()) {
// Allow community posts on channel profile page,
// or if viewing an individual channel in the feed.
@@ -369,28 +400,28 @@ public final class LayoutComponentsFilter extends Filter {
* Injection point.
* Called from a different place then the other filters.
*/
public static boolean filterMixPlaylists(Object conversionContext, @Nullable final byte[] bytes) {
public static boolean filterMixPlaylists(Object conversionContext, @Nullable byte[] buffer) {
// Edit: This hook may no longer be needed, and mix playlist filtering
// might be possible using the existing litho filters.
try {
if (!Settings.HIDE_MIX_PLAYLISTS.get()) {
return false;
}
if (bytes == null) {
Logger.printDebug(() -> "bytes is null");
if (buffer == null) {
Logger.printDebug(() -> "buffer is null");
return false;
}
// Prevent playlist items being hidden, if a mix playlist is present in it.
if (mixPlaylistsExceptions.matches(conversionContext.toString())) {
return false;
}
// Prevent hiding the description of some videos accidentally.
if (mixPlaylistsExceptions2.check(bytes).isFiltered()) {
return false;
}
if (mixPlaylists.check(bytes).isFiltered()) {
if (mixPlaylists.check(buffer).isFiltered()
// Prevent hiding the description of some videos accidentally.
&& !mixPlaylistsBufferExceptions.check(buffer).isFiltered()
// Prevent playlist items being hidden, if a mix playlist is present in it.
// Check last since it requires creating a context string.
//
// FIXME: The conversion context passed in does not always generate a valid toString.
// This string check may no longer be needed, or the patch may be broken.
&& !mixPlaylistsContextExceptions.matches(conversionContext.toString())) {
Logger.printDebug(() -> "Filtered mix playlist");
return true;
}
@@ -405,7 +436,7 @@ public final class LayoutComponentsFilter extends Filter {
* Injection point.
*/
public static boolean showWatermark() {
return !Settings.HIDE_VIDEO_CHANNEL_WATERMARK.get();
return !Settings.HIDE_CHANNEL_WATERMARK.get();
}
/**
@@ -447,11 +478,23 @@ public final class LayoutComponentsFilter extends Filter {
: height;
}
private static final boolean HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS_ENABLED
= Settings.HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS.get();
/**
* Injection point.
*/
public static void hideInRelatedVideos(View chipView) {
Utils.hideViewBy0dpUnderCondition(Settings.HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS, chipView);
// Cannot use 0dp hide with later targets, otherwise the suggested videos
// can be shown in full screen mode.
// This behavior may also be present in earlier app targets.
if (IS_20_21_OR_GREATER) {
// FIXME: The filter bar is still briefly shown when dragging the suggested videos
// below the video player.
Utils.hideViewUnderCondition(HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS_ENABLED, chipView);
} else {
Utils.hideViewBy0dpUnderCondition(HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS_ENABLED, chipView);
}
}
private static final boolean HIDE_DOODLES_ENABLED = Settings.HIDE_DOODLES.get();
@@ -476,7 +519,9 @@ public final class LayoutComponentsFilter extends Filter {
&& NavigationBar.isSearchBarActive()
// Search bar can be active but behind the player.
&& !PlayerType.getCurrent().isMaximizedOrFullscreen()) {
Utils.hideViewByLayoutParams(view);
// FIXME: "Show more" button is visible hidden,
// but an empty space remains that can be clicked.
Utils.hideViewBy0dp(view);
}
}

View File

@@ -4,11 +4,16 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.StringTrieSearch;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -73,6 +78,15 @@ public final class LithoFilterPatch {
}
}
/**
* Placeholder for actual filters.
*/
private static final class DummyFilter extends Filter { }
private static final Filter[] filters = new Filter[] {
new DummyFilter() // Replaced during patching, do not touch.
};
/**
* Litho layout fixed thread pool size override.
* <p>
@@ -90,25 +104,50 @@ public final class LithoFilterPatch {
private static final int LITHO_LAYOUT_THREAD_POOL_SIZE = 1;
/**
* Placeholder for actual filters.
* 20.22+ cannot use the thread buffer, because frequently the buffer is not correct,
* especially for components that are recreated such as dragging off screen then back on screen.
* Instead, parse the identifier found near the start of the buffer and use that to
* identify the correct buffer to use when filtering.
*/
private static final class DummyFilter extends Filter { }
private static final boolean EXTRACT_IDENTIFIER_FROM_BUFFER = VersionCheckPatch.IS_20_22_OR_GREATER;
private static final Filter[] filters = new Filter[] {
new DummyFilter() // Replaced patching, do not touch.
};
/**
* Turns on additional logging, used for development purposes only.
*/
public static final boolean DEBUG_EXTRACT_IDENTIFIER_FROM_BUFFER = false;
private static final StringTrieSearch pathSearchTree = new StringTrieSearch();
private static final StringTrieSearch identifierSearchTree = new StringTrieSearch();
/**
* String suffix for components.
* Can be any of: ".eml", ".e-b", ".eml-js", "e-js-b"
*/
private static final String LITHO_COMPONENT_EXTENSION = ".e";
private static final byte[] LITHO_COMPONENT_EXTENSION_BYTES = LITHO_COMPONENT_EXTENSION.getBytes(StandardCharsets.US_ASCII);
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/**
* Because litho filtering is multi-threaded and the buffer is passed in from a different injection point,
* the buffer is saved to a ThreadLocal so each calling thread does not interfere with other threads.
* Used for 20.21 and lower.
*/
private static final ThreadLocal<byte[]> bufferThreadLocal = new ThreadLocal<>();
/**
* Identifier to protocol buffer mapping. Only used for 20.22+.
* Thread local is needed because filtering is multi-threaded and each thread can load
* a different component with the same identifier.
*/
private static final ThreadLocal<Map<String, byte[]>> identifierToBufferThread = new ThreadLocal<>();
/**
* Global shared buffer. Used only if the buffer is not found in the ThreadLocal.
*/
private static final Map<String, byte[]> identifierToBufferGlobal
= Collections.synchronizedMap(createIdentifierToBufferMap());
private static final StringTrieSearch pathSearchTree = new StringTrieSearch();
private static final StringTrieSearch identifierSearchTree = new StringTrieSearch();
static {
for (Filter filter : filters) {
filterUsingCallbacks(identifierSearchTree, filter,
@@ -160,16 +199,107 @@ public final class LithoFilterPatch {
}
}
private static Map<String, byte[]> createIdentifierToBufferMap() {
// It's unclear how many items should be cached. This is a guess.
return Utils.createSizeRestrictedMap(100);
}
/**
* Helper function that differs from {@link Character#isDigit(char)}
* as this only matches ascii and not unicode numbers.
*/
private static boolean isAsciiNumber(byte character) {
return '0' <= character && character <= '9';
}
private static boolean isAsciiLowerCaseLetter(byte character) {
return 'a' <= character && character <= 'z';
}
/**
* Injection point. Called off the main thread.
* Targets 20.22+
*/
public static void setProtoBuffer(byte[] buffer) {
// Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes.
// This is intentional, as it appears the buffer can be set once and then filtered multiple times.
// The buffer will be cleared from memory after a new buffer is set by the same thread,
// or when the calling thread eventually dies.
bufferThreadLocal.set(buffer);
if (DEBUG_EXTRACT_IDENTIFIER_FROM_BUFFER) {
StringBuilder builder = new StringBuilder();
LithoFilterParameters.findAsciiStrings(builder, buffer);
Logger.printDebug(() -> "New buffer: " + builder);
}
// Could use Boyer-Moore-Horspool since the string is ASCII and has a limited number of
// unique characters, but it seems to be slower since the extra overhead of checking the
// bad character array negates any performance gain of skipping a few extra subsearches.
int emlIndex = -1;
final int emlStringLength = LITHO_COMPONENT_EXTENSION_BYTES.length;
for (int i = 0, lastStartIndex = buffer.length - emlStringLength; i <= lastStartIndex; i++) {
boolean match = true;
for (int j = 0; j < emlStringLength; j++) {
if (buffer[i + j] != LITHO_COMPONENT_EXTENSION_BYTES[j]) {
match = false;
break;
}
}
if (match) {
emlIndex = i;
break;
}
}
if (emlIndex < 0) {
// Buffer is not used for creating a new litho component.
return;
}
int startIndex = emlIndex - 1;
while (startIndex > 0) {
final byte character = buffer[startIndex];
int startIndexFinal = startIndex;
if (isAsciiLowerCaseLetter(character) || isAsciiNumber(character) || character == '_') {
// Valid character for the first path element.
startIndex--;
} else {
startIndex++;
break;
}
}
// Strip away any numbers on the start of the identifier, which can
// be from random data in the buffer before the identifier starts.
while (true) {
final byte character = buffer[startIndex];
if (isAsciiNumber(character)) {
startIndex++;
} else {
break;
}
}
// Find the pipe character after the identifier.
int endIndex = -1;
for (int i = emlIndex, length = buffer.length; i < length; i++) {
if (buffer[i] == '|') {
endIndex = i;
break;
}
}
if (endIndex < 0) {
Logger.printException(() -> "Could not find buffer identifier");
return;
}
String identifier = new String(buffer, startIndex, endIndex - startIndex, StandardCharsets.US_ASCII);
if (DEBUG_EXTRACT_IDENTIFIER_FROM_BUFFER) {
Logger.printDebug(() -> "Found buffer for identifier: " + identifier);
}
identifierToBufferGlobal.put(identifier, buffer);
Map<String, byte[]> map = identifierToBufferThread.get();
if (map == null) {
map = createIdentifierToBufferMap();
identifierToBufferThread.set(map);
}
map.put(identifier, buffer);
}
/**
@@ -177,46 +307,70 @@ public final class LithoFilterPatch {
* Targets 20.21 and lower.
*/
public static void setProtoBuffer(@Nullable ByteBuffer buffer) {
// Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes.
// This is intentional, as it appears the buffer can be set once and then filtered multiple times.
// The buffer will be cleared from memory after a new buffer is set by the same thread,
// or when the calling thread eventually dies.
if (buffer == null || !buffer.hasArray()) {
// It appears the buffer can be cleared out just before the call to #filter()
// Ignore this null value and retain the last buffer that was set.
Logger.printDebug(() -> "Ignoring null or empty buffer: " + buffer);
} else {
setProtoBuffer(buffer.array());
// Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes.
// This is intentional, as it appears the buffer can be set once and then filtered multiple times.
// The buffer will be cleared from memory after a new buffer is set by the same thread,
// or when the calling thread eventually dies.
bufferThreadLocal.set(buffer.array());
}
}
/**
* Injection point.
*/
public static boolean isFiltered(String lithoIdentifier, StringBuilder pathBuilder) {
public static boolean isFiltered(String identifier, StringBuilder pathBuilder) {
try {
if (lithoIdentifier.isEmpty() && pathBuilder.length() == 0) {
if (identifier.isEmpty() || pathBuilder.length() == 0) {
return false;
}
byte[] buffer = bufferThreadLocal.get();
byte[] buffer = null;
if (EXTRACT_IDENTIFIER_FROM_BUFFER) {
final int pipeIndex = identifier.indexOf('|');
if (pipeIndex >= 0) {
// If the identifier contains no pipe, then it's not an ".eml" identifier
// and the buffer is not uniquely identified. Typically this only happens
// for subcomponents where buffer filtering is not used.
String identifierKey = identifier.substring(0, pipeIndex);
var map = identifierToBufferThread.get();
if (map != null) {
buffer = map.get(identifierKey);
}
if (buffer == null) {
// Buffer for thread local not found. Use the last buffer found from any thread.
buffer = identifierToBufferGlobal.get(identifierKey);
if (DEBUG_EXTRACT_IDENTIFIER_FROM_BUFFER && buffer == null) {
// No buffer is found for some components, such as
// shorts_lockup_cell.eml on channel profiles.
// For now, just ignore this and filter without a buffer.
Logger.printException(() -> "Could not find global buffer for identifier: " + identifier);
}
}
}
} else {
buffer = bufferThreadLocal.get();
}
// Potentially the buffer may have been null or never set up until now.
// Use an empty buffer so the litho id/path filters still work correctly.
// Use an empty buffer so the litho id/path filters that do not use a buffer still work.
if (buffer == null) {
buffer = EMPTY_BYTE_ARRAY;
}
LithoFilterParameters parameter = new LithoFilterParameters(
lithoIdentifier, pathBuilder.toString(), buffer);
String path = pathBuilder.toString();
LithoFilterParameters parameter = new LithoFilterParameters(identifier, path, buffer);
Logger.printDebug(() -> "Searching " + parameter);
if (identifierSearchTree.matches(parameter.identifier, parameter)) {
return true;
}
if (pathSearchTree.matches(parameter.path, parameter)) {
return true;
}
return identifierSearchTree.matches(identifier, parameter)
|| pathSearchTree.matches(path, parameter);
} catch (Exception ex) {
Logger.printException(() -> "isFiltered failure", ex);
}

View File

@@ -12,13 +12,9 @@ import java.util.List;
public class PlayerFlyoutMenuItemsFilter extends Filter {
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
private static final boolean AVAILABLE_ON_LAUNCH = !SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams();
@Override
public boolean isAvailable() {
// Check conditions of launch and now. Otherwise if spoofing is changed
// without a restart the setting will show as available when it's not.
return AVAILABLE_ON_LAUNCH && !SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams();
return !SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams();
}
@Override

View File

@@ -4,15 +4,15 @@ import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.TrieSearch;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.ReturnYouTubeDislikePatch;
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.TrieSearch;
/**
* Searches for video id's in the proto buffer of Shorts dislike.
@@ -33,18 +33,7 @@ public final class ReturnYouTubeDislikeFilter extends Filter {
* Cannot use {@link LinkedHashSet} because it's missing #removeEldestEntry().
*/
@GuardedBy("itself")
private static final Map<String, Boolean> lastVideoIds = new LinkedHashMap<>() {
/**
* Number of video id's to keep track of for searching thru the buffer.
* A minimum value of 3 should be sufficient, but check a few more just in case.
*/
private static final int NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK = 5;
@Override
protected boolean removeEldestEntry(Entry eldest) {
return size() > NUMBER_OF_LAST_VIDEO_IDS_TO_TRACK;
}
};
private static final Map<String, Boolean> lastVideoIds = Utils.createSizeRestrictedMap(5);
/**
* Injection point.

View File

@@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.NavigationBar;
import app.revanced.extension.youtube.shared.PlayerType;
@@ -44,9 +45,10 @@ public final class ShortsFilter extends Filter {
private final StringFilterGroup useTemplateButton;
private final ByteArrayFilterGroup useTemplateButtonBuffer;
private final StringFilterGroup autoDubbedLabel;
private final StringFilterGroup subscribeButton;
private final StringFilterGroup joinButton;
private final StringFilterGroup paidPromotionButton;
private final StringFilterGroup paidPromotionLabel;
private final StringFilterGroup shelfHeader;
private final StringFilterGroup suggestedAction;
@@ -161,6 +163,18 @@ public final class ShortsFilter extends Filter {
"participation_bar.e"
);
StringFilterGroup livePreview = new StringFilterGroup(
Settings.HIDE_SHORTS_LIVE_PREVIEW,
// Live Shorts preview that can popup while scrolling through Shorts player.
// Can be removed if a way to disable live Shorts is found.
"live_preview_page_vm.e"
);
autoDubbedLabel = new StringFilterGroup(
Settings.HIDE_SHORTS_AUTO_DUBBED_LABEL,
"badge."
);
joinButton = new StringFilterGroup(
Settings.HIDE_SHORTS_JOIN_BUTTON,
"sponsor_button"
@@ -171,9 +185,10 @@ public final class ShortsFilter extends Filter {
"subscribe_button"
);
paidPromotionButton = new StringFilterGroup(
paidPromotionLabel = new StringFilterGroup(
Settings.HIDE_PAID_PROMOTION_LABEL,
"reel_player_disclosure.e"
"reel_player_disclosure.e",
"shorts_disclosures.e"
);
shortsActionBar = new StringFilterGroup(
@@ -209,7 +224,11 @@ public final class ShortsFilter extends Filter {
videoActionButton = new StringFilterGroup(
null,
// Can be simply 'button.e', 'shorts_video_action_button.e' or 'reel_action_button.e'
// Can be any of:
// button.eml
// shorts_video_action_button.eml
// reel_action_button.eml
// reel_pivot_button.eml
"button.e"
);
@@ -219,37 +238,49 @@ public final class ShortsFilter extends Filter {
);
addPathCallbacks(
shortsCompactFeedVideo, joinButton, subscribeButton, paidPromotionButton,
shortsActionBar, suggestedAction, pausedOverlayButtons, channelBar, previewComment,
shortsCompactFeedVideo, joinButton, subscribeButton, paidPromotionLabel, autoDubbedLabel,
suggestedAction, pausedOverlayButtons, channelBar, previewComment,
fullVideoLinkLabel, videoTitle, useSoundButton, reelSoundMetadata, soundButton, infoPanel,
stickers, likeFountain, likeButton, dislikeButton
stickers, likeFountain, likeButton, dislikeButton, livePreview
);
//
// All other action buttons.
//
videoActionButtonBuffer.addAll(
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_COMMENTS_BUTTON,
"reel_comment_button",
"youtube_shorts_comment_outline"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_SHARE_BUTTON,
"reel_share_button",
"youtube_shorts_share_outline"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_REMIX_BUTTON,
"reel_remix_button",
"youtube_shorts_remix_outline"
)
);
// FIXME: The Shorts buffer is very different with 20.22+ and if any of these filters
// are enabled then all Shorts player vertical buttons are hidden.
if (!VersionCheckPatch.IS_20_22_OR_GREATER) {
addPathCallbacks(shortsActionBar);
//
// All other action buttons.
//
videoActionButtonBuffer.addAll(
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_COMMENTS_BUTTON,
"reel_comment_button",
"youtube_shorts_comment_outline"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_SHARE_BUTTON,
"reel_share_button",
"youtube_shorts_share_outline"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_REMIX_BUTTON,
"reel_remix_button",
"youtube_shorts_remix_outline"
)
);
}
//
// Suggested actions.
//
suggestedActionsBuffer.addAll(
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_PREVIEW_COMMENT,
// Preview comment that can popup while a Short is playing.
// Uses no bundled icons, and instead the users profile photo is shown.
"shorts-comments-panel"
),
new ByteArrayFilterGroup(
Settings.HIDE_SHORTS_SHOP_BUTTON,
"yt_outline_bag_"
@@ -322,7 +353,8 @@ public final class ShortsFilter extends Filter {
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (contentType == FilterContentType.PATH) {
if (matchedGroup == subscribeButton || matchedGroup == joinButton || matchedGroup == paidPromotionButton) {
if (matchedGroup == subscribeButton || matchedGroup == joinButton
|| matchedGroup == paidPromotionLabel || matchedGroup == autoDubbedLabel) {
// Selectively filter to avoid false positive filtering of other subscribe/join buttons.
return path.startsWith(REEL_CHANNEL_BAR_PATH) || path.startsWith(REEL_METAPANEL_PATH);
}

View File

@@ -21,7 +21,6 @@ public class RememberVideoQualityPatch {
private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE;
public static boolean shouldRememberVideoQuality() {
BooleanSetting preference = ShortsPlayerState.isOpen()
? Settings.REMEMBER_SHORTS_QUALITY_LAST_SELECTED

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.youtube.patches.playback.speed;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.youtube.videoplayer.PlayerControlButton.fadeInDuration;
import static app.revanced.extension.youtube.videoplayer.PlayerControlButton.getDialogBackgroundColor;
@@ -30,6 +29,7 @@ import java.util.function.Function;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.shared.ui.SheetBottomDialog;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.patches.components.PlaybackSpeedMenuFilter;
@@ -264,14 +264,6 @@ public class CustomPlaybackSpeedPatch {
SheetBottomDialog.DraggableLinearLayout mainLayout =
SheetBottomDialog.createMainLayout(context, getDialogBackgroundColor());
// Preset size constants.
final int dip4 = dipToPixels(4);
final int dip8 = dipToPixels(8);
final int dip12 = dipToPixels(12);
final int dip20 = dipToPixels(20);
final int dip32 = dipToPixels(32);
final int dip60 = dipToPixels(60);
// Display current playback speed.
TextView currentSpeedText = new TextView(context);
float currentSpeed = VideoInformation.getPlaybackSpeed();
@@ -283,7 +275,7 @@ public class CustomPlaybackSpeedPatch {
currentSpeedText.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
textParams.setMargins(0, dip20, 0, 0);
textParams.setMargins(0, Dim.dp20, 0, 0);
currentSpeedText.setLayoutParams(textParams);
// Add current speed text view to main layout.
mainLayout.addView(currentSpeedText);
@@ -294,8 +286,8 @@ public class CustomPlaybackSpeedPatch {
sliderLayout.setGravity(Gravity.CENTER_VERTICAL);
// Create +/- buttons.
Button minusButton = createStyledButton(context, false, dip8, dip8);
Button plusButton = createStyledButton(context, true, dip8, dip8);
Button minusButton = createStyledButton(context, false);
Button plusButton = createStyledButton(context, true);
// Create slider for speed adjustment.
SeekBar speedSlider = new SeekBar(context);
@@ -363,7 +355,7 @@ public class CustomPlaybackSpeedPatch {
gridLayout.setRowCount((int) Math.ceil(customPlaybackSpeeds.length / 5.0));
LinearLayout.LayoutParams gridParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
gridParams.setMargins(dip4, dip12, dip4, dip12); // Speed buttons container.
gridParams.setMargins(Dim.dp4, Dim.dp12, Dim.dp4, Dim.dp12); // Speed buttons container.
gridLayout.setLayoutParams(gridParams);
// For button use 1 digit minimum.
@@ -378,8 +370,8 @@ public class CustomPlaybackSpeedPatch {
GridLayout.LayoutParams containerParams = new GridLayout.LayoutParams();
containerParams.width = 0; // Equal width for columns.
containerParams.columnSpec = GridLayout.spec(GridLayout.UNDEFINED, 1, 1f);
containerParams.setMargins(dip4, 0, dip4, 0); // Button margins.
containerParams.height = dip60; // Fixed height for button and label.
containerParams.setMargins(Dim.dp4, 0, Dim.dp4, 0); // Button margins.
containerParams.height = Dim.dp(60); // Fixed height for button and label.
buttonContainer.setLayoutParams(containerParams);
// Create speed button.
@@ -391,14 +383,14 @@ public class CustomPlaybackSpeedPatch {
speedButton.setGravity(Gravity.CENTER);
ShapeDrawable buttonBackground = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(20), null, null));
Dim.roundedCorners(20), null, null));
buttonBackground.getPaint().setColor(getAdjustedBackgroundColor(false));
speedButton.setBackground(buttonBackground);
speedButton.setPadding(dip4, dip4, dip4, dip4);
speedButton.setPadding(Dim.dp4, Dim.dp4, Dim.dp4, Dim.dp4);
// Center button vertically and stretch horizontally in container.
FrameLayout.LayoutParams buttonParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, dip32, Gravity.CENTER);
FrameLayout.LayoutParams.MATCH_PARENT, Dim.dp32, Gravity.CENTER);
speedButton.setLayoutParams(buttonParams);
// Add speed buttons view to buttons container layout.
@@ -475,21 +467,18 @@ public class CustomPlaybackSpeedPatch {
*
* @param context The Android context used to create the button.
* @param isPlus True to display a plus symbol, false to display a minus symbol.
* @param marginStart The start margin in pixels (left for LTR, right for RTL).
* @param marginEnd The end margin in pixels (right for LTR, left for RTL).
* @return A configured {@link Button} with the specified styling and layout parameters.
*/
private static Button createStyledButton(Context context, boolean isPlus, int marginStart, int marginEnd) {
private static Button createStyledButton(Context context, boolean isPlus) {
Button button = new Button(context, null, 0); // Disable default theme style.
button.setText(""); // No text on button.
ShapeDrawable background = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(20), null, null));
Dim.roundedCorners(20), null, null));
background.getPaint().setColor(getAdjustedBackgroundColor(false));
button.setBackground(background);
button.setForeground(new OutlineSymbolDrawable(isPlus)); // Plus or minus symbol.
final int dip36 = dipToPixels(36);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dip36, dip36);
params.setMargins(marginStart, 0, marginEnd, 0); // Set margins.
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(Dim.dp36, Dim.dp36);
params.setMargins(Dim.dp8, 0, Dim.dp8, 0); // Set margins.
button.setLayoutParams(params);
return button;
}
@@ -554,7 +543,7 @@ class OutlineSymbolDrawable extends Drawable {
paint = new Paint(Paint.ANTI_ALIAS_FLAG); // Enable anti-aliasing for smooth rendering.
paint.setColor(Utils.getAppForegroundColor());
paint.setStyle(Paint.Style.STROKE); // Use stroke style for outline.
paint.setStrokeWidth(dipToPixels(1)); // 1dp stroke width.
paint.setStrokeWidth(Dim.dp1); // 1dp stroke width.
}
@Override

View File

@@ -0,0 +1,85 @@
package app.revanced.extension.youtube.patches.theme;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
/**
* Dynamic drawable that is either the regular or bolded ReVanced preference icon.
*
* This is needed because the YouTube ReVanced preference intent is an AndroidX preference,
* and AndroidX classes are not built into Android which makes programmatically changing
* the preference thru patching overly complex. This solves the problem by using a drawable
* wrapper to dynamically pick which icon drawable to use at runtime.
*/
@SuppressWarnings("unused")
public class ReVancedSettingsIconDynamicDrawable extends Drawable {
private final Drawable icon;
public ReVancedSettingsIconDynamicDrawable() {
final int resId = Utils.getResourceIdentifier(ResourceType.DRAWABLE,
Utils.appIsUsingBoldIcons()
? "revanced_settings_icon_bold"
: "revanced_settings_icon"
);
icon = Utils.getContext().getDrawable(resId);
}
@Override
public void draw(@NonNull Canvas canvas) {
icon.draw(canvas);
}
@Override
public void setAlpha(int alpha) {
icon.setAlpha(alpha);
}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {
icon.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return icon.getOpacity();
}
@Override
public int getIntrinsicWidth() {
return icon.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
return icon.getIntrinsicHeight();
}
@Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
icon.setBounds(left, top, right, bottom);
}
@Override
public void setBounds(@NonNull Rect bounds) {
super.setBounds(bounds);
icon.setBounds(bounds);
}
@Override
public void onBoundsChange(@NonNull Rect bounds) {
super.onBoundsChange(bounds);
icon.setBounds(bounds);
}
}

View File

@@ -16,6 +16,7 @@ import java.util.Arrays;
import java.util.Scanner;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.settings.Settings;
@@ -101,16 +102,6 @@ public final class SeekbarColorPatch {
return customSeekbarColor;
}
/**
* injection point.
*/
public static boolean useLotteLaunchSplashScreen(boolean original) {
// This method is only used for development purposes to force the old style launch screen.
// Forcing this off on some devices can cause unexplained startup crashes,
// where the lottie animation is still used even though this condition appears to bypass it.
return original; // false = drawable style, true = lottie style.
}
/**
* Injection point.
* Modern Lottie style animation.

View File

@@ -40,6 +40,7 @@ import java.util.concurrent.TimeoutException;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.youtube.returnyoutubedislike.requests.RYDVoteData;
import app.revanced.extension.youtube.returnyoutubedislike.requests.ReturnYouTubeDislikeApi;
import app.revanced.extension.youtube.settings.Settings;
@@ -124,12 +125,12 @@ public class ReturnYouTubeDislike {
static {
leftSeparatorBounds = new Rect(0, 0,
Utils.dipToPixels(1.2f),
Utils.dipToPixels(14f));
final int middleSeparatorSize = Utils.dipToPixels(3.7f);
Dim.dp(1.2f),
Dim.dp(14f));
final int middleSeparatorSize = Dim.dp(3.7f);
middleSeparatorBounds = new Rect(0, 0, middleSeparatorSize, middleSeparatorSize);
leftSeparatorShapePaddingPixels = Utils.dipToPixels(8.4f);
leftSeparatorShapePaddingPixels = Dim.dp(8.4f);
leftSeparatorShape = new ShapeDrawable(new RectShape());
leftSeparatorShape.setBounds(leftSeparatorBounds);
@@ -259,7 +260,8 @@ public class ReturnYouTubeDislike {
// middle separator
String middleSeparatorString = compactLayout
? " " + MIDDLE_SEPARATOR_CHARACTER + " "
: " \u2009" + MIDDLE_SEPARATOR_CHARACTER + "\u2009 "; // u2009 = 'narrow space' character
: " \u2009\u2009" + MIDDLE_SEPARATOR_CHARACTER + "\u2009\u2009 "; // u2009 = 'narrow space'
final int shapeInsertionIndex = middleSeparatorString.length() / 2;
Spannable middleSeparatorSpan = new SpannableString(middleSeparatorString);
ShapeDrawable shapeDrawable = new ShapeDrawable(new OvalShape());
@@ -554,7 +556,8 @@ public class ReturnYouTubeDislike {
if (originalDislikeSpan != null && replacementLikeDislikeSpan != null
&& spansHaveEqualTextAndColor(original, originalDislikeSpan)) {
Logger.printDebug(() -> "Replacing span with previously created dislike span of data: " + videoId);
Logger.printDebug(() -> "Replacing span: " + original + " with " +
"previously created dislike span of data: " + videoId);
return replacementLikeDislikeSpan;
}

View File

@@ -3,6 +3,7 @@ package app.revanced.extension.youtube.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.settings.Setting.parentNot;
import static app.revanced.extension.shared.settings.Setting.parentsAll;
import static app.revanced.extension.shared.settings.Setting.parentsAny;
import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormFactor;
@@ -44,7 +45,7 @@ import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrow
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailOption;
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.ThumbnailStillTime;
import app.revanced.extension.youtube.patches.MiniplayerPatch;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
@@ -95,7 +96,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_CHIPS_SHELF = new BooleanSetting("revanced_hide_chips_shelf", TRUE);
public static final BooleanSetting HIDE_COMMUNITY_POSTS = new BooleanSetting("revanced_hide_community_posts", FALSE);
public static final BooleanSetting HIDE_COMPACT_BANNER = new BooleanSetting("revanced_hide_compact_banner", TRUE);
public static final BooleanSetting HIDE_CROWDFUNDING_BOX = new BooleanSetting("revanced_hide_crowdfunding_box", FALSE, true);
public static final BooleanSetting HIDE_DOODLES = new BooleanSetting("revanced_hide_doodles", FALSE, true, "revanced_hide_doodles_user_dialog_message");
public static final BooleanSetting HIDE_EXPANDABLE_CARD = new BooleanSetting("revanced_hide_expandable_card", TRUE);
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_FEED = new BooleanSetting("revanced_hide_filter_bar_feed_in_feed", FALSE, true);
@@ -115,6 +115,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_UPLOAD_TIME = new BooleanSetting("revanced_hide_upload_time", FALSE, "revanced_hide_upload_time_user_dialog_message");
public static final BooleanSetting HIDE_VIDEO_RECOMMENDATION_LABELS = new BooleanSetting("revanced_hide_video_recommendation_labels", TRUE);
public static final BooleanSetting HIDE_VIEW_COUNT = new BooleanSetting("revanced_hide_view_count", FALSE, "revanced_hide_view_count_user_dialog_message");
public static final BooleanSetting HIDE_VISUAL_SPACER = new BooleanSetting("revanced_hide_visual_spacer", TRUE);
// Alternative thumbnails
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
@@ -136,11 +137,13 @@ public class Settings extends BaseSettings {
parentsAny(HIDE_KEYWORD_CONTENT_HOME, HIDE_KEYWORD_CONTENT_SUBSCRIPTIONS, HIDE_KEYWORD_CONTENT_SEARCH));
// Channel page
public static final BooleanSetting HIDE_COMMUNITY_BUTTON = new BooleanSetting("revanced_hide_community_button", TRUE);
public static final BooleanSetting HIDE_FOR_YOU_SHELF = new BooleanSetting("revanced_hide_for_you_shelf", FALSE);
public static final BooleanSetting HIDE_JOIN_BUTTON = new BooleanSetting("revanced_hide_join_button", FALSE);
public static final BooleanSetting HIDE_LINKS_PREVIEW = new BooleanSetting("revanced_hide_links_preview", TRUE);
public static final BooleanSetting HIDE_MEMBERS_SHELF = new BooleanSetting("revanced_hide_members_shelf", TRUE);
public static final BooleanSetting HIDE_VISIT_COMMUNITY_BUTTON = new BooleanSetting("revanced_hide_visit_community_button", TRUE);
public static final BooleanSetting HIDE_VISIT_STORE_BUTTON = new BooleanSetting("revanced_hide_visit_store_button", TRUE);
public static final BooleanSetting HIDE_STORE_BUTTON = new BooleanSetting("revanced_hide_store_button", TRUE);
public static final BooleanSetting HIDE_SUBSCRIBE_BUTTON_IN_CHANNEL_PAGE = new BooleanSetting("revanced_hide_subscribe_button_in_channel_page", FALSE);
// Player
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
@@ -154,6 +157,8 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
public static final BooleanSetting HIDE_CHANNEL_BAR = new BooleanSetting("revanced_hide_channel_bar", FALSE);
public static final BooleanSetting HIDE_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
public static final BooleanSetting HIDE_CROWDFUNDING_BOX = new BooleanSetting("revanced_hide_crowdfunding_box", FALSE, 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_END_SCREEN_SUGGESTED_VIDEO = new BooleanSetting("revanced_end_screen_suggested_video", FALSE, true);
@@ -168,7 +173,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_RELATED_VIDEOS = new BooleanSetting("revanced_hide_related_videos", FALSE);
public static final BooleanSetting HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_subscribers_community_guidelines", TRUE);
public static final BooleanSetting HIDE_TIMED_REACTIONS = new BooleanSetting("revanced_hide_timed_reactions", TRUE);
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final BooleanSetting VIDEO_QUALITY_DIALOG_BUTTON = new BooleanSetting("revanced_video_quality_dialog_button", FALSE);
@@ -183,7 +187,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting MINIPLAYER_DOUBLE_TAP_ACTION = new BooleanSetting("revanced_miniplayer_double_tap_action", TRUE, true, new MiniplayerAnyModernAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_OVERLAY_BUTTONS = new BooleanSetting("revanced_miniplayer_hide_overlay_buttons", FALSE, true, new MiniplayerHideOverlayButtonsAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, new MiniplayerHideSubtextsAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, new MiniplayerPatch.MiniplayerHideRewindOrOverlayOpacityAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, new MiniplayerHideRewindOrOverlayOpacityAvailability());
public static final IntegerSetting MINIPLAYER_WIDTH_DIP = new IntegerSetting("revanced_miniplayer_width_dip", 192, true, new MiniplayerAnyModernAvailability());
public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, new MiniplayerHideRewindOrOverlayOpacityAvailability());
@@ -213,6 +217,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_HOW_THIS_WAS_MADE_SECTION = new BooleanSetting("revanced_hide_how_this_was_made_section", FALSE);
public static final BooleanSetting HIDE_HYPE_POINTS = new BooleanSetting("revanced_hide_hype_points", FALSE);
public static final BooleanSetting HIDE_INFO_CARDS_SECTION = new BooleanSetting("revanced_hide_info_cards_section", TRUE);
public static final BooleanSetting HIDE_FEATURED_LINKS_SECTION = new BooleanSetting("revanced_hide_featured_links_section", FALSE, parentNot(HIDE_INFO_CARDS_SECTION));
public static final BooleanSetting HIDE_FEATURED_VIDEOS_SECTION = new BooleanSetting("revanced_hide_featured_videos_section", FALSE, parentNot(HIDE_INFO_CARDS_SECTION));
public static final BooleanSetting HIDE_SUBSCRIBE_BUTTON = new BooleanSetting("revanced_hide_subscribe_button", FALSE, parentNot(HIDE_INFO_CARDS_SECTION));
public static final BooleanSetting HIDE_KEY_CONCEPTS_SECTION = new BooleanSetting("revanced_hide_key_concepts_section", FALSE);
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);
@@ -226,11 +233,11 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_HYPE_BUTTON = new BooleanSetting("revanced_hide_hype_button", FALSE);
public static final BooleanSetting HIDE_LIKE_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_like_dislike_button", FALSE);
public static final BooleanSetting HIDE_PROMOTE_BUTTON = new BooleanSetting("revanced_hide_promote_button", FALSE);
public static final BooleanSetting HIDE_REMIX_BUTTON = new BooleanSetting("revanced_hide_remix_button", TRUE);
public static final BooleanSetting HIDE_REMIX_BUTTON = new BooleanSetting("revanced_hide_remix_button", FALSE);
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
public static final BooleanSetting HIDE_SAVE_BUTTON = new BooleanSetting("revanced_hide_save_button", FALSE);
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
public static final BooleanSetting HIDE_SHOP_BUTTON = new BooleanSetting("revanced_hide_shop_button", FALSE);
public static final BooleanSetting HIDE_SHOP_BUTTON = new BooleanSetting("revanced_hide_shop_button", TRUE);
public static final BooleanSetting HIDE_STOP_ADS_BUTTON = new BooleanSetting("revanced_hide_stop_ads_button", TRUE);
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
@@ -281,6 +288,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_hide_notifications_button", FALSE, true);
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true,
"revanced_switch_create_with_notifications_button_user_dialog_message");
public static final BooleanSetting NAVIGATION_BAR_ANIMATIONS = new BooleanSetting("revanced_navigation_bar_animations", FALSE);
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true,
"revanced_disable_translucent_status_bar_user_dialog_message");
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true);
@@ -290,6 +298,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
public static final EnumSetting<ShortsPlayerType> SHORTS_PLAYER_TYPE = new EnumSetting<>("revanced_shorts_player_type", ShortsPlayerType.SHORTS_PLAYER);
public static final BooleanSetting HIDE_SHORTS_AUTO_DUBBED_LABEL = new BooleanSetting("revanced_hide_shorts_auto_dubbed_label", FALSE);
public static final BooleanSetting HIDE_SHORTS_CHANNEL_BAR = new BooleanSetting("revanced_hide_shorts_channel_bar", FALSE);
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
@@ -304,11 +313,12 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SHORTS_JOIN_BUTTON = new BooleanSetting("revanced_hide_shorts_join_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_LIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_like_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_LIKE_FOUNTAIN = new BooleanSetting("revanced_hide_shorts_like_fountain", TRUE);
public static final BooleanSetting HIDE_SHORTS_LIVE_PREVIEW = new BooleanSetting("revanced_hide_shorts_live_preview", FALSE);
public static final BooleanSetting HIDE_SHORTS_LOCATION_LABEL = new BooleanSetting("revanced_hide_shorts_location_label", FALSE);
public static final BooleanSetting HIDE_SHORTS_NAVIGATION_BAR = new BooleanSetting("revanced_hide_shorts_navigation_bar", FALSE, true);
public static final BooleanSetting HIDE_SHORTS_PAUSED_OVERLAY_BUTTONS = new BooleanSetting("revanced_hide_shorts_paused_overlay_buttons", FALSE);
public static final BooleanSetting HIDE_SHORTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_shorts_preview_comment", TRUE);
public static final BooleanSetting HIDE_SHORTS_REMIX_BUTTON = new BooleanSetting("revanced_hide_shorts_remix_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_REMIX_BUTTON = new BooleanSetting("revanced_hide_shorts_remix_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_SAVE_SOUND_BUTTON = new BooleanSetting("revanced_hide_shorts_save_sound_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_SEARCH = new BooleanSetting("revanced_hide_shorts_search", FALSE);
public static final BooleanSetting HIDE_SHORTS_SEARCH_SUGGESTIONS = new BooleanSetting("revanced_hide_shorts_search_suggestions", TRUE);
@@ -332,6 +342,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", FALSE);
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true);
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE, true);
public static final BooleanSetting FULLSCREEN_LARGE_SEEKBAR = new BooleanSetting("revanced_fullscreen_large_seekbar", FALSE);
public static final BooleanSetting HIDE_TIMESTAMP = new BooleanSetting("revanced_hide_timestamp", FALSE);
public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE);
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", FALSE);
@@ -466,6 +477,13 @@ public class Settings extends BaseSettings {
static {
// region Migration
// 20.37+ YT removed parts of the code for the legacy tablet miniplayer.
// This check must remain until the Tablet type is eventually removed.
if (VersionCheckPatch.IS_20_37_OR_GREATER && MINIPLAYER_TYPE.get() == MiniplayerType.TABLET) {
Logger.printInfo(() -> "Resetting miniplayer tablet type");
MINIPLAYER_TYPE.resetToDefault();
}
// Migrate renamed change header enums.
if (HEADER_LOGO.get() == HeaderLogo.REVANCED) {
HEADER_LOGO.save(HeaderLogo.ROUNDED);
@@ -508,6 +526,14 @@ public class Settings extends BaseSettings {
SPOOF_APP_VERSION.resetToDefault();
}
if (!BaseSettings.SETTINGS_DISABLE_BOLD_ICONS.get() && SPOOF_APP_VERSION.get()
&& SPOOF_APP_VERSION_TARGET.get().compareTo("19.35.00") <= 0) {
Logger.printInfo(() -> "Temporarily disabling bold icons that don't work with old spoof targets");
// Don't save and only temporarily overwrite the value so
// if spoofing is turned off the old setting value is used.
BooleanSetting.privateSetValue(BaseSettings.SETTINGS_DISABLE_BOLD_ICONS, false);
}
// VR 1.61 is not selectable in the settings, and it's selected by spoof stream patch if needed.
if (SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.ANDROID_VR_1_61_48) {
SPOOF_VIDEO_STREAMS_CLIENT_TYPE.resetToDefault();

View File

@@ -7,6 +7,7 @@ import android.preference.PreferenceFragment;
import android.view.View;
import android.widget.Toolbar;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseActivityHook;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
@@ -15,11 +16,28 @@ import app.revanced.extension.youtube.settings.preference.YouTubePreferenceFragm
import app.revanced.extension.youtube.settings.search.YouTubeSearchViewController;
/**
* Hooks LicenseActivity to inject a custom {@link YouTubePreferenceFragment} with a toolbar and search functionality.
* Hooks LicenseActivity to inject a custom {@link YouTubePreferenceFragment}
* with a toolbar and search functionality.
*/
@SuppressWarnings("deprecation")
public class YouTubeActivityHook extends BaseActivityHook {
/**
* How much time has passed since the first launch of the app. Simple check to prevent
* forcing bold icons on first launch where the settings menu is partially broken
* due to missing icon resources the client has not yet received.
*/
private static final long MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS = 30 * 1000; // 30 seconds.
private static final boolean USE_BOLD_ICONS = VersionCheckPatch.IS_20_31_OR_GREATER
&& !Settings.SETTINGS_DISABLE_BOLD_ICONS.get()
&& (System.currentTimeMillis() - Settings.FIRST_TIME_APP_LAUNCHED.get())
> MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS;
static {
Utils.setAppIsUsingBoldIcons(USE_BOLD_ICONS);
}
private static int currentThemeValueOrdinal = -1; // Must initially be a non-valid enum ordinal value.
/**
@@ -44,15 +62,7 @@ public class YouTubeActivityHook extends BaseActivityHook {
final var theme = Utils.isDarkModeEnabled()
? "Theme.YouTube.Settings.Dark"
: "Theme.YouTube.Settings";
activity.setTheme(Utils.getResourceIdentifierOrThrow(theme, "style"));
}
/**
* Returns the resource ID for the YouTube settings layout.
*/
@Override
protected int getContentViewResourceId() {
return LAYOUT_REVANCED_SETTINGS_WITH_TOOLBAR;
activity.setTheme(Utils.getResourceIdentifierOrThrow(ResourceType.STYLE, theme));
}
/**
@@ -155,4 +165,12 @@ public class YouTubeActivityHook extends BaseActivityHook {
public static boolean handleBackPress() {
return YouTubeSearchViewController.handleFinish(searchViewController);
}
/**
* Injection point.
*/
@SuppressWarnings("unused")
public static boolean useBoldIcons(boolean original) {
return USE_BOLD_ICONS;
}
}

View File

@@ -2,7 +2,6 @@ package app.revanced.extension.youtube.settings.preference;
import static app.revanced.extension.shared.StringRef.sf;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import android.app.Dialog;
import android.content.Context;
@@ -15,13 +14,9 @@ import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.TypedValue;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import androidx.annotation.Nullable;
@@ -37,6 +32,7 @@ import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.CustomDialogListPreference;
import app.revanced.extension.shared.ui.CustomDialog;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.youtube.settings.Settings;
/**
@@ -264,42 +260,12 @@ public class ExternalDownloaderPreference extends CustomDialogListPreference {
// Add ListView to content layout with initial height.
LinearLayout.LayoutParams listViewParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
0 // Initial height, will be updated.
);
listViewParams.bottomMargin = dipToPixels(16);
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.0f);
listViewParams.bottomMargin = Dim.dp16;
contentLayout.addView(listView, listViewParams);
// Add EditText for custom package name.
editText = new EditText(context);
editText.setText(packageName);
editText.setSelection(packageName.length());
editText.setHint(str("revanced_external_downloader_other_item_hint"));
editText.setSingleLine(true); // Restrict EditText to a single line.
editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
// Set initial EditText state based on selected downloader.
editText.setEnabled(usingCustomDownloader);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable edit) {
String updatedPackageName = edit.toString().trim();
updateListViewSelection.apply(updatedPackageName);
}
});
ShapeDrawable editTextBackground = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(10), null, null));
editTextBackground.getPaint().setColor(Utils.getEditTextBackground());
final int dip8 = dipToPixels(8);
editText.setPadding(dip8, dip8, dip8, dip8);
editText.setBackground(editTextBackground);
editText.setClipToOutline(true);
editText = createEditText(context, packageName, usingCustomDownloader, updateListViewSelection);
contentLayout.addView(editText);
// Create the custom dialog.
@@ -350,50 +316,59 @@ public class ExternalDownloaderPreference extends CustomDialogListPreference {
);
// Add the content layout directly to the dialog's main layout.
LinearLayout dialogMainLayout = dialogPair.second;
dialogMainLayout.addView(contentLayout, dialogMainLayout.getChildCount() - 1);
LinearLayout mainLayout = dialogPair.second;
LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 0, 1.0f);
// Update ListView height dynamically based on orientation.
//noinspection ExtractMethodRecommender
Runnable updateListViewHeight = () -> {
int totalHeight = 0;
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter != null) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
final int listAdapterCount = listAdapter.getCount();
for (int i = 0; i < listAdapterCount; i++) {
View item = listAdapter.getView(i, null, listView);
item.measure(
View.MeasureSpec.makeMeasureSpec(metrics.widthPixels, View.MeasureSpec.AT_MOST),
View.MeasureSpec.UNSPECIFIED
);
totalHeight += item.getMeasuredHeight();
}
totalHeight += listView.getDividerHeight() * (listAdapterCount - 1);
// Insert content before the dialog button row.
mainLayout.addView(contentLayout, mainLayout.getChildCount() - 1, contentParams);
Dialog dialog = dialogPair.first;
dialog.show();
}
/**
* Creates and configures the EditText for the custom package name.
*
* @param context Context for creating views.
* @param initialPackageName The package name to pre-fill.
* @param isCustom Whether the "Other" option is selected.
* @param textChangeCallback Callback to run when text changes.
* @return A configured EditText.
*/
private EditText createEditText(Context context,
String initialPackageName, boolean isCustom,
Function<String, Void> textChangeCallback) {
EditText editText = new EditText(context);
editText.setText(initialPackageName);
editText.setSelection(initialPackageName.length());
editText.setHint(str("revanced_external_downloader_other_item_hint"));
editText.setSingleLine(true);
editText.setTextSize(16);
editText.setEnabled(isCustom);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable edit) {
String updatedPackageName = edit.toString().trim();
textChangeCallback.apply(updatedPackageName);
}
});
final int orientation = context.getResources().getConfiguration().orientation;
if (orientation == android.content.res.Configuration.ORIENTATION_PORTRAIT) {
// In portrait orientation, use WRAP_CONTENT for ListView height.
listViewParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
} else {
// In landscape orientation, limit ListView height to 30% of screen height.
final int maxHeight = Utils.percentageHeightToPixels(30);
listViewParams.height = Math.min(totalHeight, maxHeight);
}
listView.setLayoutParams(listViewParams);
};
ShapeDrawable editTextBackground = new ShapeDrawable(new RoundRectShape(
Dim.roundedCorners(10), null, null));
editTextBackground.getPaint().setColor(Utils.getEditTextBackground());
editText.setPadding(Dim.dp8, Dim.dp8, Dim.dp8, Dim.dp8);
editText.setBackground(editTextBackground);
editText.setClipToOutline(true);
// Initial height calculation.
updateListViewHeight.run();
// Listen for configuration changes (e.g., orientation).
View dialogView = dialogPair.second;
// Recalculate height when layout changes (e.g., orientation change).
dialogView.getViewTreeObserver().addOnGlobalLayoutListener(updateListViewHeight::run);
// Show the dialog.
dialogPair.first.show();
return editText;
}
/**

View File

@@ -19,8 +19,10 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -72,7 +74,7 @@ public final class NavigationBar {
*/
public static boolean isSearchBarActive() {
View searchbarResults = searchBarResultsRef.get();
return searchbarResults != null && searchbarResults.getParent() != null;
return searchbarResults != null && searchbarResults.isShown();
}
public static boolean isBackButtonVisible() {
@@ -277,12 +279,14 @@ public final class NavigationBar {
}
/**
* Use the bundled non cairo filled icon instead of a custom icon.
* Use the old non cairo filled icon, which is almost identical to
* the what would be the filled cairo icon.
* Custom cairo notification filled icon to fix unpatched app missing resource.
*/
private static final int fillBellCairoBlack = Utils.getResourceIdentifier(
"yt_fill_bell_black_24", "drawable");
private static final int fillBellCairoBlack = Utils.getResourceIdentifier(ResourceType.DRAWABLE,
// The bold cairo notification filled icon is present,
// but YT still has not fixed the icon not associated to the enum.
VersionCheckPatch.IS_20_31_OR_GREATER && !Settings.SETTINGS_DISABLE_BOLD_ICONS.get()
? "yt_fill_experimental_bell_vd_theme_24"
: "revanced_fill_bell_cairo_black_24");
/**
* Injection point.
@@ -290,13 +294,12 @@ public final class NavigationBar {
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
if (fillBellCairoBlack != 0) {
// Show a popup informing this fix is no longer needed to those who might care.
if (BaseSettings.DEBUG.get() && enumMap.containsKey(tabActivityCairo)) {
Logger.printException(() -> "YouTube fixed the cairo notification icons");
}
enumMap.putIfAbsent(tabActivityCairo, fillBellCairoBlack);
// Show a popup informing this fix is no longer needed to those who might care.
if (BaseSettings.DEBUG.get() && enumMap.containsKey(tabActivityCairo)) {
Logger.printException(() -> "YouTube fixed the notification icons");
}
enumMap.putIfAbsent(tabActivityCairo, fillBellCairoBlack);
}
public enum NavigationButton {

View File

@@ -3,6 +3,7 @@ package app.revanced.extension.youtube.shared
import android.app.Activity
import android.view.View
import android.view.ViewGroup
import app.revanced.extension.shared.ResourceType
import app.revanced.extension.shared.Utils
import java.lang.ref.WeakReference
@@ -19,13 +20,13 @@ class PlayerControlsVisibilityObserverImpl(
* id of the direct parent of controls_layout, R.id.youtube_controls_overlay
*/
private val controlsLayoutParentId =
Utils.getResourceIdentifier(activity, "youtube_controls_overlay", "id")
Utils.getResourceIdentifier(activity, ResourceType.ID, "youtube_controls_overlay")
/**
* id of R.id.controls_layout
*/
private val controlsLayoutId =
Utils.getResourceIdentifier(activity, "controls_layout", "id")
Utils.getResourceIdentifier(activity, ResourceType.ID, "controls_layout")
/**
* reference to the controls layout view

View File

@@ -2,7 +2,6 @@ package app.revanced.extension.youtube.shared
import app.revanced.extension.shared.Logger
import app.revanced.extension.youtube.Event
import app.revanced.extension.youtube.patches.VideoInformation
/**
* Regular player type.

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.youtube.sponsorblock;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
import android.annotation.SuppressLint;
@@ -25,7 +24,6 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -34,6 +32,7 @@ import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
@@ -82,7 +81,7 @@ public class SegmentPlaybackController {
* Highlight segments have zero length as they are a point in time.
* Draw them on screen using a fixed width bar.
*/
private static final int HIGHLIGHT_SEGMENT_DRAW_BAR_WIDTH = dipToPixels(7);
private static final int HIGHLIGHT_SEGMENT_DRAW_BAR_WIDTH = Dim.dp7;
@Nullable
private static String currentVideoId;
@@ -150,9 +149,9 @@ public class SegmentPlaybackController {
private static long skipSegmentButtonEndTime;
@Nullable
private static String timeWithoutSegments;
private static int sponsorBarAbsoluteLeft;
private static int sponsorAbsoluteBarRight;
private static int sponsorBarThickness;
private static int seekbarAbsoluteLeft;
private static int seekbarAbsoluteRight;
private static int seekbarThickness;
@Nullable
private static SponsorSegment lastSegmentSkipped;
@@ -808,14 +807,12 @@ public class SegmentPlaybackController {
LinearLayout mainLayout = new LinearLayout(currentContext);
mainLayout.setOrientation(LinearLayout.VERTICAL);
final int dip8 = dipToPixels(8);
final int dip16 = dipToPixels(16);
mainLayout.setPadding(dip16, dip8, dip16, dip8);
mainLayout.setPadding(Dim.dp16, Dim.dp8, Dim.dp16, Dim.dp8);
mainLayout.setGravity(Gravity.CENTER);
mainLayout.setMinimumHeight(dipToPixels(48));
mainLayout.setMinimumHeight(Dim.dp48);
ShapeDrawable background = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(20), null, null));
Dim.roundedCorners(20), null, null));
background.getPaint().setColor(Utils.getDialogBackgroundColor());
mainLayout.setBackground(background);
@@ -910,31 +907,13 @@ public class SegmentPlaybackController {
* injection point.
*/
@SuppressWarnings("unused")
public static void setSponsorBarRect(Object self) {
try {
Field field = self.getClass().getDeclaredField("replaceMeWithsetSponsorBarRect");
field.setAccessible(true);
Rect rect = (Rect) Objects.requireNonNull(field.get(self));
setSponsorBarAbsoluteLeft(rect);
setSponsorBarAbsoluteRight(rect);
} catch (Exception ex) {
Logger.printException(() -> "setSponsorBarRect failure", ex);
}
}
private static void setSponsorBarAbsoluteLeft(Rect rect) {
final int left = rect.left;
if (sponsorBarAbsoluteLeft != left) {
Logger.printDebug(() -> "setSponsorBarAbsoluteLeft: " + left);
sponsorBarAbsoluteLeft = left;
}
}
private static void setSponsorBarAbsoluteRight(Rect rect) {
final int right = rect.right;
if (sponsorAbsoluteBarRight != right) {
Logger.printDebug(() -> "setSponsorBarAbsoluteRight: " + right);
sponsorAbsoluteBarRight = right;
public static void setSeekbarRectangle(Rect seekbarRect) {
final int left = seekbarRect.left;
final int right = seekbarRect.right;
if (seekbarAbsoluteLeft != left || seekbarAbsoluteRight != right) {
Logger.printDebug(() -> "setSeekbarRectangle left: " + left + " right: " + right);
seekbarAbsoluteLeft = left;
seekbarAbsoluteRight = right;
}
}
@@ -942,8 +921,8 @@ public class SegmentPlaybackController {
* injection point.
*/
@SuppressWarnings("unused")
public static void setSponsorBarThickness(int thickness) {
sponsorBarThickness = thickness;
public static void setSeekbarThickness(int thickness) {
seekbarThickness = thickness;
}
/**
@@ -953,8 +932,7 @@ public class SegmentPlaybackController {
public static String appendTimeWithoutSegments(String totalTime) {
try {
if (Settings.SB_ENABLED.get() && Settings.SB_VIDEO_LENGTH_WITHOUT_SEGMENTS.get()
&& !TextUtils.isEmpty(totalTime) && !TextUtils.isEmpty(timeWithoutSegments)
&& !isAdProgressTextVisible()) {
&& !TextUtils.isEmpty(totalTime) && !TextUtils.isEmpty(timeWithoutSegments)) {
// Force LTR layout, to match the same LTR video time/length layout YouTube uses for all languages
return "\u202D" + totalTime + timeWithoutSegments; // u202D = left to right override
}
@@ -982,6 +960,7 @@ public class SegmentPlaybackController {
continue;
}
foundNonhighlightSegments = true;
long start = segment.start;
final long end = segment.end;
// To prevent nested segments from incorrectly counting additional time,
@@ -1013,17 +992,17 @@ public class SegmentPlaybackController {
* Injection point.
*/
@SuppressWarnings("unused")
public static void drawSponsorTimeBars(final Canvas canvas, final float posY) {
public static void drawSegmentTimeBars(final Canvas canvas, final float posY) {
try {
if (segments == null || isAdProgressTextVisible()) return;
if (segments == null) return;
final long videoLength = VideoInformation.getVideoLength();
if (videoLength <= 0) return;
final int thicknessDiv2 = sponsorBarThickness / 2; // rounds down
final float top = posY - (sponsorBarThickness - thicknessDiv2);
final int thicknessDiv2 = seekbarThickness / 2; // Rounds down.
final float top = posY - (seekbarThickness - thicknessDiv2);
final float bottom = posY + thicknessDiv2;
final float videoMillisecondsToPixels = (1f / videoLength) * (sponsorAbsoluteBarRight - sponsorBarAbsoluteLeft);
final float leftPadding = sponsorBarAbsoluteLeft;
final float videoMillisecondsToPixels = (1f / videoLength) * (seekbarAbsoluteRight - seekbarAbsoluteLeft);
final float leftPadding = seekbarAbsoluteLeft;
for (SponsorSegment segment : segments) {
final float left = leftPadding + segment.start * videoMillisecondsToPixels;

View File

@@ -1,7 +1,6 @@
package app.revanced.extension.youtube.sponsorblock.objects;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings.migrateOldColorString;
import android.content.Context;
@@ -17,6 +16,7 @@ import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.ColorPickerPreference;
import app.revanced.extension.shared.ui.ColorDot;
import app.revanced.extension.shared.ui.Dim;
@SuppressWarnings("deprecation")
public class SegmentCategoryPreference extends ColorPickerPreference {
@@ -110,7 +110,7 @@ public class SegmentCategoryPreference extends ColorPickerPreference {
}
radioGroup.setOnCheckedChangeListener((group, checkedId) -> selectedDialogEntryIndex = checkedId);
radioGroup.setPadding(dipToPixels(10), 0, dipToPixels(10), dipToPixels(10));
radioGroup.setPadding(Dim.dp10, 0, Dim.dp10, Dim.dp10);
return radioGroup;
}

View File

@@ -1,16 +1,26 @@
package app.revanced.extension.youtube.sponsorblock.ui;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
@SuppressWarnings("unused")
public class CreateSegmentButton {
private static final int DRAWABLE_SB_LOGO = Utils.getResourceIdentifierOrThrow(
ResourceType.DRAWABLE, Utils.appIsUsingBoldIcons()
? "revanced_sb_logo_bold"
: "revanced_sb_logo"
);
@Nullable
private static PlayerControlButton instance;
@@ -31,6 +41,14 @@ public class CreateSegmentButton {
v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility(),
null
);
// FIXME: Bold YT player icons are currently forced off.
// Enable this logic when the new player icons are not forced off.
ImageView icon = Utils.getChildViewByResourceName(controlsView,
"revanced_sb_create_segment_button");
if (false) {
icon.setImageResource(DRAWABLE_SB_LOGO);
}
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}

View File

@@ -15,6 +15,8 @@ import android.widget.FrameLayout;
import android.widget.ImageButton;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
@@ -44,8 +46,8 @@ public final class NewSegmentLayout extends FrameLayout {
final int defStyleAttr, final int defStyleRes) {
super(context, attributeSet, defStyleAttr, defStyleRes);
LayoutInflater.from(context).inflate(
getResourceIdentifierOrThrow(context, "revanced_sb_new_segment", "layout"), this, true
LayoutInflater.from(context).inflate(getResourceIdentifierOrThrow(context,
ResourceType.LAYOUT, "revanced_sb_new_segment"), this, true
);
initializeButton(
@@ -104,7 +106,7 @@ public final class NewSegmentLayout extends FrameLayout {
*/
private void initializeButton(final Context context, final String resourceIdentifierName,
final ButtonOnClickHandlerFunction handler, final String debugMessage) {
ImageButton button = findViewById(getResourceIdentifierOrThrow(context, resourceIdentifierName, "id"));
ImageButton button = findViewById(getResourceIdentifierOrThrow(context, ResourceType.ID, resourceIdentifierName));
// Add ripple effect
RippleDrawable rippleDrawable = new RippleDrawable(
@@ -132,9 +134,7 @@ public final class NewSegmentLayout extends FrameLayout {
GradientDrawable backgroundDrawable = new GradientDrawable();
backgroundDrawable.setColor(getResourceColor("skip_ad_button_background_color"));
final float cornerRadius = squareLayout
? 0
: 16 * getResources().getDisplayMetrics().density;
final float cornerRadius = squareLayout ? 0f : Dim.dp16;
backgroundDrawable.setCornerRadius(cornerRadius);
setBackground(backgroundDrawable);
}

View File

@@ -21,6 +21,7 @@ import androidx.annotation.NonNull;
import java.util.Objects;
import app.revanced.extension.shared.ResourceType;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
@@ -57,11 +58,10 @@ public class SkipSponsorButton extends FrameLayout {
public SkipSponsorButton(Context context, AttributeSet attributeSet, int defStyleAttr, int defStyleRes) {
super(context, attributeSet, defStyleAttr, defStyleRes);
LayoutInflater.from(context).inflate(getResourceIdentifierOrThrow(context,
"revanced_sb_skip_sponsor_button", "layout"), this, true); // layout:skip_ad_button
LayoutInflater.from(context).inflate(getResourceIdentifierOrThrow(context, ResourceType.LAYOUT, "revanced_sb_skip_sponsor_button"), this, true); // layout:skip_ad_button
setMinimumHeight(getResourceDimensionPixelSize("ad_skip_ad_button_min_height")); // dimen:ad_skip_ad_button_min_height
skipSponsorBtnContainer = Objects.requireNonNull(findViewById(getResourceIdentifierOrThrow(
context, "revanced_sb_skip_sponsor_button_container", "id"))); // id:skip_ad_button_container
context, ResourceType.ID, "revanced_sb_skip_sponsor_button_container"))); // id:skip_ad_button_container
background = new Paint();
background.setColor(getResourceColor("skip_ad_button_background_color")); // color:skip_ad_button_background_color);
@@ -72,7 +72,7 @@ public class SkipSponsorButton extends FrameLayout {
border.setStrokeWidth(getResourceDimension("ad_skip_ad_button_border_width")); // dimen:ad_skip_ad_button_border_width);
border.setStyle(Paint.Style.STROKE);
skipSponsorTextView = Objects.requireNonNull(findViewById(getResourceIdentifier(context, "revanced_sb_skip_sponsor_button_text", "id"))); // id:skip_ad_button_text;
skipSponsorTextView = Objects.requireNonNull(findViewById(getResourceIdentifier(context, ResourceType.ID, "revanced_sb_skip_sponsor_button_text"))); // id:skip_ad_button_text;
defaultBottomMargin = getResourceDimensionPixelSize("skip_button_default_bottom_margin"); // dimen:skip_button_default_bottom_margin
ctaBottomMargin = getResourceDimensionPixelSize("skip_button_cta_bottom_margin"); // dimen:skip_button_cta_bottom_margin

View File

@@ -18,7 +18,6 @@ import android.preference.SwitchPreference;
import android.text.InputType;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
@@ -34,6 +33,7 @@ import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.preference.CustomDialogListPreference;
import app.revanced.extension.shared.settings.preference.ResettableEditTextPreference;
import app.revanced.extension.shared.ui.CustomDialog;
import app.revanced.extension.shared.ui.Dim;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
@@ -507,7 +507,7 @@ public class SponsorBlockPreferenceGroup extends PreferenceGroup {
EditText editText = getEditText();
editText.setInputType(editText.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
editText.setTextSize(TypedValue.COMPLEX_UNIT_PT, 7); // Use a smaller font to reduce text wrap.
editText.setTextSize(14);
// Create a custom dialog.
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
@@ -550,7 +550,7 @@ public class SponsorBlockPreferenceGroup extends PreferenceGroup {
| InputType.TYPE_TEXT_FLAG_MULTI_LINE
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
editText.setAutofillHints((String) null);
editText.setTextSize(TypedValue.COMPLEX_UNIT_PT, 8);
editText.setTextSize(14);
// Set preference listeners.
importExport.setOnPreferenceClickListener(preference1 -> {

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