mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-04-30 14:44:33 +02:00
Compare commits
88 Commits
v5.19.0-de
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f925479a93 | ||
![]() |
6514420719 | ||
![]() |
0e064cd607 | ||
![]() |
a50edf06f6 | ||
![]() |
43bcf5a098 | ||
![]() |
f6f06dcd02 | ||
![]() |
9d30474318 | ||
![]() |
2bbcf9d82c | ||
![]() |
ba83c01700 | ||
![]() |
ebee07ec3a | ||
![]() |
81999d8cd5 | ||
![]() |
d21c1b2a94 | ||
![]() |
eaceab5ee5 | ||
![]() |
4db5d3c3d5 | ||
![]() |
9ab0338a68 | ||
![]() |
433dbc3bf8 | ||
![]() |
255cb5874c | ||
![]() |
fd214046f2 | ||
![]() |
5062e24433 | ||
![]() |
ee9039428c | ||
![]() |
0cb38f9f36 | ||
![]() |
3e64a4c18f | ||
![]() |
cc3b94a3cb | ||
![]() |
dc89be0e94 | ||
![]() |
84bc40cba0 | ||
![]() |
f62bcbf42a | ||
![]() |
11027ec551 | ||
![]() |
79a2c0db3d | ||
![]() |
5ecbe823ed | ||
![]() |
409f98cf65 | ||
![]() |
4c8bf7fbf7 | ||
![]() |
655b39043a | ||
![]() |
564cfd2e38 | ||
![]() |
1ea8047aef | ||
![]() |
4c619fb346 | ||
![]() |
703359f0c1 | ||
![]() |
e2b9d65bc6 | ||
![]() |
1050c77efd | ||
![]() |
f28efcf965 | ||
![]() |
03d0eb2f8c | ||
![]() |
ffc74be822 | ||
![]() |
c5d1702411 | ||
![]() |
42230b0291 | ||
![]() |
b02928cdc6 | ||
![]() |
7b58010076 | ||
![]() |
d639151641 | ||
![]() |
36e25966c8 | ||
![]() |
215fccbaf2 | ||
![]() |
780822f1bd | ||
![]() |
8957325d78 | ||
![]() |
aba6a6b5c5 | ||
![]() |
50f5b1ac54 | ||
![]() |
5b5449e07a | ||
![]() |
6837348c45 | ||
![]() |
95440273a6 | ||
![]() |
a6b5d043fd | ||
![]() |
0ee36939f4 | ||
![]() |
b99c2e5eb7 | ||
![]() |
dcf6178f19 | ||
![]() |
065cc2a015 | ||
![]() |
26b25ef479 | ||
![]() |
7f98474d37 | ||
![]() |
3e18e868bb | ||
![]() |
618e3276e4 | ||
![]() |
89d44da171 | ||
![]() |
4b84742210 | ||
![]() |
47ebec532e | ||
![]() |
48358ff45d | ||
![]() |
a22ba8e4d6 | ||
![]() |
6d7101cb2e | ||
![]() |
d42320d49f | ||
![]() |
56e48f4c89 | ||
![]() |
67fe151d77 | ||
![]() |
152bb7c3ee | ||
![]() |
9387aae7ba | ||
![]() |
e8e5a6776a | ||
![]() |
106202f9eb | ||
![]() |
e5ffd2c353 | ||
![]() |
a3fde874af | ||
![]() |
b91285ec20 | ||
![]() |
649a2c0616 | ||
![]() |
fe1f3ed169 | ||
![]() |
b6e568f719 | ||
![]() |
e3fad97484 | ||
![]() |
ebc5ebc0a9 | ||
![]() |
3864f35501 | ||
![]() |
8e91507b95 | ||
![]() |
b67bbb2996 |
302
CHANGELOG.md
302
CHANGELOG.md
@ -1,3 +1,305 @@
|
|||||||
|
# [5.21.0](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0) (2025-04-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
|
||||||
|
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
|
||||||
|
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
|
||||||
|
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
|
||||||
|
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
|
||||||
|
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
|
||||||
|
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
|
||||||
|
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
|
||||||
|
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([655b390](https://github.com/ReVanced/revanced-patches/commit/655b39043ad77efcb4380de67c3f603666e7bc49))
|
||||||
|
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
|
||||||
|
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
|
||||||
|
|
||||||
|
# [5.21.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.11...v5.21.0-dev.12) (2025-04-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide video action buttons:** Add option to hide 'Ask' button ([#4852](https://github.com/ReVanced/revanced-patches/issues/4852)) ([43bcf5a](https://github.com/ReVanced/revanced-patches/commit/43bcf5a098c9008cc11dc7df9680437d5effbb32))
|
||||||
|
|
||||||
|
# [5.21.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.10...v5.21.0-dev.11) (2025-04-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **GmsCore Support:** Correct the description to refer to the app being patched ([2bbcf9d](https://github.com/ReVanced/revanced-patches/commit/2bbcf9d82ca2f442572a6aa886cc611b0d56ff0a))
|
||||||
|
|
||||||
|
# [5.21.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.9...v5.21.0-dev.10) (2025-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Swipe controls:** Add option for vertical progress bar ([#4811](https://github.com/ReVanced/revanced-patches/issues/4811)) ([ebee07e](https://github.com/ReVanced/revanced-patches/commit/ebee07ec3aba6fd3adbd8e0af30390e197879d89))
|
||||||
|
|
||||||
|
# [5.21.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.8...v5.21.0-dev.9) (2025-04-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide video action buttons:** Hide A/B layout buttons ([4db5d3c](https://github.com/ReVanced/revanced-patches/commit/4db5d3c3d5ac04faf70cc07fb309b324d752e7e3))
|
||||||
|
|
||||||
|
# [5.21.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.7...v5.21.0-dev.8) (2025-04-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Wide search bar:** Fix patching `19.16.39` ([433dbc3](https://github.com/ReVanced/revanced-patches/commit/433dbc3bf81823369e146035c954281e84d3a436))
|
||||||
|
|
||||||
|
# [5.21.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.6...v5.21.0-dev.7) (2025-04-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Change start page:** Add option to always override start page on app launch ([#4832](https://github.com/ReVanced/revanced-patches/issues/4832)) ([5062e24](https://github.com/ReVanced/revanced-patches/commit/5062e24433ba38eba397438e8fde32099109d3c3))
|
||||||
|
|
||||||
|
# [5.21.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.5...v5.21.0-dev.6) (2025-04-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Wide search bar:** Do not force phone layout for tablet devices ([#4827](https://github.com/ReVanced/revanced-patches/issues/4827)) ([0cb38f9](https://github.com/ReVanced/revanced-patches/commit/0cb38f9f367a7fe742d8ca336150049181d637b6))
|
||||||
|
|
||||||
|
# [5.21.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.4...v5.21.0-dev.5) (2025-04-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* `Hide ADB status` patch ([#4814](https://github.com/ReVanced/revanced-patches/issues/4814)) ([dc89be0](https://github.com/ReVanced/revanced-patches/commit/dc89be0e94880733f862b250d95d4848f02c594d))
|
||||||
|
|
||||||
|
# [5.21.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.3...v5.21.0-dev.4) (2025-04-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Disable auto captions:** Correctly hide captions with YT 20.12 ([5ecbe82](https://github.com/ReVanced/revanced-patches/commit/5ecbe823ed5197533328cc37f1de5cd1f048a217))
|
||||||
|
|
||||||
|
# [5.21.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.2...v5.21.0-dev.3) (2025-04-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **X / Twitter:** Support version `10.86.0-release.0` ([#4805](https://github.com/ReVanced/revanced-patches/issues/4805)) ([655b390](https://github.com/ReVanced/revanced-patches/commit/655b39043ad77efcb4380de67c3f603666e7bc49))
|
||||||
|
|
||||||
|
# [5.21.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.21.0-dev.1...v5.21.0-dev.2) (2025-04-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add `Hide ADB status` patch ([#4585](https://github.com/ReVanced/revanced-patches/issues/4585)) ([1ea8047](https://github.com/ReVanced/revanced-patches/commit/1ea8047aefdaa358e9af8038923ac54d68a39176))
|
||||||
|
|
||||||
|
# [5.21.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.1...v5.21.0-dev.1) (2025-04-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube:** Support version `20.12.46` ([#4779](https://github.com/ReVanced/revanced-patches/issues/4779)) ([703359f](https://github.com/ReVanced/revanced-patches/commit/703359f0c16b613c204cf16cf42227b628f664fa))
|
||||||
|
|
||||||
|
## [5.20.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1) (2025-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
|
||||||
|
|
||||||
|
## [5.20.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.20.0...v5.20.1-dev.1) (2025-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Spotify - Custom theme:** Support latest app target ([#4800](https://github.com/ReVanced/revanced-patches/issues/4800)) ([03d0eb2](https://github.com/ReVanced/revanced-patches/commit/03d0eb2f8c0f3e48d53bdab38d34057f2020bb65))
|
||||||
|
|
||||||
|
# [5.20.0](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0) (2025-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
|
||||||
|
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
|
||||||
|
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
|
||||||
|
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
|
||||||
|
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
|
||||||
|
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
|
||||||
|
|
||||||
|
# [5.20.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.6...v5.20.0-dev.7) (2025-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Spotify:** Fix login by replacing `Spoof signature` patch with new `Spoof package info` patch ([#4794](https://github.com/ReVanced/revanced-patches/issues/4794)) ([d639151](https://github.com/ReVanced/revanced-patches/commit/d639151641352ce651037b17fb65bd58953cd51c))
|
||||||
|
|
||||||
|
# [5.20.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.5...v5.20.0-dev.6) (2025-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Duolingo - Hide ads:** Support lastest app release ([#4790](https://github.com/ReVanced/revanced-patches/issues/4790)) ([215fccb](https://github.com/ReVanced/revanced-patches/commit/215fccbaf2fdd54251c46cbda106029eb304996b))
|
||||||
|
|
||||||
|
# [5.20.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.4...v5.20.0-dev.5) (2025-04-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Swipe controls:** Add option to change volume swipe sensitivity (step size) ([#4557](https://github.com/ReVanced/revanced-patches/issues/4557)) ([8957325](https://github.com/ReVanced/revanced-patches/commit/8957325d78eb42e087c4c1ff0abedb2146aa4423))
|
||||||
|
|
||||||
|
# [5.20.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.3...v5.20.0-dev.4) (2025-04-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Spotify - Unlock Spotify Premium:** Remove premium restriction for 'Spotify Connect' ([#4782](https://github.com/ReVanced/revanced-patches/issues/4782)) ([50f5b1a](https://github.com/ReVanced/revanced-patches/commit/50f5b1ac54372542d76e87626f00ddefb54da125))
|
||||||
|
|
||||||
|
# [5.20.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.2...v5.20.0-dev.3) (2025-04-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Remove background playback restrictions:** Restore PiP button functionality after screen is unlocked ([6837348](https://github.com/ReVanced/revanced-patches/commit/6837348c45156d6743a63fef8b6e045087afbda8))
|
||||||
|
|
||||||
|
# [5.20.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.20.0-dev.1...v5.20.0-dev.2) (2025-04-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Spotify - Custom theme:** Add option to use unmodified player background gradient ([#4741](https://github.com/ReVanced/revanced-patches/issues/4741)) ([0ee3693](https://github.com/ReVanced/revanced-patches/commit/0ee36939f43f325afca37119db1cf1af3b63be27))
|
||||||
|
|
||||||
|
# [5.20.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.1...v5.20.0-dev.1) (2025-04-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add `Set target SDK version 34` patch (Disable edge-to-edge display) ([#4780](https://github.com/ReVanced/revanced-patches/issues/4780)) ([dcf6178](https://github.com/ReVanced/revanced-patches/commit/dcf6178f19f86dd1b57d54c855b8c47b086dd33a))
|
||||||
|
|
||||||
|
## [5.19.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1) (2025-04-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([3e18e86](https://github.com/ReVanced/revanced-patches/commit/3e18e868bbd9fd0600fe81a7fe8767b4bd89a00e))
|
||||||
|
* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](https://github.com/ReVanced/revanced-patches/commit/89d44da171c3f56f13112d1d82bc4ea4a56c7c06))
|
||||||
|
|
||||||
|
## [5.19.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.19.1-dev.1...v5.19.1-dev.2) (2025-04-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Google Photos:** Restore patching with ReVanced Manager ([#4773](https://github.com/ReVanced/revanced-patches/issues/4773)) ([3e18e86](https://github.com/ReVanced/revanced-patches/commit/3e18e868bbd9fd0600fe81a7fe8767b4bd89a00e))
|
||||||
|
|
||||||
|
## [5.19.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.19.0...v5.19.1-dev.1) (2025-04-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Spotify:** Restore patching with ReVanced Manager ([#4769](https://github.com/ReVanced/revanced-patches/issues/4769)) ([89d44da](https://github.com/ReVanced/revanced-patches/commit/89d44da171c3f56f13112d1d82bc4ea4a56c7c06))
|
||||||
|
|
||||||
|
# [5.19.0](https://github.com/ReVanced/revanced-patches/compare/v5.18.0...v5.19.0) (2025-04-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([152bb7c](https://github.com/ReVanced/revanced-patches/commit/152bb7c3ee7cf36bc07460e7a3444631ec540441))
|
||||||
|
* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([56e48f4](https://github.com/ReVanced/revanced-patches/commit/56e48f4c89da51f81ff11a79a164eaa5b440690e))
|
||||||
|
* **Spotify - Custom theme:** Override more color resources ([#4690](https://github.com/ReVanced/revanced-patches/issues/4690)) ([d7a7a0b](https://github.com/ReVanced/revanced-patches/commit/d7a7a0b982dbafa181b04f984a5f7618fb067c2a))
|
||||||
|
* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([106202f](https://github.com/ReVanced/revanced-patches/commit/106202f9ebb7699c4ba4ae46b82133e35f1ac6b9))
|
||||||
|
* **Spotify:** Remove ads sections from home ([#4722](https://github.com/ReVanced/revanced-patches/issues/4722)) ([0b9a5e7](https://github.com/ReVanced/revanced-patches/commit/0b9a5e7f89a89d971762b3539166d4f145111481))
|
||||||
|
* **Twitter - Hide recommended users:** Make hiding work again by filtering for new entryId prefix ([#4456](https://github.com/ReVanced/revanced-patches/issues/4456)) ([ff846b0](https://github.com/ReVanced/revanced-patches/commit/ff846b0b7ef5060caaffedb08c1f901172f5b2d1))
|
||||||
|
* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([3864f35](https://github.com/ReVanced/revanced-patches/commit/3864f3550153617e23ad9979fb543d8a7fb4dc0a))
|
||||||
|
* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([b67bbb2](https://github.com/ReVanced/revanced-patches/commit/b67bbb299669336addb68cf52a8ce5b39c68cec0))
|
||||||
|
* **YouTube - Remove background playback restrictions:** Do not show media controls when playing Shorts from the feed ([2ed675c](https://github.com/ReVanced/revanced-patches/commit/2ed675cdd058fb5876381a9d30dee5263f6b2e26))
|
||||||
|
* **YouTube - Return YouTube Dislike:** Correctly update label after disliking a Short with 20.07 ([0bb3e32](https://github.com/ReVanced/revanced-patches/commit/0bb3e32244fa10809aee5c4e549f77ed4054537e))
|
||||||
|
* **YouTube - Return YouTube Dislike:** Fix inconsistent label after disliking a Short ([ea92a2e](https://github.com/ReVanced/revanced-patches/commit/ea92a2e36c7aab3bd115f7d0ec40467179485b32))
|
||||||
|
* **YouTube - Seekbar:** Correctly hide the feed seekbar with target 20.07 ([ddc6e4c](https://github.com/ReVanced/revanced-patches/commit/ddc6e4c34fe35fa34bd859bf34e25645a23dbdc9))
|
||||||
|
* **YouTube:** Combine multiple seekbar patches into a single patch ([#4705](https://github.com/ReVanced/revanced-patches/issues/4705)) ([503b7eb](https://github.com/ReVanced/revanced-patches/commit/503b7eb8d413ef7f248394f128f3b2a6f3192ba6))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Angulus:** Add `Hide ads` patch ([#4604](https://github.com/ReVanced/revanced-patches/issues/4604)) ([87c86b5](https://github.com/ReVanced/revanced-patches/commit/87c86b53a91b0054ac892a3f02bbe7bf83bbf813))
|
||||||
|
* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([e3fad97](https://github.com/ReVanced/revanced-patches/commit/e3fad97484d7eb962aeb53d44a0047b34a881071))
|
||||||
|
* **Photomath:** Support latest version ([#4672](https://github.com/ReVanced/revanced-patches/issues/4672)) ([8e16483](https://github.com/ReVanced/revanced-patches/commit/8e1648322948151e4565fb0d86e0f37d0a02d73f))
|
||||||
|
* **Proton Mail:** Add `Remove 'Sent from' signature` patch ([#4514](https://github.com/ReVanced/revanced-patches/issues/4514)) ([34c14c9](https://github.com/ReVanced/revanced-patches/commit/34c14c9b443092824d035afd77adb678c6f89e3e))
|
||||||
|
* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([6d7101c](https://github.com/ReVanced/revanced-patches/commit/6d7101cb2e546e01a934eff9cad1264367aeafe3))
|
||||||
|
* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([a3fde87](https://github.com/ReVanced/revanced-patches/commit/a3fde874af993125ba7a741820e7bd48e3641b84))
|
||||||
|
* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([649a2c0](https://github.com/ReVanced/revanced-patches/commit/649a2c06161c72a2040b179dbed5b415847d7527))
|
||||||
|
* **YouTube - Settings:** Add icons to the ReVanced settings ([#4496](https://github.com/ReVanced/revanced-patches/issues/4496)) ([d0c85f0](https://github.com/ReVanced/revanced-patches/commit/d0c85f044083d720c63a8ea4ff15d42eefeb9db7))
|
||||||
|
|
||||||
|
# [5.19.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.16...v5.19.0-dev.17) (2025-04-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Spotify:** Add `Check environment` patch ([#4765](https://github.com/ReVanced/revanced-patches/issues/4765)) ([6d7101c](https://github.com/ReVanced/revanced-patches/commit/6d7101cb2e546e01a934eff9cad1264367aeafe3))
|
||||||
|
|
||||||
|
# [5.19.0-dev.16](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.15...v5.19.0-dev.16) (2025-04-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Google Photos:** Remove obsolete non functional patch `Restore hidden 'Back up while charging' toggle` ([#4764](https://github.com/ReVanced/revanced-patches/issues/4764)) ([56e48f4](https://github.com/ReVanced/revanced-patches/commit/56e48f4c89da51f81ff11a79a164eaa5b440690e))
|
||||||
|
|
||||||
|
# [5.19.0-dev.15](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.14...v5.19.0-dev.15) (2025-04-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Google Photos - Restore hidden 'Back up while charging' toggle:** Constrain to last working app target ([#4761](https://github.com/ReVanced/revanced-patches/issues/4761)) ([152bb7c](https://github.com/ReVanced/revanced-patches/commit/152bb7c3ee7cf36bc07460e7a3444631ec540441))
|
||||||
|
|
||||||
|
# [5.19.0-dev.14](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.13...v5.19.0-dev.14) (2025-04-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Spotify - Unlock Spotify Premium:** Remove restrictions for Google voice assistant ([#4702](https://github.com/ReVanced/revanced-patches/issues/4702)) ([106202f](https://github.com/ReVanced/revanced-patches/commit/106202f9ebb7699c4ba4ae46b82133e35f1ac6b9))
|
||||||
|
|
||||||
|
# [5.19.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.12...v5.19.0-dev.13) (2025-04-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Spotify:** Add limited support for version `8.6.98.900` (last version that supports Kenwood and Pioneer car stereos) ([#4750](https://github.com/ReVanced/revanced-patches/issues/4750)) ([a3fde87](https://github.com/ReVanced/revanced-patches/commit/a3fde874af993125ba7a741820e7bd48e3641b84))
|
||||||
|
|
||||||
|
# [5.19.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.11...v5.19.0-dev.12) (2025-04-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Strava - Disable subscription suggestions:** Make compatible with latest version ([#4739](https://github.com/ReVanced/revanced-patches/issues/4739)) ([649a2c0](https://github.com/ReVanced/revanced-patches/commit/649a2c06161c72a2040b179dbed5b415847d7527))
|
||||||
|
|
||||||
|
# [5.19.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.10...v5.19.0-dev.11) (2025-04-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Messenger:** Add `Remove Meta AI tab` patch ([#4726](https://github.com/ReVanced/revanced-patches/issues/4726)) ([e3fad97](https://github.com/ReVanced/revanced-patches/commit/e3fad97484d7eb962aeb53d44a0047b34a881071))
|
||||||
|
|
||||||
|
# [5.19.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.9...v5.19.0-dev.10) (2025-04-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide layout components:** Do not hide video description music/game links if hide horizontal shelves is enabled ([3864f35](https://github.com/ReVanced/revanced-patches/commit/3864f3550153617e23ad9979fb543d8a7fb4dc0a))
|
||||||
|
|
||||||
|
# [5.19.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.8...v5.19.0-dev.9) (2025-04-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Hide player flyout menu items:** Show more detailed summary text for 'Hide Audio track' if using Android spoof client ([#4756](https://github.com/ReVanced/revanced-patches/issues/4756)) ([b67bbb2](https://github.com/ReVanced/revanced-patches/commit/b67bbb299669336addb68cf52a8ce5b39c68cec0))
|
||||||
|
|
||||||
# [5.19.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.7...v5.19.0-dev.8) (2025-04-09)
|
# [5.19.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.19.0-dev.7...v5.19.0-dev.8) (2025-04-09)
|
||||||
|
|
||||||
|
|
||||||
|
16
extensions/all/misc/adb/hide-adb/build.gradle.kts
Normal file
16
extensions/all/misc/adb/hide-adb/build.gradle.kts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
android {
|
||||||
|
namespace = "app.revanced.extension"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 21
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(libs.annotation)
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
@ -0,0 +1,28 @@
|
|||||||
|
package app.revanced.extension.all.misc.hide.adb;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public final class HideAdbPatch {
|
||||||
|
private static final List<String> SPOOF_SETTINGS = Arrays.asList("adb_enabled", "adb_wifi_enabled", "development_settings_enabled");
|
||||||
|
|
||||||
|
public static int getInt(ContentResolver cr, String name) throws Settings.SettingNotFoundException {
|
||||||
|
if (SPOOF_SETTINGS.contains(name)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Settings.Global.getInt(cr, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInt(ContentResolver cr, String name, int def) {
|
||||||
|
if (SPOOF_SETTINGS.contains(name)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Settings.Global.getInt(cr, name, def);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.all.connectivity.wifi.spoof;
|
package app.revanced.extension.all.misc.connectivity.wifi.spoof;
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.all.screencapture.removerestriction;
|
package app.revanced.extension.all.misc.screencapture.removerestriction;
|
||||||
|
|
||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.all.screenshot.removerestriction;
|
package app.revanced.extension.all.misc.screenshot.removerestriction;
|
||||||
|
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
@ -342,9 +342,12 @@ public abstract class Setting<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Identical to calling {@link #save(Object)} using {@link #defaultValue}.
|
* Identical to calling {@link #save(Object)} using {@link #defaultValue}.
|
||||||
|
*
|
||||||
|
* @return The newly saved default value.
|
||||||
*/
|
*/
|
||||||
public void resetToDefault() {
|
public T resetToDefault() {
|
||||||
save(defaultValue);
|
save(defaultValue);
|
||||||
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -204,7 +204,7 @@ public class StreamingDataRequest {
|
|||||||
// but empty response body does.
|
// but empty response body does.
|
||||||
if (connection.getContentLength() == 0) {
|
if (connection.getContentLength() == 0) {
|
||||||
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
|
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
|
||||||
Utils.showToastShort("Ignoring empty spoof stream client: " + clientType);
|
Utils.showToastShort("Debug: Ignoring empty spoof stream client " + clientType);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
|
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
|
||||||
|
@ -3,7 +3,6 @@ package app.revanced.extension.spotify.misc;
|
|||||||
import static java.lang.Boolean.FALSE;
|
import static java.lang.Boolean.FALSE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
|
|
||||||
import com.spotify.remoteconfig.internal.AccountAttribute;
|
|
||||||
import com.spotify.home.evopage.homeapi.proto.Section;
|
import com.spotify.home.evopage.homeapi.proto.Section;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -15,6 +14,25 @@ import app.revanced.extension.shared.Logger;
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class UnlockPremiumPatch {
|
public final class UnlockPremiumPatch {
|
||||||
|
|
||||||
|
private static final String SPOTIFY_MAIN_ACTIVITY_LEGACY = "com.spotify.music.MainActivity";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the app target is 8.6.98.900.
|
||||||
|
*/
|
||||||
|
private static final boolean IS_SPOTIFY_LEGACY_APP_TARGET;
|
||||||
|
|
||||||
|
static {
|
||||||
|
boolean legacy;
|
||||||
|
try {
|
||||||
|
Class.forName(SPOTIFY_MAIN_ACTIVITY_LEGACY);
|
||||||
|
legacy = true;
|
||||||
|
} catch (ClassNotFoundException ex) {
|
||||||
|
legacy = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IS_SPOTIFY_LEGACY_APP_TARGET = legacy;
|
||||||
|
}
|
||||||
|
|
||||||
private static class OverrideAttribute {
|
private static class OverrideAttribute {
|
||||||
/**
|
/**
|
||||||
* Account attribute key.
|
* Account attribute key.
|
||||||
@ -55,13 +73,16 @@ public final class UnlockPremiumPatch {
|
|||||||
// Make sure playing songs is not disabled remotely and playlists show up.
|
// Make sure playing songs is not disabled remotely and playlists show up.
|
||||||
new OverrideAttribute("streaming", TRUE),
|
new OverrideAttribute("streaming", TRUE),
|
||||||
// Allows adding songs to queue and removes the smart shuffle mode restriction,
|
// Allows adding songs to queue and removes the smart shuffle mode restriction,
|
||||||
// allowing to pick any of the other modes.
|
// allowing to pick any of the other modes. Flag is not present in legacy app target.
|
||||||
new OverrideAttribute("pick-and-shuffle", FALSE),
|
new OverrideAttribute("pick-and-shuffle", FALSE, !IS_SPOTIFY_LEGACY_APP_TARGET),
|
||||||
// Disables shuffle-mode streaming-rule, which forces songs to be played shuffled
|
// Disables shuffle-mode streaming-rule, which forces songs to be played shuffled
|
||||||
// and breaks the player when other patches are applied.
|
// and breaks the player when other patches are applied.
|
||||||
new OverrideAttribute("streaming-rules", ""),
|
new OverrideAttribute("streaming-rules", ""),
|
||||||
// Enables premium UI in settings and removes the premium button in the nav-bar.
|
// Enables premium UI in settings and removes the premium button in the nav-bar.
|
||||||
new OverrideAttribute("nft-disabled", "1"),
|
new OverrideAttribute("nft-disabled", "1"),
|
||||||
|
// Enable Spotify Connect and disable other premium related UI, like buying premium.
|
||||||
|
// It also removes the download button.
|
||||||
|
new OverrideAttribute("type", "premium"),
|
||||||
// Enable Spotify Car Thing hardware device.
|
// Enable Spotify Car Thing hardware device.
|
||||||
// Device is discontinued and no longer works with the latest releases,
|
// Device is discontinued and no longer works with the latest releases,
|
||||||
// but it might still work with older app targets.
|
// but it might still work with older app targets.
|
||||||
@ -76,9 +97,9 @@ public final class UnlockPremiumPatch {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override attributes injection point.
|
* Injection point. Override account attributes.
|
||||||
*/
|
*/
|
||||||
public static void overrideAttribute(Map<String, AccountAttribute> attributes) {
|
public static void overrideAttribute(Map<String, /*AccountAttribute*/ Object> attributes) {
|
||||||
try {
|
try {
|
||||||
for (var override : OVERRIDES) {
|
for (var override : OVERRIDES) {
|
||||||
var attribute = attributes.get(override.key);
|
var attribute = attributes.get(override.key);
|
||||||
@ -87,7 +108,12 @@ public final class UnlockPremiumPatch {
|
|||||||
Logger.printException(() -> "'" + override.key + "' expected but not found");
|
Logger.printException(() -> "'" + override.key + "' expected but not found");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
attribute.value_ = override.overrideValue;
|
Object overrideValue = override.overrideValue;
|
||||||
|
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||||
|
((com.spotify.useraccount.v1.AccountAttribute) attribute).value_ = overrideValue;
|
||||||
|
} else {
|
||||||
|
((com.spotify.remoteconfig.internal.AccountAttribute) attribute).value_ = overrideValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@ -96,7 +122,14 @@ public final class UnlockPremiumPatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove ads sections from home injection point.
|
* Injection point. Remove station data from Google assistant URI.
|
||||||
|
*/
|
||||||
|
public static String removeStationString(String spotifyUriOrUrl) {
|
||||||
|
return spotifyUriOrUrl.replace("spotify:station:", "spotify:");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point. Remove ads sections from home.
|
||||||
*/
|
*/
|
||||||
public static void removeHomeSections(List<Section> sections) {
|
public static void removeHomeSections(List<Section> sections) {
|
||||||
try {
|
try {
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.spotify.useraccount.v1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for target 8.6.98.900. Class is still present in newer app targets.
|
||||||
|
*/
|
||||||
|
public class AccountAttribute {
|
||||||
|
public Object value_;
|
||||||
|
}
|
@ -9,6 +9,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@ -81,6 +82,13 @@ public final class ChangeStartPagePatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ChangeStartPageTypeAvailability implements Setting.Availability {
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return Settings.CHANGE_START_PAGE.get() != StartPage.DEFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intent action when YouTube is cold started from the launcher.
|
* Intent action when YouTube is cold started from the launcher.
|
||||||
* <p>
|
* <p>
|
||||||
@ -93,6 +101,8 @@ public final class ChangeStartPagePatch {
|
|||||||
|
|
||||||
private static final StartPage START_PAGE = Settings.CHANGE_START_PAGE.get();
|
private static final StartPage START_PAGE = Settings.CHANGE_START_PAGE.get();
|
||||||
|
|
||||||
|
private static final boolean CHANGE_START_PAGE_ALWAYS = Settings.CHANGE_START_PAGE_ALWAYS.get();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* There is an issue where the back button on the toolbar doesn't work properly.
|
* There is an issue where the back button on the toolbar doesn't work properly.
|
||||||
* As a workaround for this issue, instead of overriding the browserId multiple times, just override it once.
|
* As a workaround for this issue, instead of overriding the browserId multiple times, just override it once.
|
||||||
@ -104,13 +114,13 @@ public final class ChangeStartPagePatch {
|
|||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appLaunched) {
|
if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
|
||||||
Logger.printDebug(() -> "Ignore override browseId as the app already launched");
|
Logger.printDebug(() -> "Ignore override browseId as the app already launched");
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
appLaunched = true;
|
appLaunched = true;
|
||||||
|
|
||||||
Logger.printDebug(() -> "Changing browseId to " + START_PAGE.id);
|
Logger.printDebug(() -> "Changing browseId to: " + START_PAGE.id);
|
||||||
return START_PAGE.id;
|
return START_PAGE.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,14 +135,14 @@ public final class ChangeStartPagePatch {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appLaunched) {
|
if (!CHANGE_START_PAGE_ALWAYS && appLaunched) {
|
||||||
Logger.printDebug(() -> "Ignore override intent action as the app already launched");
|
Logger.printDebug(() -> "Ignore override intent action as the app already launched");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
appLaunched = true;
|
appLaunched = true;
|
||||||
|
|
||||||
String intentAction = START_PAGE.id;
|
String intentAction = START_PAGE.id;
|
||||||
Logger.printDebug(() -> "Changing intent action to " + intentAction);
|
Logger.printDebug(() -> "Changing intent action to: " + intentAction);
|
||||||
intent.setAction(intentAction);
|
intent.setAction(intentAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,7 @@ public class CustomPlayerOverlayOpacityPatch {
|
|||||||
|
|
||||||
if (opacity < 0 || opacity > 100) {
|
if (opacity < 0 || opacity > 100) {
|
||||||
Utils.showToastLong(str("revanced_player_overlay_opacity_invalid_toast"));
|
Utils.showToastLong(str("revanced_player_overlay_opacity_invalid_toast"));
|
||||||
Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
|
opacity = Settings.PLAYER_OVERLAY_OPACITY.resetToDefault();
|
||||||
opacity = Settings.PLAYER_OVERLAY_OPACITY.defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PLAYER_OVERLAY_OPACITY_LEVEL = (opacity * 255) / 100;
|
PLAYER_OVERLAY_OPACITY_LEVEL = (opacity * 255) / 100;
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.ShortsPlayerState;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class DisableAutoCaptionsPatch {
|
public class DisableAutoCaptionsPatch {
|
||||||
|
|
||||||
/**
|
private static volatile boolean captionsButtonStatus;
|
||||||
* Used by injected code. Do not delete.
|
|
||||||
*/
|
|
||||||
public static boolean captionsButtonDisabled;
|
|
||||||
|
|
||||||
public static boolean autoCaptionsEnabled() {
|
/**
|
||||||
return Settings.AUTO_CAPTIONS.get()
|
* Injection point.
|
||||||
// Do not use auto captions for Shorts.
|
*/
|
||||||
&& ShortsPlayerState.isOpen();
|
public static boolean disableAutoCaptions() {
|
||||||
|
return Settings.DISABLE_AUTO_CAPTIONS.get() && !captionsButtonStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void setCaptionsButtonStatus(boolean status) {
|
||||||
|
captionsButtonStatus = status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,8 +162,7 @@ public final class MiniplayerPatch {
|
|||||||
|
|
||||||
if (opacity < 0 || opacity > 100) {
|
if (opacity < 0 || opacity > 100) {
|
||||||
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
|
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
|
||||||
Settings.MINIPLAYER_OPACITY.resetToDefault();
|
opacity = Settings.MINIPLAYER_OPACITY.resetToDefault();
|
||||||
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OPACITY_LEVEL = (opacity * 255) / 100;
|
OPACITY_LEVEL = (opacity * 255) / 100;
|
||||||
|
@ -1,11 +1,48 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
package app.revanced.extension.youtube.patches;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.Logger;
|
||||||
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public final class WideSearchbarPatch {
|
public final class WideSearchbarPatch {
|
||||||
|
|
||||||
|
private static final Boolean WIDE_SEARCHBAR_ENABLED = Settings.WIDE_SEARCHBAR.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
public static boolean enableWideSearchbar(boolean original) {
|
public static boolean enableWideSearchbar(boolean original) {
|
||||||
return Settings.WIDE_SEARCHBAR.get() || original;
|
return WIDE_SEARCHBAR_ENABLED || original;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void setActionBar(View view) {
|
||||||
|
try {
|
||||||
|
if (!WIDE_SEARCHBAR_ENABLED) return;
|
||||||
|
|
||||||
|
View searchBarView = Utils.getChildViewByResourceName(view, "search_bar");
|
||||||
|
|
||||||
|
final int paddingLeft = searchBarView.getPaddingLeft();
|
||||||
|
final int paddingRight = searchBarView.getPaddingRight();
|
||||||
|
final int paddingTop = searchBarView.getPaddingTop();
|
||||||
|
final int paddingBottom = searchBarView.getPaddingBottom();
|
||||||
|
final int paddingStart = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
8, Resources.getSystem().getDisplayMetrics());
|
||||||
|
|
||||||
|
if (Utils.isRightToLeftTextLayout()) {
|
||||||
|
searchBarView.setPadding(paddingLeft, paddingTop, paddingStart, paddingBottom);
|
||||||
|
} else {
|
||||||
|
searchBarView.setPadding(paddingStart, paddingTop, paddingRight, paddingBottom);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "setActionBar failure", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ final class ButtonsFilter extends Filter {
|
|||||||
|
|
||||||
bufferFilterPathGroup = new StringFilterGroup(
|
bufferFilterPathGroup = new StringFilterGroup(
|
||||||
null,
|
null,
|
||||||
"|ContainerType|button.eml|"
|
"|ContainerType|button.eml"
|
||||||
);
|
);
|
||||||
|
|
||||||
addPathCallbacks(
|
addPathCallbacks(
|
||||||
@ -43,7 +43,7 @@ final class ButtonsFilter extends Filter {
|
|||||||
),
|
),
|
||||||
new StringFilterGroup(
|
new StringFilterGroup(
|
||||||
Settings.HIDE_DOWNLOAD_BUTTON,
|
Settings.HIDE_DOWNLOAD_BUTTON,
|
||||||
"|download_button.eml|"
|
"|download_button.eml"
|
||||||
),
|
),
|
||||||
new StringFilterGroup(
|
new StringFilterGroup(
|
||||||
Settings.HIDE_PLAYLIST_BUTTON,
|
Settings.HIDE_PLAYLIST_BUTTON,
|
||||||
@ -51,7 +51,7 @@ final class ButtonsFilter extends Filter {
|
|||||||
),
|
),
|
||||||
new StringFilterGroup(
|
new StringFilterGroup(
|
||||||
Settings.HIDE_CLIP_BUTTON,
|
Settings.HIDE_CLIP_BUTTON,
|
||||||
"|clip_button.eml|"
|
"|clip_button.eml"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -68,15 +68,19 @@ final class ButtonsFilter extends Filter {
|
|||||||
Settings.HIDE_REMIX_BUTTON,
|
Settings.HIDE_REMIX_BUTTON,
|
||||||
"yt_outline_youtube_shorts_plus"
|
"yt_outline_youtube_shorts_plus"
|
||||||
),
|
),
|
||||||
|
new ByteArrayFilterGroup(
|
||||||
|
Settings.HIDE_THANKS_BUTTON,
|
||||||
|
"yt_outline_dollar_sign_heart"
|
||||||
|
),
|
||||||
|
new ByteArrayFilterGroup(
|
||||||
|
Settings.HIDE_ASK_BUTTON,
|
||||||
|
"yt_fill_spark"
|
||||||
|
),
|
||||||
// Check for clip button both here and using a path filter,
|
// Check for clip button both here and using a path filter,
|
||||||
// as there's a chance the path is a generic action button and won't contain 'clip_button'
|
// as there's a chance the path is a generic action button and won't contain 'clip_button'
|
||||||
new ByteArrayFilterGroup(
|
new ByteArrayFilterGroup(
|
||||||
Settings.HIDE_CLIP_BUTTON,
|
Settings.HIDE_CLIP_BUTTON,
|
||||||
"yt_outline_scissors"
|
"yt_outline_scissors"
|
||||||
),
|
|
||||||
new ByteArrayFilterGroup(
|
|
||||||
Settings.HIDE_THANKS_BUTTON,
|
|
||||||
"yt_outline_dollar_sign_heart"
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
|
|
||||||
compactChannelBarInnerButton = new StringFilterGroup(
|
compactChannelBarInnerButton = new StringFilterGroup(
|
||||||
null,
|
null,
|
||||||
"|button.eml|"
|
"|button.eml"
|
||||||
);
|
);
|
||||||
|
|
||||||
joinMembershipButton = new ByteArrayFilterGroup(
|
joinMembershipButton = new ByteArrayFilterGroup(
|
||||||
@ -454,16 +454,20 @@ public final class LayoutComponentsFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hideShelves() {
|
private static boolean hideShelves() {
|
||||||
// If the player is opened while library is selected,
|
// Horizontal shelves are used for music/game links in video descriptions,
|
||||||
// then filter any recommendations below the player.
|
// such as https://youtube.com/watch?v=W8kI1na3S2M
|
||||||
if (PlayerType.getCurrent().isMaximizedOrFullscreen()
|
if (PlayerType.getCurrent().isMaximizedOrFullscreen()) {
|
||||||
// Or if the search is active while library is selected, then also filter.
|
return false;
|
||||||
|| NavigationBar.isSearchBarActive()) {
|
}
|
||||||
|
|
||||||
|
// Must check search bar after player type, since search results
|
||||||
|
// can be in the background behind an open player.
|
||||||
|
if (NavigationBar.isSearchBarActive()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not hide if the navigation back button is visible,
|
// Do not hide if the navigation back button is visible,
|
||||||
// otherwise the content shelves in the YouTube Movie/Courses pages is hidden.
|
// otherwise the content shelves in the explore/music/courses pages are hidde.
|
||||||
if (NavigationBar.isBackButtonVisible()) {
|
if (NavigationBar.isBackButtonVisible()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,25 @@ package app.revanced.extension.youtube.patches.components;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
|
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
import app.revanced.extension.youtube.shared.PlayerType;
|
import app.revanced.extension.youtube.shared.PlayerType;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class PlayerFlyoutMenuItemsFilter extends Filter {
|
public class PlayerFlyoutMenuItemsFilter extends Filter {
|
||||||
|
|
||||||
|
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
|
||||||
|
private static final boolean AVAILABLE_ON_LAUNCH = SpoofVideoStreamsPatch.notSpoofingToAndroid();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
// Check conditions of launch and now. Otherwise if spoofing is changed
|
||||||
|
// without a restart the setting will show as available when it's not.
|
||||||
|
return AVAILABLE_ON_LAUNCH && SpoofVideoStreamsPatch.notSpoofingToAndroid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList();
|
private final ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList();
|
||||||
|
|
||||||
private final ByteArrayFilterGroup exception;
|
private final ByteArrayFilterGroup exception;
|
||||||
@ -27,7 +40,7 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
|
|||||||
|
|
||||||
addPathCallbacks(
|
addPathCallbacks(
|
||||||
videoQualityMenuFooter,
|
videoQualityMenuFooter,
|
||||||
new StringFilterGroup(null, "overflow_menu_item.eml|")
|
new StringFilterGroup(null, "overflow_menu_item.eml")
|
||||||
);
|
);
|
||||||
|
|
||||||
flyoutFilterGroupList.addAll(
|
flyoutFilterGroupList.addAll(
|
||||||
|
@ -54,12 +54,12 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get();
|
final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get();
|
||||||
|
|
||||||
if (holdSpeed > 0 && holdSpeed <= PLAYBACK_SPEED_MAXIMUM) {
|
if (holdSpeed > 0 && holdSpeed <= PLAYBACK_SPEED_MAXIMUM) {
|
||||||
TAP_AND_HOLD_SPEED = holdSpeed;
|
TAP_AND_HOLD_SPEED = holdSpeed;
|
||||||
} else {
|
} else {
|
||||||
showInvalidCustomSpeedToast();
|
showInvalidCustomSpeedToast();
|
||||||
Settings.SPEED_TAP_AND_HOLD.resetToDefault();
|
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.resetToDefault();
|
||||||
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loadCustomSpeeds();
|
loadCustomSpeeds();
|
||||||
|
@ -7,6 +7,7 @@ import static app.revanced.extension.shared.settings.Setting.migrateOldSettingTo
|
|||||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||||
import static app.revanced.extension.shared.settings.Setting.parentsAny;
|
import static app.revanced.extension.shared.settings.Setting.parentsAny;
|
||||||
import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormFactor;
|
import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormFactor;
|
||||||
|
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.ChangeStartPageTypeAvailability;
|
||||||
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
||||||
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
|
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
|
||||||
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
|
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
|
||||||
@ -19,10 +20,12 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
|
|||||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
|
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
|
||||||
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
|
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
|
||||||
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
|
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
|
||||||
|
import static app.revanced.extension.youtube.patches.components.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability;
|
||||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
|
||||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.MANUAL_SKIP;
|
||||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
|
||||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
|
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
|
||||||
|
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider.SwipeOverlayStyle;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
|
||||||
@ -123,6 +126,7 @@ public class Settings extends BaseSettings {
|
|||||||
// Player
|
// Player
|
||||||
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("revanced_copy_video_url", FALSE);
|
||||||
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_copy_video_url_timestamp", TRUE);
|
||||||
|
public static final BooleanSetting DISABLE_AUTO_CAPTIONS = new BooleanSetting("revanced_disable_auto_captions", FALSE, true);
|
||||||
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
|
public static final BooleanSetting DISABLE_FULLSCREEN_AMBIENT_MODE = new BooleanSetting("revanced_disable_fullscreen_ambient_mode", TRUE, true);
|
||||||
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
|
||||||
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
|
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
|
||||||
@ -195,10 +199,11 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
|
public static final BooleanSetting HIDE_REPORT_BUTTON = new BooleanSetting("revanced_hide_report_button", FALSE);
|
||||||
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
|
public static final BooleanSetting HIDE_SHARE_BUTTON = new BooleanSetting("revanced_hide_share_button", FALSE);
|
||||||
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
|
public static final BooleanSetting HIDE_THANKS_BUTTON = new BooleanSetting("revanced_hide_thanks_button", TRUE);
|
||||||
|
public static final BooleanSetting HIDE_ASK_BUTTON = new BooleanSetting("revanced_hide_ask_button", FALSE);
|
||||||
// Player flyout menu items
|
// Player flyout menu items
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_ADDITIONAL_SETTINGS = new BooleanSetting("revanced_hide_player_flyout_additional_settings", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AMBIENT_MODE = new BooleanSetting("revanced_hide_player_flyout_ambient_mode", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_AUDIO_TRACK = new BooleanSetting("revanced_hide_player_flyout_audio_track", FALSE, new HideAudioFlyoutMenuAvailability());
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_CAPTIONS = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_CAPTIONS = new BooleanSetting("revanced_hide_player_flyout_captions", FALSE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_HELP = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_HELP = new BooleanSetting("revanced_hide_player_flyout_help", TRUE);
|
||||||
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
public static final BooleanSetting HIDE_PLAYER_FLYOUT_LOCK_SCREEN = new BooleanSetting("revanced_hide_player_flyout_lock_screen", FALSE);
|
||||||
@ -220,6 +225,8 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
|
||||||
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
|
||||||
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
|
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
|
||||||
|
public static final BooleanSetting CHANGE_START_PAGE_ALWAYS = new BooleanSetting("revanced_change_start_page_always", FALSE, true,
|
||||||
|
new ChangeStartPageTypeAvailability());
|
||||||
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
|
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", "19.01.34", true, parent(SPOOF_APP_VERSION));
|
||||||
// Custom filter
|
// Custom filter
|
||||||
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
|
||||||
@ -293,7 +300,6 @@ public class Settings extends BaseSettings {
|
|||||||
// Misc
|
// Misc
|
||||||
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
|
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
|
||||||
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
||||||
public static final BooleanSetting AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
|
|
||||||
public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
|
public static final BooleanSetting AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
|
||||||
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
||||||
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
|
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
|
||||||
@ -318,12 +324,15 @@ public class Settings extends BaseSettings {
|
|||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
public static final IntegerSetting SWIPE_MAGNITUDE_THRESHOLD = new IntegerSetting("revanced_swipe_threshold", 30, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final BooleanSetting SWIPE_SHOW_CIRCULAR_OVERLAY = new BooleanSetting("revanced_swipe_show_circular_overlay", FALSE, true,
|
public static final IntegerSetting SWIPE_VOLUME_SENSITIVITY = new IntegerSetting("revanced_swipe_volume_sensitivity", 1, true, parent(SWIPE_VOLUME));
|
||||||
|
public static final EnumSetting<SwipeOverlayStyle> SWIPE_OVERLAY_STYLE = new EnumSetting<>("revanced_swipe_overlay_style", SwipeOverlayStyle.HORIZONTAL,true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final BooleanSetting SWIPE_OVERLAY_MINIMAL_STYLE = new BooleanSetting("revanced_swipe_overlay_minimal_style", FALSE, true,
|
public static final IntegerSetting SWIPE_OVERLAY_TEXT_SIZE = new IntegerSetting("revanced_swipe_text_overlay_size", 14, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
|
public static final IntegerSetting SWIPE_OVERLAY_OPACITY = new IntegerSetting("revanced_swipe_overlay_background_opacity", 60, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
|
public static final StringSetting SWIPE_OVERLAY_PROGRESS_COLOR = new StringSetting("revanced_swipe_overlay_progress_color", "#FFFFFF", true,
|
||||||
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
public static final LongSetting SWIPE_OVERLAY_TIMEOUT = new LongSetting("revanced_swipe_overlay_timeout", 500L, true,
|
||||||
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
|
||||||
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
|
public static final BooleanSetting SWIPE_SAVE_AND_RESTORE_BRIGHTNESS = new BooleanSetting("revanced_swipe_save_and_restore_brightness", TRUE, true, parent(SWIPE_BRIGHTNESS));
|
||||||
@ -401,6 +410,7 @@ public class Settings extends BaseSettings {
|
|||||||
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
|
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
|
||||||
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
|
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
|
||||||
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
|
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
|
||||||
|
private static final BooleanSetting DEPRECATED_AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// region Migration
|
// region Migration
|
||||||
@ -454,6 +464,11 @@ public class Settings extends BaseSettings {
|
|||||||
SPOOF_APP_VERSION_TARGET.resetToDefault();
|
SPOOF_APP_VERSION_TARGET.resetToDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!DEPRECATED_AUTO_CAPTIONS.isSetToDefault()) {
|
||||||
|
DISABLE_AUTO_CAPTIONS.save(true);
|
||||||
|
DEPRECATED_AUTO_CAPTIONS.resetToDefault();
|
||||||
|
}
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region SB import/export callbacks
|
// region SB import/export callbacks
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
package app.revanced.extension.youtube.settings.preference;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.preference.SwitchPreference;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
|
||||||
|
|
||||||
|
@SuppressWarnings({"deprecation", "unused"})
|
||||||
|
public class HideAudioFlyoutMenuPreference extends SwitchPreference {
|
||||||
|
|
||||||
|
{
|
||||||
|
// Audio menu is not available if spoofing to Android client type.
|
||||||
|
if (!SpoofVideoStreamsPatch.notSpoofingToAndroid()) {
|
||||||
|
String summary = str("revanced_hide_player_flyout_audio_track_not_available");
|
||||||
|
setSummary(summary);
|
||||||
|
setSummaryOn(summary);
|
||||||
|
setSummaryOff(summary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
public HideAudioFlyoutMenuPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
public HideAudioFlyoutMenuPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
}
|
@ -1,83 +1,98 @@
|
|||||||
package app.revanced.extension.youtube.swipecontrols
|
package app.revanced.extension.youtube.swipecontrols
|
||||||
|
|
||||||
import android.content.Context
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import app.revanced.extension.shared.Logger
|
||||||
import app.revanced.extension.shared.StringRef.str
|
import app.revanced.extension.shared.StringRef.str
|
||||||
import app.revanced.extension.shared.Utils
|
import app.revanced.extension.shared.Utils
|
||||||
import app.revanced.extension.youtube.settings.Settings
|
import app.revanced.extension.youtube.settings.Settings
|
||||||
import app.revanced.extension.youtube.shared.PlayerType
|
import app.revanced.extension.youtube.shared.PlayerType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* provider for configuration for volume and brightness swipe controls
|
* Provides configuration settings for volume and brightness swipe controls in the YouTube player.
|
||||||
*
|
* Manages enabling/disabling gestures, overlay appearance, and behavior preferences.
|
||||||
* @param context the context to create in
|
|
||||||
*/
|
*/
|
||||||
class SwipeControlsConfigurationProvider(
|
class SwipeControlsConfigurationProvider {
|
||||||
private val context: Context,
|
//region swipe enable
|
||||||
) {
|
|
||||||
//region swipe enable
|
|
||||||
/**
|
/**
|
||||||
* should swipe controls be enabled? (global setting)
|
* Indicates whether swipe controls are enabled globally.
|
||||||
|
* Returns true if either volume or brightness controls are enabled and the video is in fullscreen mode.
|
||||||
*/
|
*/
|
||||||
val enableSwipeControls: Boolean
|
val enableSwipeControls: Boolean
|
||||||
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
|
get() = (enableVolumeControls || enableBrightnessControl) && isFullscreenVideo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should swipe controls for volume be enabled?
|
* Indicates whether swipe controls for adjusting volume are enabled.
|
||||||
*/
|
*/
|
||||||
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
|
val enableVolumeControls = Settings.SWIPE_VOLUME.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should swipe controls for volume be enabled?
|
* Indicates whether swipe controls for adjusting brightness are enabled.
|
||||||
*/
|
*/
|
||||||
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
|
val enableBrightnessControl = Settings.SWIPE_BRIGHTNESS.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is the video player currently in fullscreen mode?
|
* Checks if the video player is currently in fullscreen mode.
|
||||||
*/
|
*/
|
||||||
private val isFullscreenVideo: Boolean
|
private val isFullscreenVideo: Boolean
|
||||||
get() = PlayerType.current == PlayerType.WATCH_WHILE_FULLSCREEN
|
get() = PlayerType.current == PlayerType.WATCH_WHILE_FULLSCREEN
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region keys enable
|
//region keys enable
|
||||||
/**
|
/**
|
||||||
* should volume key controls be overwritten? (global setting)
|
* Indicates whether volume key controls should be overridden by swipe controls.
|
||||||
|
* Returns true if volume controls are enabled and the video is in fullscreen mode.
|
||||||
*/
|
*/
|
||||||
val overwriteVolumeKeyControls: Boolean
|
val overwriteVolumeKeyControls: Boolean
|
||||||
get() = enableVolumeControls && isFullscreenVideo
|
get() = enableVolumeControls && isFullscreenVideo
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region gesture adjustments
|
//region gesture adjustments
|
||||||
/**
|
/**
|
||||||
* should press-to-swipe be enabled?
|
* Indicates whether press-to-swipe mode is enabled, requiring a press before swiping to activate controls.
|
||||||
*/
|
*/
|
||||||
val shouldEnablePressToSwipe: Boolean
|
val shouldEnablePressToSwipe: Boolean
|
||||||
get() = Settings.SWIPE_PRESS_TO_ENGAGE.get()
|
get() = Settings.SWIPE_PRESS_TO_ENGAGE.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* threshold for swipe detection
|
* The threshold for detecting swipe gestures, in pixels.
|
||||||
* this may be called rapidly in onScroll, so we have to load it once and then leave it constant
|
* Loaded once to ensure consistent behavior during rapid scroll events.
|
||||||
*/
|
*/
|
||||||
val swipeMagnitudeThreshold: Int
|
val swipeMagnitudeThreshold: Int
|
||||||
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
|
get() = Settings.SWIPE_MAGNITUDE_THRESHOLD.get()
|
||||||
//endregion
|
|
||||||
|
|
||||||
//region overlay adjustments
|
|
||||||
/**
|
/**
|
||||||
* should the overlay enable haptic feedback?
|
* The sensitivity of volume swipe gestures, determining how much volume changes per swipe.
|
||||||
|
* Resets to default if set to 0, as it would disable swiping.
|
||||||
|
*/
|
||||||
|
val volumeSwipeSensitivity: Int
|
||||||
|
get() {
|
||||||
|
val sensitivity = Settings.SWIPE_VOLUME_SENSITIVITY.get()
|
||||||
|
|
||||||
|
if (sensitivity < 1) {
|
||||||
|
return Settings.SWIPE_VOLUME_SENSITIVITY.resetToDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
return sensitivity
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region overlay adjustments
|
||||||
|
/**
|
||||||
|
* Indicates whether haptic feedback should be enabled for swipe control interactions.
|
||||||
*/
|
*/
|
||||||
val shouldEnableHapticFeedback: Boolean
|
val shouldEnableHapticFeedback: Boolean
|
||||||
get() = Settings.SWIPE_HAPTIC_FEEDBACK.get()
|
get() = Settings.SWIPE_HAPTIC_FEEDBACK.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* how long the overlay should be shown on changes
|
* The duration in milliseconds that the overlay should remain visible after a change.
|
||||||
*/
|
*/
|
||||||
val overlayShowTimeoutMillis: Long
|
val overlayShowTimeoutMillis: Long
|
||||||
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
|
get() = Settings.SWIPE_OVERLAY_TIMEOUT.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the opacity value (0-100%) is converted to an alpha value (0-255) for transparency.
|
* The background opacity of the overlay, converted from a percentage (0-100) to an alpha value (0-255).
|
||||||
* If the opacity value is out of range, it resets to the default and displays a warning message.
|
* Resets to default and shows a toast if the value is out of range.
|
||||||
*/
|
*/
|
||||||
val overlayBackgroundOpacity: Int
|
val overlayBackgroundOpacity: Int
|
||||||
get() {
|
get() {
|
||||||
@ -85,8 +100,7 @@ class SwipeControlsConfigurationProvider(
|
|||||||
|
|
||||||
if (opacity < 0 || opacity > 100) {
|
if (opacity < 0 || opacity > 100) {
|
||||||
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
|
Utils.showToastLong(str("revanced_swipe_overlay_background_opacity_invalid_toast"))
|
||||||
Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
|
opacity = Settings.SWIPE_OVERLAY_OPACITY.resetToDefault()
|
||||||
opacity = Settings.SWIPE_OVERLAY_OPACITY.get()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opacity = opacity * 255 / 100
|
opacity = opacity * 255 / 100
|
||||||
@ -94,55 +108,125 @@ class SwipeControlsConfigurationProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The color of the progress overlay.
|
* The color of the progress bar in the overlay.
|
||||||
|
* Resets to default and shows a toast if the color string is invalid or empty.
|
||||||
*/
|
*/
|
||||||
val overlayProgressColor: Int
|
val overlayProgressColor: Int
|
||||||
get() = 0xBFFFFFFF.toInt()
|
get() {
|
||||||
|
try {
|
||||||
|
@SuppressLint("UseKtx")
|
||||||
|
val color = Color.parseColor(Settings.SWIPE_OVERLAY_PROGRESS_COLOR.get())
|
||||||
|
return (0xBF000000.toInt() or (color and 0xFFFFFF))
|
||||||
|
} catch (ex: IllegalArgumentException) {
|
||||||
|
Logger.printDebug({ "Could not parse color" }, ex)
|
||||||
|
Utils.showToastLong(str("revanced_swipe_overlay_progress_color_invalid_toast"))
|
||||||
|
Settings.SWIPE_OVERLAY_PROGRESS_COLOR.resetToDefault()
|
||||||
|
return overlayProgressColor // Recursively return.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The color used for the background of the progress overlay fill.
|
* The background color used for the filled portion of the progress bar in the overlay.
|
||||||
*/
|
*/
|
||||||
val overlayFillBackgroundPaint: Int
|
val overlayFillBackgroundPaint: Int
|
||||||
get() = 0x80D3D3D3.toInt()
|
get() = 0x80D3D3D3.toInt()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The color used for the text and icons in the overlay.
|
* The color used for text and icons in the overlay.
|
||||||
*/
|
*/
|
||||||
val overlayTextColor: Int
|
val overlayTextColor: Int
|
||||||
get() = Color.WHITE
|
get() = Color.WHITE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flag that determines if the overlay should only show the icon.
|
* The text size in the overlay, in density-independent pixels (dp).
|
||||||
|
* Must be between 1 and 30 dp; resets to default and shows a toast if invalid.
|
||||||
*/
|
*/
|
||||||
val overlayShowOverlayMinimalStyle: Boolean
|
val overlayTextSize: Int
|
||||||
get() = Settings.SWIPE_OVERLAY_MINIMAL_STYLE.get()
|
get() {
|
||||||
|
val size = Settings.SWIPE_OVERLAY_TEXT_SIZE.get()
|
||||||
|
if (size < 1 || size > 30) {
|
||||||
|
Utils.showToastLong(str("revanced_swipe_text_overlay_size_invalid_toast"))
|
||||||
|
return Settings.SWIPE_OVERLAY_TEXT_SIZE.resetToDefault()
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flag that determines if the progress bar should be circular.
|
* Defines the style of the swipe controls overlay, determining its layout and appearance.
|
||||||
|
*
|
||||||
|
* @property isMinimal Indicates whether the style is minimalistic, omitting detailed progress indicators.
|
||||||
|
* @property isHorizontalMinimalCenter Indicates whether the style is a minimal horizontal bar centered vertically.
|
||||||
|
* @property isCircular Indicates whether the style uses a circular progress bar.
|
||||||
|
* @property isVertical Indicates whether the style uses a vertical progress bar.
|
||||||
*/
|
*/
|
||||||
val isCircularProgressBar: Boolean
|
@Suppress("unused")
|
||||||
get() = Settings.SWIPE_SHOW_CIRCULAR_OVERLAY.get()
|
enum class SwipeOverlayStyle(
|
||||||
//endregion
|
val isMinimal: Boolean = false,
|
||||||
|
val isHorizontalMinimalCenter: Boolean = false,
|
||||||
|
val isCircular: Boolean = false,
|
||||||
|
val isVertical: Boolean = false
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* A full horizontal progress bar with detailed indicators.
|
||||||
|
*/
|
||||||
|
HORIZONTAL,
|
||||||
|
|
||||||
//region behaviour
|
/**
|
||||||
|
* A minimal horizontal progress bar positioned at the top.
|
||||||
|
*/
|
||||||
|
HORIZONTAL_MINIMAL_TOP(isMinimal = true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A minimal horizontal progress bar centered vertically.
|
||||||
|
*/
|
||||||
|
HORIZONTAL_MINIMAL_CENTER(isMinimal = true, isHorizontalMinimalCenter = true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A full circular progress bar with detailed indicators.
|
||||||
|
*/
|
||||||
|
CIRCULAR(isCircular = true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A minimal circular progress bar.
|
||||||
|
*/
|
||||||
|
CIRCULAR_MINIMAL(isMinimal = true, isCircular = true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A full vertical progress bar with detailed indicators.
|
||||||
|
*/
|
||||||
|
VERTICAL(isVertical = true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A minimal vertical progress bar.
|
||||||
|
*/
|
||||||
|
VERTICAL_MINIMAL(isMinimal = true, isVertical = true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should the brightness be saved and restored when exiting or entering fullscreen
|
* The current style of the overlay, determining its layout and appearance.
|
||||||
|
*/
|
||||||
|
val overlayStyle: SwipeOverlayStyle
|
||||||
|
get() = Settings.SWIPE_OVERLAY_STYLE.get()
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region behaviour
|
||||||
|
/**
|
||||||
|
* Indicates whether the brightness level should be saved and restored when entering or exiting fullscreen mode.
|
||||||
*/
|
*/
|
||||||
val shouldSaveAndRestoreBrightness: Boolean
|
val shouldSaveAndRestoreBrightness: Boolean
|
||||||
get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get()
|
get() = Settings.SWIPE_SAVE_AND_RESTORE_BRIGHTNESS.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* should auto-brightness be enabled at the lowest value of the brightness gesture
|
* Indicates whether auto-brightness should be enabled when the brightness gesture reaches its lowest value.
|
||||||
*/
|
*/
|
||||||
val shouldLowestValueEnableAutoBrightness: Boolean
|
val shouldLowestValueEnableAutoBrightness: Boolean
|
||||||
get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get()
|
get() = Settings.SWIPE_LOWEST_VALUE_ENABLE_AUTO_BRIGHTNESS.get()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* variable that stores the brightness gesture value in the settings
|
* The saved brightness value for the swipe gesture, used to restore brightness in fullscreen mode.
|
||||||
*/
|
*/
|
||||||
var savedScreenBrightnessValue: Float
|
var savedScreenBrightnessValue: Float
|
||||||
get() = Settings.SWIPE_BRIGHTNESS_VALUE.get()
|
get() = Settings.SWIPE_BRIGHTNESS_VALUE.get()
|
||||||
set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value)
|
set(value) = Settings.SWIPE_BRIGHTNESS_VALUE.save(value)
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
@ -23,9 +23,7 @@ import java.lang.ref.WeakReference
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The main controller for volume and brightness swipe controls.
|
* The main controller for volume and brightness swipe controls.
|
||||||
* note that the superclass is overwritten to the superclass of the MainActivity at patch time
|
* note that the superclass is overwritten to the superclass of the MainActivity at patch time.
|
||||||
*
|
|
||||||
* @smali Lapp/revanced/extension/swipecontrols/SwipeControlsHostActivity;
|
|
||||||
*/
|
*/
|
||||||
class SwipeControlsHostActivity : Activity() {
|
class SwipeControlsHostActivity : Activity() {
|
||||||
/**
|
/**
|
||||||
@ -127,7 +125,7 @@ class SwipeControlsHostActivity : Activity() {
|
|||||||
private fun initialize() {
|
private fun initialize() {
|
||||||
// create controllers
|
// create controllers
|
||||||
printDebug { "initializing swipe controls controllers" }
|
printDebug { "initializing swipe controls controllers" }
|
||||||
config = SwipeControlsConfigurationProvider(this)
|
config = SwipeControlsConfigurationProvider()
|
||||||
keys = VolumeKeysController(this)
|
keys = VolumeKeysController(this)
|
||||||
audio = createAudioController()
|
audio = createAudioController()
|
||||||
screen = createScreenController()
|
screen = createScreenController()
|
||||||
|
@ -41,7 +41,7 @@ class VolumeKeysController(
|
|||||||
private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean {
|
private fun handleVolumeKeyEvent(event: KeyEvent, volumeUp: Boolean): Boolean {
|
||||||
if (event.action == KeyEvent.ACTION_DOWN) {
|
if (event.action == KeyEvent.ACTION_DOWN) {
|
||||||
controller.audio?.apply {
|
controller.audio?.apply {
|
||||||
volume += if (volumeUp) 1 else -1
|
volume += controller.config.volumeSwipeSensitivity * if (volumeUp) 1 else -1
|
||||||
controller.overlay.onVolumeChanged(volume, maxVolume)
|
controller.overlay.onVolumeChanged(volume, maxVolume)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ abstract class BaseGestureController(
|
|||||||
controller.overlay,
|
controller.overlay,
|
||||||
10,
|
10,
|
||||||
1,
|
1,
|
||||||
|
controller.config.volumeSwipeSensitivity,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +41,7 @@ interface VolumeAndBrightnessScroller {
|
|||||||
* @param overlayController overlay controller instance
|
* @param overlayController overlay controller instance
|
||||||
* @param volumeDistance unit distance for volume scrolling, in dp
|
* @param volumeDistance unit distance for volume scrolling, in dp
|
||||||
* @param brightnessDistance unit distance for brightness scrolling, in dp
|
* @param brightnessDistance unit distance for brightness scrolling, in dp
|
||||||
|
* @param volumeSwipeSensitivity how much volume will change by single swipe
|
||||||
*/
|
*/
|
||||||
class VolumeAndBrightnessScrollerImpl(
|
class VolumeAndBrightnessScrollerImpl(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -49,6 +50,7 @@ class VolumeAndBrightnessScrollerImpl(
|
|||||||
private val overlayController: SwipeControlsOverlay,
|
private val overlayController: SwipeControlsOverlay,
|
||||||
volumeDistance: Int = 10,
|
volumeDistance: Int = 10,
|
||||||
brightnessDistance: Int = 1,
|
brightnessDistance: Int = 1,
|
||||||
|
private val volumeSwipeSensitivity: Int,
|
||||||
) : VolumeAndBrightnessScroller {
|
) : VolumeAndBrightnessScroller {
|
||||||
|
|
||||||
// region volume
|
// region volume
|
||||||
@ -60,7 +62,7 @@ class VolumeAndBrightnessScrollerImpl(
|
|||||||
),
|
),
|
||||||
) { _, _, direction ->
|
) { _, _, direction ->
|
||||||
volumeController?.run {
|
volumeController?.run {
|
||||||
volume += direction
|
volume += direction * volumeSwipeSensitivity
|
||||||
overlayController.onVolumeChanged(volume, maxVolume)
|
overlayController.onVolumeChanged(volume, maxVolume)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package app.revanced.extension.youtube.swipecontrols.views
|
package app.revanced.extension.youtube.swipecontrols.views
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.Resources
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Rect
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
@ -11,21 +14,30 @@ import android.util.AttributeSet
|
|||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
|
import app.revanced.extension.shared.StringRef.str
|
||||||
import app.revanced.extension.shared.Utils
|
import app.revanced.extension.shared.Utils
|
||||||
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
|
import app.revanced.extension.youtube.swipecontrols.SwipeControlsConfigurationProvider
|
||||||
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
|
import app.revanced.extension.youtube.swipecontrols.misc.SwipeControlsOverlay
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import kotlin.math.max
|
||||||
import kotlin.math.round
|
import kotlin.math.round
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main overlay layout for displaying volume and brightness level with both circular and horizontal progress bars.
|
* Convert dp to pixels based on system display density.
|
||||||
|
*/
|
||||||
|
fun Float.toDisplayPixels(): Float {
|
||||||
|
return this * Resources.getSystem().displayMetrics.density
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main overlay layout for displaying volume and brightness level with circular, horizontal and vertical progress bars.
|
||||||
*/
|
*/
|
||||||
class SwipeControlsOverlayLayout(
|
class SwipeControlsOverlayLayout(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val config: SwipeControlsConfigurationProvider,
|
private val config: SwipeControlsConfigurationProvider,
|
||||||
) : RelativeLayout(context), SwipeControlsOverlay {
|
) : RelativeLayout(context), SwipeControlsOverlay {
|
||||||
|
|
||||||
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider(context))
|
constructor(context: Context) : this(context, SwipeControlsConfigurationProvider())
|
||||||
|
|
||||||
// Drawable icons for brightness and volume
|
// Drawable icons for brightness and volume
|
||||||
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
private val autoBrightnessIcon: Drawable = getDrawable("revanced_ic_sc_brightness_auto")
|
||||||
@ -51,18 +63,21 @@ class SwipeControlsOverlayLayout(
|
|||||||
// Initialize progress bars
|
// Initialize progress bars
|
||||||
private val circularProgressView: CircularProgressView
|
private val circularProgressView: CircularProgressView
|
||||||
private val horizontalProgressView: HorizontalProgressView
|
private val horizontalProgressView: HorizontalProgressView
|
||||||
|
private val verticalBrightnessProgressView: VerticalProgressView
|
||||||
|
private val verticalVolumeProgressView: VerticalProgressView
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Initialize circular progress bar
|
// Initialize circular progress bar
|
||||||
circularProgressView = CircularProgressView(
|
circularProgressView = CircularProgressView(
|
||||||
context,
|
context,
|
||||||
config.overlayBackgroundOpacity,
|
config.overlayBackgroundOpacity,
|
||||||
config.overlayShowOverlayMinimalStyle,
|
config.overlayStyle.isMinimal,
|
||||||
config.overlayProgressColor,
|
config.overlayProgressColor,
|
||||||
config.overlayFillBackgroundPaint,
|
config.overlayFillBackgroundPaint,
|
||||||
config.overlayTextColor
|
config.overlayTextColor,
|
||||||
|
config.overlayTextSize
|
||||||
).apply {
|
).apply {
|
||||||
layoutParams = LayoutParams(300, 300).apply {
|
layoutParams = LayoutParams(100f.toDisplayPixels().toInt(), 100f.toDisplayPixels().toInt()).apply {
|
||||||
addRule(CENTER_IN_PARENT, TRUE)
|
addRule(CENTER_IN_PARENT, TRUE)
|
||||||
}
|
}
|
||||||
visibility = GONE // Initially hidden
|
visibility = GONE // Initially hidden
|
||||||
@ -71,22 +86,65 @@ class SwipeControlsOverlayLayout(
|
|||||||
|
|
||||||
// Initialize horizontal progress bar
|
// Initialize horizontal progress bar
|
||||||
val screenWidth = resources.displayMetrics.widthPixels
|
val screenWidth = resources.displayMetrics.widthPixels
|
||||||
val layoutWidth = (screenWidth * 2 / 3).toInt() // 2/3 of screen width
|
val layoutWidth = (screenWidth * 4 / 5).toInt() // Cap at ~360dp
|
||||||
horizontalProgressView = HorizontalProgressView(
|
horizontalProgressView = HorizontalProgressView(
|
||||||
context,
|
context,
|
||||||
config.overlayBackgroundOpacity,
|
config.overlayBackgroundOpacity,
|
||||||
config.overlayShowOverlayMinimalStyle,
|
config.overlayStyle.isMinimal,
|
||||||
config.overlayProgressColor,
|
config.overlayProgressColor,
|
||||||
config.overlayFillBackgroundPaint,
|
config.overlayFillBackgroundPaint,
|
||||||
config.overlayTextColor
|
config.overlayTextColor,
|
||||||
|
config.overlayTextSize
|
||||||
).apply {
|
).apply {
|
||||||
layoutParams = LayoutParams(layoutWidth, 100).apply {
|
layoutParams = LayoutParams(layoutWidth, 32f.toDisplayPixels().toInt()).apply {
|
||||||
addRule(CENTER_HORIZONTAL)
|
addRule(CENTER_HORIZONTAL)
|
||||||
topMargin = 40 // Top margin
|
if (config.overlayStyle.isHorizontalMinimalCenter) {
|
||||||
|
addRule(CENTER_VERTICAL)
|
||||||
|
} else {
|
||||||
|
topMargin = 20f.toDisplayPixels().toInt()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
visibility = GONE // Initially hidden
|
visibility = GONE // Initially hidden
|
||||||
}
|
}
|
||||||
addView(horizontalProgressView)
|
addView(horizontalProgressView)
|
||||||
|
|
||||||
|
// Initialize vertical progress bar for brightness (right side)
|
||||||
|
verticalBrightnessProgressView = VerticalProgressView(
|
||||||
|
context,
|
||||||
|
config.overlayBackgroundOpacity,
|
||||||
|
config.overlayStyle.isMinimal,
|
||||||
|
config.overlayProgressColor,
|
||||||
|
config.overlayFillBackgroundPaint,
|
||||||
|
config.overlayTextColor,
|
||||||
|
config.overlayTextSize
|
||||||
|
).apply {
|
||||||
|
layoutParams = LayoutParams(40f.toDisplayPixels().toInt(), 150f.toDisplayPixels().toInt()).apply {
|
||||||
|
addRule(ALIGN_PARENT_RIGHT)
|
||||||
|
rightMargin = 40f.toDisplayPixels().toInt()
|
||||||
|
addRule(CENTER_VERTICAL)
|
||||||
|
}
|
||||||
|
visibility = GONE // Initially hidden
|
||||||
|
}
|
||||||
|
addView(verticalBrightnessProgressView)
|
||||||
|
|
||||||
|
// Initialize vertical progress bar for volume (left side)
|
||||||
|
verticalVolumeProgressView = VerticalProgressView(
|
||||||
|
context,
|
||||||
|
config.overlayBackgroundOpacity,
|
||||||
|
config.overlayStyle.isMinimal,
|
||||||
|
config.overlayProgressColor,
|
||||||
|
config.overlayFillBackgroundPaint,
|
||||||
|
config.overlayTextColor,
|
||||||
|
config.overlayTextSize
|
||||||
|
).apply {
|
||||||
|
layoutParams = LayoutParams(40f.toDisplayPixels().toInt(), 150f.toDisplayPixels().toInt()).apply {
|
||||||
|
addRule(ALIGN_PARENT_LEFT)
|
||||||
|
leftMargin = 40f.toDisplayPixels().toInt()
|
||||||
|
addRule(CENTER_VERTICAL)
|
||||||
|
}
|
||||||
|
visibility = GONE // Initially hidden
|
||||||
|
}
|
||||||
|
addView(verticalVolumeProgressView)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler and callback for hiding progress bars
|
// Handler and callback for hiding progress bars
|
||||||
@ -94,6 +152,8 @@ class SwipeControlsOverlayLayout(
|
|||||||
private val feedbackHideCallback = Runnable {
|
private val feedbackHideCallback = Runnable {
|
||||||
circularProgressView.visibility = GONE
|
circularProgressView.visibility = GONE
|
||||||
horizontalProgressView.visibility = GONE
|
horizontalProgressView.visibility = GONE
|
||||||
|
verticalBrightnessProgressView.visibility = GONE
|
||||||
|
verticalVolumeProgressView.visibility = GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +163,11 @@ class SwipeControlsOverlayLayout(
|
|||||||
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
|
feedbackHideHandler.removeCallbacks(feedbackHideCallback)
|
||||||
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
|
feedbackHideHandler.postDelayed(feedbackHideCallback, config.overlayShowTimeoutMillis)
|
||||||
|
|
||||||
val viewToShow = if (config.isCircularProgressBar) circularProgressView else horizontalProgressView
|
val viewToShow = when {
|
||||||
|
config.overlayStyle.isCircular -> circularProgressView
|
||||||
|
config.overlayStyle.isVertical -> if (isBrightness) verticalBrightnessProgressView else verticalVolumeProgressView
|
||||||
|
else -> horizontalProgressView
|
||||||
|
}
|
||||||
viewToShow.apply {
|
viewToShow.apply {
|
||||||
setProgress(progress, max, value, isBrightness)
|
setProgress(progress, max, value, isBrightness)
|
||||||
this.icon = icon
|
this.icon = icon
|
||||||
@ -126,7 +190,9 @@ class SwipeControlsOverlayLayout(
|
|||||||
// Handle brightness change
|
// Handle brightness change
|
||||||
override fun onBrightnessChanged(brightness: Double) {
|
override fun onBrightnessChanged(brightness: Double) {
|
||||||
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
|
if (config.shouldLowestValueEnableAutoBrightness && brightness <= 0) {
|
||||||
showFeedbackView("Auto", 0, 100, autoBrightnessIcon, isBrightness = true)
|
val displayText = if (config.overlayStyle.isVertical) "А"
|
||||||
|
else str("revanced_swipe_lowest_value_enable_auto_brightness_overlay_text")
|
||||||
|
showFeedbackView(displayText, 0, 100, autoBrightnessIcon, isBrightness = true)
|
||||||
} else {
|
} else {
|
||||||
val brightnessValue = round(brightness).toInt()
|
val brightnessValue = round(brightness).toInt()
|
||||||
val icon = when {
|
val icon = when {
|
||||||
@ -135,7 +201,8 @@ class SwipeControlsOverlayLayout(
|
|||||||
brightnessValue < 75 -> highBrightnessIcon
|
brightnessValue < 75 -> highBrightnessIcon
|
||||||
else -> fullBrightnessIcon
|
else -> fullBrightnessIcon
|
||||||
}
|
}
|
||||||
showFeedbackView("$brightnessValue%", brightnessValue, 100, icon, isBrightness = true)
|
val displayText = if (config.overlayStyle.isVertical) "$brightnessValue" else "$brightnessValue%"
|
||||||
|
showFeedbackView(displayText, brightnessValue, 100, icon, isBrightness = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,11 +223,12 @@ class SwipeControlsOverlayLayout(
|
|||||||
*/
|
*/
|
||||||
abstract class AbstractProgressView(
|
abstract class AbstractProgressView(
|
||||||
context: Context,
|
context: Context,
|
||||||
protected val overlayBackgroundOpacity: Int,
|
overlayBackgroundOpacity: Int,
|
||||||
protected val overlayShowOverlayMinimalStyle: Boolean,
|
protected val isMinimalStyle: Boolean,
|
||||||
protected val overlayProgressColor: Int,
|
overlayProgressColor: Int,
|
||||||
protected val overlayFillBackgroundPaint: Int,
|
overlayFillBackgroundPaint: Int,
|
||||||
protected val overlayTextColor: Int,
|
private val overlayTextColor: Int,
|
||||||
|
protected val overlayTextSize: Int,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : View(context, attrs, defStyleAttr) {
|
) : View(context, attrs, defStyleAttr) {
|
||||||
@ -174,26 +242,25 @@ abstract class AbstractProgressView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize paints
|
// Initialize paints
|
||||||
public val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
|
val backgroundPaint = createPaint(overlayBackgroundOpacity, style = Paint.Style.FILL)
|
||||||
public val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 20f)
|
val progressPaint = createPaint(overlayProgressColor, style = Paint.Style.STROKE, strokeCap = Paint.Cap.ROUND, strokeWidth = 6f.toDisplayPixels())
|
||||||
public val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
|
val fillBackgroundPaint = createPaint(overlayFillBackgroundPaint, style = Paint.Style.FILL)
|
||||||
public val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
color = overlayTextColor
|
color = overlayTextColor
|
||||||
textAlign = Paint.Align.CENTER
|
textAlign = Paint.Align.CENTER
|
||||||
textSize = 40f // Can adjust based on need
|
textSize = overlayTextSize.toFloat().toDisplayPixels()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rect for text measurement
|
||||||
|
protected val textBounds = Rect()
|
||||||
|
|
||||||
protected var progress = 0
|
protected var progress = 0
|
||||||
protected var maxProgress = 100
|
protected var maxProgress = 100
|
||||||
protected var displayText: String = "0"
|
protected var displayText: String = "0"
|
||||||
protected var isBrightness = true
|
protected var isBrightness = true
|
||||||
public var icon: Drawable? = null
|
var icon: Drawable? = null
|
||||||
|
|
||||||
init {
|
open fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||||
// Stroke widths are now set in createPaint for progressPaint and fillBackgroundPaint
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
|
||||||
progress = value
|
progress = value
|
||||||
maxProgress = max
|
maxProgress = max
|
||||||
displayText = text
|
displayText = text
|
||||||
@ -201,6 +268,11 @@ abstract class AbstractProgressView(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun measureTextWidth(text: String, paint: Paint): Int {
|
||||||
|
paint.getTextBounds(text, 0, text.length, textBounds)
|
||||||
|
return textBounds.width()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas) {
|
override fun onDraw(canvas: Canvas) {
|
||||||
// Base class implementation can be empty
|
// Base class implementation can be empty
|
||||||
}
|
}
|
||||||
@ -209,34 +281,36 @@ abstract class AbstractProgressView(
|
|||||||
/**
|
/**
|
||||||
* Custom view for rendering a circular progress indicator with icons and text.
|
* Custom view for rendering a circular progress indicator with icons and text.
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("ViewConstructor")
|
||||||
class CircularProgressView(
|
class CircularProgressView(
|
||||||
context: Context,
|
context: Context,
|
||||||
overlayBackgroundOpacity: Int,
|
overlayBackgroundOpacity: Int,
|
||||||
overlayShowOverlayMinimalStyle: Boolean,
|
isMinimalStyle: Boolean,
|
||||||
overlayProgressColor: Int,
|
overlayProgressColor: Int,
|
||||||
overlayFillBackgroundPaint: Int,
|
overlayFillBackgroundPaint: Int,
|
||||||
overlayTextColor: Int,
|
overlayTextColor: Int,
|
||||||
|
overlayTextSize: Int,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : AbstractProgressView(
|
) : AbstractProgressView(
|
||||||
context,
|
context,
|
||||||
overlayBackgroundOpacity,
|
overlayBackgroundOpacity,
|
||||||
overlayShowOverlayMinimalStyle,
|
isMinimalStyle,
|
||||||
overlayProgressColor,
|
overlayProgressColor,
|
||||||
overlayFillBackgroundPaint,
|
overlayFillBackgroundPaint,
|
||||||
overlayTextColor,
|
overlayTextColor,
|
||||||
|
overlayTextSize,
|
||||||
attrs,
|
attrs,
|
||||||
defStyleAttr
|
defStyleAttr
|
||||||
) {
|
) {
|
||||||
private val rectF = RectF()
|
private val rectF = RectF()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
textPaint.textSize = 40f // Override default text size for circular view
|
progressPaint.strokeWidth = 6f.toDisplayPixels()
|
||||||
progressPaint.strokeWidth = 20f
|
fillBackgroundPaint.strokeWidth = 6f.toDisplayPixels()
|
||||||
fillBackgroundPaint.strokeWidth = 20f
|
progressPaint.strokeCap = Paint.Cap.ROUND
|
||||||
progressPaint.strokeCap = Paint.Cap.ROUND
|
|
||||||
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
|
fillBackgroundPaint.strokeCap = Paint.Cap.BUTT
|
||||||
progressPaint.style = Paint.Style.STROKE
|
progressPaint.style = Paint.Style.STROKE
|
||||||
fillBackgroundPaint.style = Paint.Style.STROKE
|
fillBackgroundPaint.style = Paint.Style.STROKE
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +318,8 @@ class CircularProgressView(
|
|||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
|
|
||||||
val size = min(width, height).toFloat()
|
val size = min(width, height).toFloat()
|
||||||
rectF.set(20f, 20f, size - 20f, size - 20f)
|
val inset = 6f.toDisplayPixels()
|
||||||
|
rectF.set(inset, inset, size - inset, size - inset)
|
||||||
|
|
||||||
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
|
canvas.drawOval(rectF, fillBackgroundPaint) // Draw the outer ring.
|
||||||
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
|
canvas.drawCircle(width / 2f, height / 2f, size / 3, backgroundPaint) // Draw the inner circle.
|
||||||
@ -255,124 +330,307 @@ class CircularProgressView(
|
|||||||
|
|
||||||
// Draw the icon in the center.
|
// Draw the icon in the center.
|
||||||
icon?.let {
|
icon?.let {
|
||||||
val iconSize = if (overlayShowOverlayMinimalStyle) 100 else 80
|
val iconSize = (if (isMinimalStyle) 36f else 24f).toDisplayPixels().toInt()
|
||||||
val iconX = (width - iconSize) / 2
|
val iconX = (width - iconSize) / 2
|
||||||
val iconY = (height / 2) - if (overlayShowOverlayMinimalStyle) 50 else 80
|
val iconY = if (isMinimalStyle) {
|
||||||
|
(height - iconSize) / 2
|
||||||
|
} else {
|
||||||
|
(height / 2) - 24f.toDisplayPixels().toInt()
|
||||||
|
}
|
||||||
it.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
|
it.setBounds(iconX, iconY, iconX + iconSize, iconY + iconSize)
|
||||||
it.draw(canvas)
|
it.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not a minimal style mode, draw the text inside the ring.
|
// If not a minimal style mode, draw the text inside the ring.
|
||||||
if (!overlayShowOverlayMinimalStyle) {
|
if (!isMinimalStyle) {
|
||||||
canvas.drawText(displayText, width / 2f, height / 2f + 60f, textPaint)
|
canvas.drawText(displayText, width / 2f, height / 2f + 20f.toDisplayPixels(), textPaint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||||
|
super.setProgress(value, max, text, isBrightnessMode)
|
||||||
|
requestLayout()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom view for rendering a rectangular progress bar with icons and text.
|
* Custom view for rendering a rectangular progress bar with icons and text.
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("ViewConstructor")
|
||||||
class HorizontalProgressView(
|
class HorizontalProgressView(
|
||||||
context: Context,
|
context: Context,
|
||||||
overlayBackgroundOpacity: Int,
|
overlayBackgroundOpacity: Int,
|
||||||
overlayShowOverlayMinimalStyle: Boolean,
|
isMinimalStyle: Boolean,
|
||||||
overlayProgressColor: Int,
|
overlayProgressColor: Int,
|
||||||
overlayFillBackgroundPaint: Int,
|
overlayFillBackgroundPaint: Int,
|
||||||
overlayTextColor: Int,
|
overlayTextColor: Int,
|
||||||
|
overlayTextSize: Int,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : AbstractProgressView(
|
) : AbstractProgressView(
|
||||||
context,
|
context,
|
||||||
overlayBackgroundOpacity,
|
overlayBackgroundOpacity,
|
||||||
overlayShowOverlayMinimalStyle,
|
isMinimalStyle,
|
||||||
overlayProgressColor,
|
overlayProgressColor,
|
||||||
overlayFillBackgroundPaint,
|
overlayFillBackgroundPaint,
|
||||||
overlayTextColor,
|
overlayTextColor,
|
||||||
|
overlayTextSize,
|
||||||
attrs,
|
attrs,
|
||||||
defStyleAttr
|
defStyleAttr
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val iconSize = 60f
|
private val iconSize = 20f.toDisplayPixels()
|
||||||
private val padding = 40f
|
private val padding = 12f.toDisplayPixels()
|
||||||
|
private var textWidth = 0f
|
||||||
|
private val progressBarHeight = 3f.toDisplayPixels()
|
||||||
|
private val progressBarWidth: Float = resources.displayMetrics.widthPixels / 4f
|
||||||
|
|
||||||
init {
|
init {
|
||||||
textPaint.textSize = 36f // Override default text size for horizontal view
|
|
||||||
progressPaint.strokeWidth = 0f
|
progressPaint.strokeWidth = 0f
|
||||||
progressPaint.strokeCap = Paint.Cap.BUTT
|
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||||
progressPaint.style = Paint.Style.FILL
|
progressPaint.style = Paint.Style.FILL
|
||||||
fillBackgroundPaint.style = Paint.Style.FILL
|
fillBackgroundPaint.style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate required width based on content
|
||||||
|
* @return Required width to display all elements
|
||||||
|
*/
|
||||||
|
private fun calculateRequiredWidth(): Float {
|
||||||
|
textWidth = measureTextWidth(displayText, textPaint).toFloat()
|
||||||
|
|
||||||
|
return if (!isMinimalStyle) {
|
||||||
|
padding + iconSize + padding + progressBarWidth + padding + textWidth + padding
|
||||||
|
} else {
|
||||||
|
padding + iconSize + padding + textWidth + padding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||||
|
|
||||||
|
val suggestedWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
|
val suggestedHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||||
|
|
||||||
|
val height = suggestedHeight
|
||||||
|
val requiredWidth = calculateRequiredWidth().toInt()
|
||||||
|
val width = min(max(100, requiredWidth), suggestedWidth)
|
||||||
|
|
||||||
|
setMeasuredDimension(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas) {
|
override fun onDraw(canvas: Canvas) {
|
||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
|
|
||||||
val width = width.toFloat()
|
val viewWidth = width.toFloat()
|
||||||
val height = height.toFloat()
|
val viewHeight = height.toFloat()
|
||||||
|
val viewHeightHalf = viewHeight / 2
|
||||||
|
|
||||||
// Radius for rounded corners
|
textWidth = measureTextWidth(displayText, textPaint).toFloat()
|
||||||
val cornerRadius = min(width, height) / 2
|
|
||||||
|
|
||||||
// Calculate the total width for the elements
|
val cornerRadius = viewHeightHalf
|
||||||
val minimalElementWidth = 5 * padding + iconSize
|
|
||||||
|
|
||||||
// Calculate the starting point (X) to center the elements
|
val startX = padding
|
||||||
val minimalStartX = (width - minimalElementWidth) / 2
|
val iconEndX = startX + iconSize
|
||||||
|
|
||||||
// Draw the background
|
val textStartX = (viewWidth - 1.5f * padding - textWidth)
|
||||||
if (!overlayShowOverlayMinimalStyle) {
|
|
||||||
canvas.drawRoundRect(0f, 0f, width, height, cornerRadius, cornerRadius, backgroundPaint)
|
|
||||||
} else {
|
|
||||||
canvas.drawRoundRect(minimalStartX, 0f, minimalStartX + minimalElementWidth, height, cornerRadius, cornerRadius, backgroundPaint)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!overlayShowOverlayMinimalStyle) {
|
canvas.drawRoundRect(
|
||||||
// Draw the fill background
|
0f, 0f, viewWidth, viewHeight,
|
||||||
val startX = 2 * padding + iconSize
|
cornerRadius, cornerRadius, backgroundPaint
|
||||||
val endX = width - 4 * padding
|
)
|
||||||
val fillWidth = endX - startX
|
|
||||||
|
|
||||||
canvas.drawRoundRect(
|
|
||||||
startX,
|
|
||||||
height / 2 - 5f,
|
|
||||||
endX,
|
|
||||||
height / 2 + 5f,
|
|
||||||
10f, 10f,
|
|
||||||
fillBackgroundPaint
|
|
||||||
)
|
|
||||||
|
|
||||||
// Draw the progress
|
|
||||||
val progressWidth = (progress.toFloat() / maxProgress) * fillWidth
|
|
||||||
canvas.drawRoundRect(
|
|
||||||
startX,
|
|
||||||
height / 2 - 5f,
|
|
||||||
startX + progressWidth,
|
|
||||||
height / 2 + 5f,
|
|
||||||
10f, 10f,
|
|
||||||
progressPaint
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the icon
|
|
||||||
icon?.let {
|
icon?.let {
|
||||||
val iconX = if (!overlayShowOverlayMinimalStyle) {
|
val iconY = viewHeightHalf - iconSize / 2
|
||||||
padding
|
it.setBounds(
|
||||||
} else {
|
startX.toInt(),
|
||||||
padding + minimalStartX
|
iconY.toInt(),
|
||||||
}
|
(startX + iconSize).toInt(),
|
||||||
val iconY = height / 2 - iconSize / 2
|
(iconY + iconSize).toInt()
|
||||||
it.setBounds(iconX.toInt(), iconY.toInt(), (iconX + iconSize).toInt(), (iconY + iconSize).toInt())
|
)
|
||||||
it.draw(canvas)
|
it.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the text on the right
|
val textY = viewHeightHalf + textPaint.textSize / 3
|
||||||
val textX = if (!overlayShowOverlayMinimalStyle) {
|
textPaint.textAlign = Paint.Align.LEFT
|
||||||
width - 2 * padding
|
|
||||||
} else {
|
|
||||||
minimalStartX + minimalElementWidth - 2 * padding
|
|
||||||
}
|
|
||||||
val textY = height / 2 + textPaint.textSize / 3
|
|
||||||
|
|
||||||
// Draw the text
|
if (isMinimalStyle) {
|
||||||
canvas.drawText(displayText, textX, textY, textPaint)
|
canvas.drawText(displayText, textStartX, textY, textPaint)
|
||||||
|
} else {
|
||||||
|
val progressStartX = iconEndX + padding
|
||||||
|
val progressEndX = textStartX - padding
|
||||||
|
val progressWidth = progressEndX - progressStartX
|
||||||
|
|
||||||
|
if (progressWidth > 50) {
|
||||||
|
val progressBarHeightHalf = progressBarHeight / 2.0f
|
||||||
|
val viewHeightHalfMinusProgressBarHeightHalf = viewHeightHalf - progressBarHeightHalf
|
||||||
|
val viewHeightHalfPlusProgressBarHeightHalf = viewHeightHalf + progressBarHeightHalf
|
||||||
|
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
progressStartX,
|
||||||
|
viewHeightHalfMinusProgressBarHeightHalf,
|
||||||
|
progressEndX,
|
||||||
|
viewHeightHalfPlusProgressBarHeightHalf,
|
||||||
|
progressBarHeightHalf,
|
||||||
|
progressBarHeightHalf,
|
||||||
|
fillBackgroundPaint
|
||||||
|
)
|
||||||
|
|
||||||
|
val progressValue = (progress.toFloat() / maxProgress) * progressWidth
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
progressStartX,
|
||||||
|
viewHeightHalfMinusProgressBarHeightHalf,
|
||||||
|
progressStartX + progressValue,
|
||||||
|
viewHeightHalfPlusProgressBarHeightHalf,
|
||||||
|
progressBarHeightHalf,
|
||||||
|
progressBarHeightHalf,
|
||||||
|
progressPaint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawText(displayText, textStartX, textY, textPaint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||||
|
super.setProgress(value, max, text, isBrightnessMode)
|
||||||
|
requestLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom view for rendering a vertical progress bar with icons and text.
|
||||||
|
*/
|
||||||
|
@SuppressLint("ViewConstructor")
|
||||||
|
class VerticalProgressView(
|
||||||
|
context: Context,
|
||||||
|
overlayBackgroundOpacity: Int,
|
||||||
|
isMinimalStyle: Boolean,
|
||||||
|
overlayProgressColor: Int,
|
||||||
|
overlayFillBackgroundPaint: Int,
|
||||||
|
overlayTextColor: Int,
|
||||||
|
overlayTextSize: Int,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : AbstractProgressView(
|
||||||
|
context,
|
||||||
|
overlayBackgroundOpacity,
|
||||||
|
isMinimalStyle,
|
||||||
|
overlayProgressColor,
|
||||||
|
overlayFillBackgroundPaint,
|
||||||
|
overlayTextColor,
|
||||||
|
overlayTextSize,
|
||||||
|
attrs,
|
||||||
|
defStyleAttr
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val iconSize = 20f.toDisplayPixels()
|
||||||
|
private val padding = 12f.toDisplayPixels()
|
||||||
|
private val progressBarWidth = 3f.toDisplayPixels()
|
||||||
|
private val progressBarHeight: Float = resources.displayMetrics.widthPixels / 3f
|
||||||
|
|
||||||
|
init {
|
||||||
|
progressPaint.strokeWidth = 0f
|
||||||
|
progressPaint.strokeCap = Paint.Cap.BUTT
|
||||||
|
progressPaint.style = Paint.Style.FILL
|
||||||
|
fillBackgroundPaint.style = Paint.Style.FILL
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate required height based on content
|
||||||
|
* @return Required height to display all elements
|
||||||
|
*/
|
||||||
|
private fun calculateRequiredHeight(): Float {
|
||||||
|
return if (!isMinimalStyle) {
|
||||||
|
padding + iconSize + padding + progressBarHeight + padding + textPaint.textSize + padding
|
||||||
|
} else {
|
||||||
|
padding + iconSize + padding + textPaint.textSize + padding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||||
|
|
||||||
|
val suggestedWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
|
val suggestedHeight = MeasureSpec.getSize(heightMeasureSpec)
|
||||||
|
|
||||||
|
val requiredHeight = calculateRequiredHeight().toInt()
|
||||||
|
val height = min(max(100, requiredHeight), suggestedHeight)
|
||||||
|
|
||||||
|
setMeasuredDimension(suggestedWidth, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
|
||||||
|
val viewWidth = width.toFloat()
|
||||||
|
val viewHeight = height.toFloat()
|
||||||
|
val viewWidthHalf = viewWidth / 2
|
||||||
|
val cornerRadius = viewWidthHalf
|
||||||
|
|
||||||
|
val startY = padding
|
||||||
|
val iconEndY = startY + iconSize
|
||||||
|
|
||||||
|
val textStartY = viewHeight - padding - textPaint.textSize / 2
|
||||||
|
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
0f, 0f, viewWidth, viewHeight,
|
||||||
|
cornerRadius, cornerRadius, backgroundPaint
|
||||||
|
)
|
||||||
|
|
||||||
|
icon?.let {
|
||||||
|
val iconX = viewWidthHalf - iconSize / 2
|
||||||
|
it.setBounds(
|
||||||
|
iconX.toInt(),
|
||||||
|
startY.toInt(),
|
||||||
|
(iconX + iconSize).toInt(),
|
||||||
|
(startY + iconSize).toInt()
|
||||||
|
)
|
||||||
|
it.draw(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
val textX = viewWidthHalf
|
||||||
|
textPaint.textAlign = Paint.Align.CENTER
|
||||||
|
|
||||||
|
if (isMinimalStyle) {
|
||||||
|
canvas.drawText(displayText, textX, textStartY, textPaint)
|
||||||
|
} else {
|
||||||
|
val progressStartY = (iconEndY + padding).toFloat()
|
||||||
|
val progressEndY = textStartY - textPaint.textSize - padding
|
||||||
|
val progressHeight = progressEndY - progressStartY
|
||||||
|
|
||||||
|
if (progressHeight > 50) {
|
||||||
|
val progressBarWidthHalf = progressBarWidth / 2
|
||||||
|
val viewHeightHalfMinusProgressBarHeightHalf = viewWidthHalf - progressBarWidthHalf
|
||||||
|
val viewHeightHalfPlusProgressBarHeightHalf = viewWidthHalf + progressBarWidthHalf
|
||||||
|
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
viewHeightHalfMinusProgressBarHeightHalf,
|
||||||
|
progressStartY,
|
||||||
|
viewHeightHalfPlusProgressBarHeightHalf,
|
||||||
|
progressEndY,
|
||||||
|
progressBarWidthHalf,
|
||||||
|
progressBarWidthHalf,
|
||||||
|
fillBackgroundPaint
|
||||||
|
)
|
||||||
|
|
||||||
|
val progressValue = (progress.toFloat() / maxProgress) * progressHeight
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
viewHeightHalfMinusProgressBarHeightHalf,
|
||||||
|
progressEndY - progressValue,
|
||||||
|
viewHeightHalfPlusProgressBarHeightHalf,
|
||||||
|
progressEndY,
|
||||||
|
progressBarWidthHalf,
|
||||||
|
progressBarWidthHalf,
|
||||||
|
progressPaint
|
||||||
|
)
|
||||||
|
}
|
||||||
|
canvas.drawText(displayText, textX, textStartY, textPaint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setProgress(value: Int, max: Int, text: String, isBrightnessMode: Boolean) {
|
||||||
|
super.setProgress(value, max, text, isBrightnessMode)
|
||||||
|
requestLayout()
|
||||||
|
}
|
||||||
|
}
|
@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.19.0-dev.8
|
version = 5.21.0
|
||||||
|
@ -2,6 +2,10 @@ public final class app/revanced/patches/all/misc/activity/exportall/ExportAllAct
|
|||||||
public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getExportAllActivitiesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/all/misc/adb/HideAdbPatchKt {
|
||||||
|
public static final fun getHideAdbStatusPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt {
|
public final class app/revanced/patches/all/misc/build/BaseSpoofBuildInfoPatchKt {
|
||||||
public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun baseSpoofBuildInfoPatch (Lkotlin/jvm/functions/Function0;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@ -108,6 +112,10 @@ public final class app/revanced/patches/all/misc/shortcut/sharetargets/RemoveSha
|
|||||||
public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getRemoveShareTargetsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/all/misc/targetSdk/SetTargetSdkVersion34Kt {
|
||||||
|
public static final fun getSetTargetSdkVersion34 ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall {
|
public abstract interface class app/revanced/patches/all/misc/transformation/IMethodCall {
|
||||||
public abstract fun getDefinedClassName ()Ljava/lang/String;
|
public abstract fun getDefinedClassName ()Ljava/lang/String;
|
||||||
public abstract fun getMethodName ()Ljava/lang/String;
|
public abstract fun getMethodName ()Ljava/lang/String;
|
||||||
@ -272,6 +280,10 @@ public final class app/revanced/patches/messenger/inputfield/DisableTypingIndica
|
|||||||
public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getDisableTypingIndicatorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/messenger/navbar/RemoveMetaAITabPatchKt {
|
||||||
|
public static final fun getRemoveMetaAITabPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt {
|
public final class app/revanced/patches/mifitness/misc/locale/ForceEnglishLocalePatchKt {
|
||||||
public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getForceEnglishLocalePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@ -564,7 +576,9 @@ public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt {
|
public final class app/revanced/patches/shared/misc/extension/SharedExtensionPatchKt {
|
||||||
|
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||||
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
public static final fun extensionHook (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||||
|
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lapp/revanced/patcher/Fingerprint;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||||
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
public static synthetic fun extensionHook$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patches/shared/misc/extension/ExtensionHook;
|
||||||
public static final fun sharedExtensionPatch (Ljava/lang/String;[Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun sharedExtensionPatch (Ljava/lang/String;[Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun sharedExtensionPatch ([Lapp/revanced/patches/shared/misc/extension/ExtensionHook;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
@ -832,6 +846,10 @@ public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt
|
|||||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/spotify/misc/fix/SpoofPackageInfoPatchKt {
|
||||||
|
public static final fun getSpoofPackageInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
|
public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt {
|
||||||
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@ -1402,6 +1420,8 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
|
|||||||
public static final fun is_20_07_or_greater ()Z
|
public static final fun is_20_07_or_greater ()Z
|
||||||
public static final fun is_20_09_or_greater ()Z
|
public static final fun is_20_09_or_greater ()Z
|
||||||
public static final fun is_20_10_or_greater ()Z
|
public static final fun is_20_10_or_greater ()Z
|
||||||
|
public static final fun is_20_14_or_greater ()Z
|
||||||
|
public static final fun is_20_15_or_greater ()Z
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
|
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
|
||||||
@ -1515,7 +1535,11 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/util/BytecodeUtilsKt {
|
public final class app/revanced/util/BytecodeUtilsKt {
|
||||||
|
public static final fun addInstructionsAtControlFlowLabel (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ILjava/lang/String;)V
|
||||||
|
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)Z
|
||||||
|
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)Z
|
||||||
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
|
public static final fun containsLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z
|
||||||
|
public static final fun findFreeRegister (Lcom/android/tools/smali/dexlib2/iface/Method;I[I)I
|
||||||
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||||
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
|
public static final fun findInstructionIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)Ljava/util/List;
|
||||||
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
public static final fun findInstructionIndicesReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List;
|
||||||
@ -1542,9 +1566,17 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
|
public static final fun indexOfFirstInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;)I
|
||||||
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
|
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lcom/android/tools/smali/dexlib2/Opcode;ILjava/lang/Object;)I
|
||||||
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
public static synthetic fun indexOfFirstInstructionReversedOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/Integer;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
||||||
|
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||||
|
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||||
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
public static final fun indexOfFirstLiteralInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
|
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||||
|
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||||
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
public static final fun indexOfFirstLiteralInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
|
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||||
|
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||||
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
public static final fun indexOfFirstLiteralInstructionReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
|
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;D)I
|
||||||
|
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;F)I
|
||||||
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
public static final fun indexOfFirstLiteralInstructionReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||||
public static final fun indexOfFirstResourceId (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfFirstResourceId (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package app.revanced.patches.all.misc.adb
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/all/misc/hide/adb/HideAdbPatch;"
|
||||||
|
|
||||||
|
private val SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE = ImmutableMethodReference(
|
||||||
|
"Landroid/provider/Settings\$Global;",
|
||||||
|
"getInt",
|
||||||
|
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;"),
|
||||||
|
"I"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE = ImmutableMethodReference(
|
||||||
|
"Landroid/provider/Settings\$Global;",
|
||||||
|
"getInt",
|
||||||
|
listOf("Landroid/content/ContentResolver;", "Ljava/lang/String;", "I"),
|
||||||
|
"I"
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun MethodReference.anyMethodSignatureMatches(vararg anyOf: MethodReference): Boolean {
|
||||||
|
return anyOf.any {
|
||||||
|
MethodUtil.methodSignaturesMatch(it, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val hideAdbStatusPatch = bytecodePatch(
|
||||||
|
name = "Hide ADB status",
|
||||||
|
description = "Hides enabled development settings and/or ADB.",
|
||||||
|
use = false,
|
||||||
|
) {
|
||||||
|
extendWith("extensions/all/misc/adb/hide-adb.rve")
|
||||||
|
|
||||||
|
dependsOn(
|
||||||
|
transformInstructionsPatch(
|
||||||
|
filterMap = filterMap@{ classDef, method, instruction, instructionIndex ->
|
||||||
|
val reference = instruction
|
||||||
|
.takeIf { it.opcode == Opcode.INVOKE_STATIC }
|
||||||
|
?.getReference<MethodReference>()
|
||||||
|
?.takeIf {
|
||||||
|
it.anyMethodSignatureMatches(it,
|
||||||
|
SETTINGS_GLOBAL_GET_INT_OR_THROW_METHOD_REFERENCE,
|
||||||
|
SETTINGS_GLOBAL_GET_INT_OR_DEFAULT_METHOD_REFERENCE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
?: return@filterMap null
|
||||||
|
|
||||||
|
Triple(instruction as Instruction35c, instructionIndex, reference.parameterTypes)
|
||||||
|
},
|
||||||
|
transform = { method, entry ->
|
||||||
|
val (instruction, index, parameterTypes) = entry
|
||||||
|
val parameterString = parameterTypes.joinToString(separator = "")
|
||||||
|
|
||||||
|
val registerString = when (parameterTypes.size) {
|
||||||
|
2 -> "v${instruction.registerC}, v${instruction.registerD}"
|
||||||
|
else -> "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
method.replaceInstruction(
|
||||||
|
index,
|
||||||
|
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->getInt($parameterString)I"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -6,7 +6,7 @@ import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
|
|||||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||||
"Lapp/revanced/extension/all/connectivity/wifi/spoof/SpoofWifiPatch"
|
"Lapp/revanced/extension/all/misc/connectivity/wifi/spoof/SpoofWifiPatch"
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ private val removeCaptureRestrictionResourcePatch = resourcePatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||||
"Lapp/revanced/extension/all/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch"
|
"Lapp/revanced/extension/all/misc/screencapture/removerestriction/RemoveScreenCaptureRestrictionPatch"
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
@ -10,7 +10,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
|
|||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
private const val EXTENSION_CLASS_DESCRIPTOR_PREFIX =
|
||||||
"Lapp/revanced/extension/all/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
|
"Lapp/revanced/extension/all/misc/screenshot/removerestriction/RemoveScreenshotRestrictionPatch"
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "$EXTENSION_CLASS_DESCRIPTOR_PREFIX;"
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package app.revanced.patches.all.misc.targetSdk
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
|
import app.revanced.util.getNode
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val setTargetSdkVersion34 = resourcePatch(
|
||||||
|
name = "Set target SDK version 34",
|
||||||
|
description = "Changes the target SDK to version 34 (Android 14). " +
|
||||||
|
"For devices running Android 15+, this will disable edge-to-edge display.",
|
||||||
|
use = false,
|
||||||
|
) {
|
||||||
|
execute {
|
||||||
|
val targetSdkOverride = 34 // Android 14.
|
||||||
|
|
||||||
|
document("AndroidManifest.xml").use { document ->
|
||||||
|
fun getLogger() = Logger.getLogger(this::class.java.name)
|
||||||
|
|
||||||
|
// Ideally, the override should only be applied if the existing target is higher.
|
||||||
|
// But since ApkTool does not add targetSdkVersion to the decompiled AndroidManifest,
|
||||||
|
// there is no way to check targetSdkVersion. Instead, check compileSdkVersion and print a warning.
|
||||||
|
try {
|
||||||
|
val manifestElement = document.getNode("manifest") as Element
|
||||||
|
val compileSdkVersion = Integer.parseInt(
|
||||||
|
manifestElement.getAttribute("android:compileSdkVersion")
|
||||||
|
)
|
||||||
|
if (compileSdkVersion <= targetSdkOverride) {
|
||||||
|
getLogger().warning(
|
||||||
|
"This app does not appear to use a target SDK above $targetSdkOverride: " +
|
||||||
|
"(compileSdkVersion: $compileSdkVersion)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
getLogger().warning("Could not check compileSdkVersion")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change targetSdkVersion to override value.
|
||||||
|
document.getElementsByTagName("manifest").item(0).let {
|
||||||
|
var element = it.ownerDocument.createElement("uses-sdk")
|
||||||
|
element.setAttribute("android:targetSdkVersion", targetSdkOverride.toString())
|
||||||
|
|
||||||
|
it.appendChild(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,8 @@ internal val initializeMonetizationDebugSettingsFingerprint = fingerprint {
|
|||||||
"Z", // useDebugBilling
|
"Z", // useDebugBilling
|
||||||
"Z", // showManageSubscriptions
|
"Z", // showManageSubscriptions
|
||||||
"Z", // alwaysShowSuperAds
|
"Z", // alwaysShowSuperAds
|
||||||
"Lcom/duolingo/debug/FamilyQuestOverride;",
|
// matches "Lcom/duolingo/debug/FamilyQuestOverride;" or "Lcom/duolingo/data/debug/monetization/FamilyQuestOverride;"
|
||||||
|
"Lcom/duolingo/",
|
||||||
)
|
)
|
||||||
opcodes(Opcode.IPUT_BOOLEAN)
|
opcodes(Opcode.IPUT_BOOLEAN)
|
||||||
}
|
}
|
||||||
|
@ -27,4 +27,5 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
|
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
|
||||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
|
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
|
||||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||||
|
addStringResources = false,
|
||||||
)
|
)
|
||||||
|
@ -22,6 +22,7 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
|
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
|
||||||
fromPackageName = PHOTOS_PACKAGE_NAME,
|
fromPackageName = PHOTOS_PACKAGE_NAME,
|
||||||
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
|
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
|
||||||
|
addStringResources = false,
|
||||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
||||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||||
)
|
)
|
||||||
|
@ -3,23 +3,28 @@ package app.revanced.patches.googlephotos.misc.preferences
|
|||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
@Deprecated("This patch no longer works and this code will soon be deleted")
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch(
|
val restoreHiddenBackUpWhileChargingTogglePatch = bytecodePatch(
|
||||||
name = "Restore hidden 'Back up while charging' toggle",
|
description = "Restores a hidden toggle to only run backups when the device is charging."
|
||||||
description = "Restores a hidden toggle to only run backups when the device is charging.",
|
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.google.android.apps.photos")
|
compatibleWith("com.google.android.apps.photos"("7.11.0.705590205"))
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
|
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
|
||||||
val chargingPrefStringIndex = backupPreferencesFingerprint.stringMatches!!.first().index
|
backupPreferencesFingerprint.let {
|
||||||
backupPreferencesFingerprint.method.apply {
|
it.method.apply {
|
||||||
// Get the register of move-result.
|
val index = indexOfFirstInstructionOrThrow(
|
||||||
val resultRegister = getInstruction<OneRegisterInstruction>(chargingPrefStringIndex + 2).registerA
|
it.stringMatches!!.first().index,
|
||||||
// Insert const after move-result to override register as true.
|
Opcode.MOVE_RESULT
|
||||||
addInstruction(chargingPrefStringIndex + 3, "const/4 v$resultRegister, 0x1")
|
)
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
addInstruction(index + 1, "const/4 v$register, 0x1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package app.revanced.patches.messenger.navbar
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
internal val createTabConfigurationFingerprint = fingerprint {
|
||||||
|
strings("MessengerTabConfigurationCreator.createTabConfiguration")
|
||||||
|
opcodes(
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
Opcode.INVOKE_DIRECT,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IF_EQZ,
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package app.revanced.patches.messenger.navbar
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val removeMetaAITabPatch = bytecodePatch(
|
||||||
|
name = "Remove Meta AI tab",
|
||||||
|
description = "Removes the 'Meta AI' tab from the navbar.",
|
||||||
|
) {
|
||||||
|
compatibleWith("com.facebook.orca")
|
||||||
|
|
||||||
|
execute {
|
||||||
|
createTabConfigurationFingerprint.let {
|
||||||
|
val moveResultIndex = it.patternMatch!!.startIndex + 1
|
||||||
|
val enabledRegister = it.method.getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
|
||||||
|
it.method.replaceInstruction(
|
||||||
|
moveResultIndex,
|
||||||
|
"const/4 v$enabledRegister, 0x0"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -41,11 +41,12 @@ fun sharedExtensionPatch(
|
|||||||
|
|
||||||
execute {
|
execute {
|
||||||
if (classes.none { EXTENSION_CLASS_DESCRIPTOR == it.type }) {
|
if (classes.none { EXTENSION_CLASS_DESCRIPTOR == it.type }) {
|
||||||
throw PatchException(
|
throw PatchException("Shared extension is not available. This patch can not succeed without it.")
|
||||||
"Shared extension has not been merged yet. This patch can not succeed without merging it.",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize {
|
||||||
|
// The hooks are made in finalize to ensure that the context is hooked before any other patches.
|
||||||
hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) }
|
hooks.forEach { hook -> hook(EXTENSION_CLASS_DESCRIPTOR) }
|
||||||
|
|
||||||
// Modify Utils method to include the patches release version.
|
// Modify Utils method to include the patches release version.
|
||||||
@ -92,7 +93,7 @@ fun sharedExtensionPatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ExtensionHook internal constructor(
|
class ExtensionHook internal constructor(
|
||||||
private val fingerprint: Fingerprint,
|
internal val fingerprint: Fingerprint,
|
||||||
private val insertIndexResolver: ((Method) -> Int),
|
private val insertIndexResolver: ((Method) -> Int),
|
||||||
private val contextRegisterResolver: (Method) -> String,
|
private val contextRegisterResolver: (Method) -> String,
|
||||||
) {
|
) {
|
||||||
@ -109,8 +110,14 @@ class ExtensionHook internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun extensionHook(
|
||||||
|
insertIndexResolver: ((Method) -> Int) = { 0 },
|
||||||
|
contextRegisterResolver: (Method) -> String = { "p0" },
|
||||||
|
fingerprint: Fingerprint,
|
||||||
|
) = ExtensionHook(fingerprint, insertIndexResolver, contextRegisterResolver)
|
||||||
|
|
||||||
fun extensionHook(
|
fun extensionHook(
|
||||||
insertIndexResolver: ((Method) -> Int) = { 0 },
|
insertIndexResolver: ((Method) -> Int) = { 0 },
|
||||||
contextRegisterResolver: (Method) -> String = { "p0" },
|
contextRegisterResolver: (Method) -> String = { "p0" },
|
||||||
fingerprintBuilderBlock: FingerprintBuilder.() -> Unit,
|
fingerprintBuilderBlock: FingerprintBuilder.() -> Unit,
|
||||||
) = ExtensionHook(fingerprint(block = fingerprintBuilderBlock), insertIndexResolver, contextRegisterResolver)
|
) = extensionHook(insertIndexResolver, contextRegisterResolver, fingerprint(block = fingerprintBuilderBlock))
|
||||||
|
@ -17,7 +17,6 @@ import com.android.tools.smali.dexlib2.Opcode
|
|||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
@ -53,8 +52,8 @@ fun gmsCoreSupportPatch(
|
|||||||
block: BytecodePatchBuilder.() -> Unit = {},
|
block: BytecodePatchBuilder.() -> Unit = {},
|
||||||
) = bytecodePatch(
|
) = bytecodePatch(
|
||||||
name = "GmsCore support",
|
name = "GmsCore support",
|
||||||
description = "Allows patched Google apps to run without root and under a different package name " +
|
description = "Allows the app to work without root by using a different package name when patched " +
|
||||||
"by using GmsCore instead of Google Play Services.",
|
"using a GmsCore instead of Google Play Services.",
|
||||||
) {
|
) {
|
||||||
val gmsCoreVendorGroupIdOption = stringOption(
|
val gmsCoreVendorGroupIdOption = stringOption(
|
||||||
key = "gmsCoreVendorGroupId",
|
key = "gmsCoreVendorGroupId",
|
||||||
@ -110,19 +109,18 @@ fun gmsCoreSupportPatch(
|
|||||||
|
|
||||||
// region Collection of transformations that are applied to all strings.
|
// region Collection of transformations that are applied to all strings.
|
||||||
|
|
||||||
fun commonTransform(referencedString: String): String? =
|
fun commonTransform(referencedString: String): String? = when (referencedString) {
|
||||||
when (referencedString) {
|
"com.google",
|
||||||
"com.google",
|
"com.google.android.gms",
|
||||||
"com.google.android.gms",
|
in PERMISSIONS,
|
||||||
in PERMISSIONS,
|
in ACTIONS,
|
||||||
in ACTIONS,
|
in AUTHORITIES,
|
||||||
in AUTHORITIES,
|
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
|
||||||
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
|
|
||||||
|
|
||||||
// No vendor prefix for whatever reason...
|
// No vendor prefix for whatever reason...
|
||||||
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
|
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun contentUrisTransform(str: String): String? {
|
fun contentUrisTransform(str: String): String? {
|
||||||
// only when content:// uri
|
// only when content:// uri
|
||||||
@ -205,16 +203,8 @@ fun gmsCoreSupportPatch(
|
|||||||
|
|
||||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||||
mainActivityOnCreateFingerprint.method.apply {
|
mainActivityOnCreateFingerprint.method.apply {
|
||||||
// Temporary fix for patches with an extension patch that hook the onCreate method as well.
|
|
||||||
val setContextIndex = indexOfFirstInstruction {
|
|
||||||
val reference = getReference<MethodReference>() ?: return@indexOfFirstInstruction false
|
|
||||||
|
|
||||||
reference.toString() == "Lapp/revanced/extension/shared/Utils;->setContext(Landroid/content/Context;)V"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add after setContext call, because this patch needs the context.
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
if (setContextIndex < 0) 0 else setContextIndex + 1,
|
0,
|
||||||
"invoke-static/range { p0 .. p0 }, Lapp/revanced/extension/shared/GmsCoreSupport;->" +
|
"invoke-static/range { p0 .. p0 }, Lapp/revanced/extension/shared/GmsCoreSupport;->" +
|
||||||
"checkGmsCore(Landroid/app/Activity;)V",
|
"checkGmsCore(Landroid/app/Activity;)V",
|
||||||
)
|
)
|
||||||
@ -510,13 +500,44 @@ private object Constants {
|
|||||||
* @param executeBlock The additional execution block of the patch.
|
* @param executeBlock The additional execution block of the patch.
|
||||||
* @param block The additional block to build the patch.
|
* @param block The additional block to build the patch.
|
||||||
*/
|
*/
|
||||||
fun gmsCoreSupportResourcePatch(
|
fun gmsCoreSupportResourcePatch( // This is here only for binary compatibility.
|
||||||
fromPackageName: String,
|
fromPackageName: String,
|
||||||
toPackageName: String,
|
toPackageName: String,
|
||||||
spoofedPackageSignature: String,
|
spoofedPackageSignature: String,
|
||||||
gmsCoreVendorGroupIdOption: Option<String>,
|
gmsCoreVendorGroupIdOption: Option<String>,
|
||||||
executeBlock: ResourcePatchContext.() -> Unit = {},
|
executeBlock: ResourcePatchContext.() -> Unit = {},
|
||||||
block: ResourcePatchBuilder.() -> Unit = {},
|
block: ResourcePatchBuilder.() -> Unit = {},
|
||||||
|
) = gmsCoreSupportResourcePatch(
|
||||||
|
fromPackageName,
|
||||||
|
toPackageName,
|
||||||
|
spoofedPackageSignature,
|
||||||
|
gmsCoreVendorGroupIdOption,
|
||||||
|
true,
|
||||||
|
executeBlock,
|
||||||
|
block
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract resource patch that allows Google apps to run without root and under a different package name
|
||||||
|
* by using GmsCore instead of Google Play Services.
|
||||||
|
*
|
||||||
|
* @param fromPackageName The package name of the original app.
|
||||||
|
* @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
|
||||||
|
* @param spoofedPackageSignature The signature of the package to spoof to.
|
||||||
|
* @param gmsCoreVendorGroupIdOption The option to get the vendor group ID of GmsCore.
|
||||||
|
* @param addStringResources If the GmsCore shared strings should be added to the patched app.
|
||||||
|
* @param executeBlock The additional execution block of the patch.
|
||||||
|
* @param block The additional block to build the patch.
|
||||||
|
*/
|
||||||
|
// TODO: On the next major release make this public and delete the public overloaded constructor.
|
||||||
|
internal fun gmsCoreSupportResourcePatch(
|
||||||
|
fromPackageName: String,
|
||||||
|
toPackageName: String,
|
||||||
|
spoofedPackageSignature: String,
|
||||||
|
gmsCoreVendorGroupIdOption: Option<String>,
|
||||||
|
addStringResources: Boolean = true,
|
||||||
|
executeBlock: ResourcePatchContext.() -> Unit = {},
|
||||||
|
block: ResourcePatchBuilder.() -> Unit = {},
|
||||||
) = resourcePatch {
|
) = resourcePatch {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
changePackageNamePatch,
|
changePackageNamePatch,
|
||||||
@ -526,7 +547,10 @@ fun gmsCoreSupportResourcePatch(
|
|||||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
|
// Some patches don't use shared String resources so there's no need to add them.
|
||||||
|
if (addStringResources) {
|
||||||
|
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
|
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
|
||||||
|
@ -14,7 +14,7 @@ import app.revanced.util.findFreeRegister
|
|||||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
import app.revanced.util.insertLiteralOverride
|
||||||
import app.revanced.util.returnEarly
|
import app.revanced.util.returnEarly
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
@ -235,7 +235,7 @@ fun spoofVideoStreamsPatch(
|
|||||||
|
|
||||||
// region Fix iOS livestream current time.
|
// region Fix iOS livestream current time.
|
||||||
|
|
||||||
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride(
|
hlsCurrentTimeFingerprint.method.insertLiteralOverride(
|
||||||
HLS_CURRENT_TIME_FEATURE_FLAG,
|
HLS_CURRENT_TIME_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
|
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
|
||||||
)
|
)
|
||||||
@ -245,21 +245,21 @@ fun spoofVideoStreamsPatch(
|
|||||||
// region turn off stream config replacement feature flag.
|
// region turn off stream config replacement feature flag.
|
||||||
|
|
||||||
if (fixMediaFetchHotConfigChanges()) {
|
if (fixMediaFetchHotConfigChanges()) {
|
||||||
mediaFetchHotConfigFingerprint.method.insertFeatureFlagBooleanOverride(
|
mediaFetchHotConfigFingerprint.method.insertLiteralOverride(
|
||||||
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG,
|
MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
|
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixMediaFetchHotConfigAlternativeChanges()) {
|
if (fixMediaFetchHotConfigAlternativeChanges()) {
|
||||||
mediaFetchHotConfigAlternativeFingerprint.method.insertFeatureFlagBooleanOverride(
|
mediaFetchHotConfigAlternativeFingerprint.method.insertLiteralOverride(
|
||||||
MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG,
|
MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
|
"$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fixParsePlaybackResponseFeatureFlag()) {
|
if (fixParsePlaybackResponseFeatureFlag()) {
|
||||||
playbackStartDescriptorFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
playbackStartDescriptorFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||||
PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG,
|
PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z"
|
"$EXTENSION_CLASS_DESCRIPTOR->usePlaybackStartFeatureFlag(Z)Z"
|
||||||
)
|
)
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
package app.revanced.patches.spotify.layout.theme
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|
||||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
|
||||||
import app.revanced.util.*
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
|
|
||||||
|
|
||||||
internal val customThemeByteCodePatch = bytecodePatch {
|
|
||||||
dependsOn(sharedExtensionPatch)
|
|
||||||
|
|
||||||
val backgroundColor by spotifyBackgroundColor
|
|
||||||
val backgroundColorSecondary by spotifyBackgroundColorSecondary
|
|
||||||
|
|
||||||
execute {
|
|
||||||
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
|
|
||||||
val index = indexOfFirstLiteralInstructionOrThrow(literal)
|
|
||||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
|
||||||
|
|
||||||
addInstructions(
|
|
||||||
index + 1,
|
|
||||||
"""
|
|
||||||
const-string v$register, "$colorString"
|
|
||||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getThemeColor(Ljava/lang/String;)J
|
|
||||||
move-result-wide v$register
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val encoreColorsClassName = with(encoreThemeFingerprint) {
|
|
||||||
// Find index of the first static get found after the string constant.
|
|
||||||
val encoreColorsFieldReferenceIndex = originalMethod.indexOfFirstInstructionOrThrow(
|
|
||||||
stringMatches!!.first().index,
|
|
||||||
Opcode.SGET_OBJECT
|
|
||||||
)
|
|
||||||
|
|
||||||
originalMethod.getInstruction(encoreColorsFieldReferenceIndex)
|
|
||||||
.getReference<FieldReference>()!!.definingClass
|
|
||||||
}
|
|
||||||
|
|
||||||
val encoreColorsConstructorFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
|
||||||
custom { method, classDef ->
|
|
||||||
classDef.type == encoreColorsClassName &&
|
|
||||||
method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
encoreColorsConstructorFingerprint.method.apply {
|
|
||||||
// Playlist song list background color.
|
|
||||||
addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL, backgroundColor!!)
|
|
||||||
|
|
||||||
// Share menu background color.
|
|
||||||
addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL, backgroundColorSecondary!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
homeCategoryPillColorsFingerprint.method.apply {
|
|
||||||
// Home category pills background color.
|
|
||||||
addColorChangeInstructions(HOME_CATEGORY_PILL_COLOR_LITERAL, backgroundColorSecondary!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
settingsHeaderColorFingerprint.method.apply {
|
|
||||||
// Settings header background color.
|
|
||||||
addColorChangeInstructions(SETTINGS_HEADER_COLOR_LITERAL, backgroundColorSecondary!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,133 @@
|
|||||||
package app.revanced.patches.spotify.layout.theme
|
package app.revanced.patches.spotify.layout.theme
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.patcher.patch.booleanOption
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
|
import app.revanced.patcher.patch.stringOption
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||||
|
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.util.*
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
|
||||||
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
|
||||||
|
|
||||||
|
internal val spotifyBackgroundColor = stringOption(
|
||||||
|
key = "backgroundColor",
|
||||||
|
default = "@android:color/black",
|
||||||
|
title = "Primary background color",
|
||||||
|
description = "The background color. Can be a hex color or a resource reference.",
|
||||||
|
required = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val overridePlayerGradientColor = booleanOption(
|
||||||
|
key = "overridePlayerGradientColor",
|
||||||
|
default = false,
|
||||||
|
title = "Override player gradient color",
|
||||||
|
description = "Apply primary background color to the player gradient color, which changes dynamically with the song.",
|
||||||
|
required = false
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val spotifyBackgroundColorSecondary = stringOption(
|
||||||
|
key = "backgroundColorSecondary",
|
||||||
|
default = "#FF121212",
|
||||||
|
title = "Secondary background color",
|
||||||
|
description =
|
||||||
|
"The secondary background color. (e.g. playlist list in home, player artist, song credits). Can be a hex color or a resource reference.",
|
||||||
|
required = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val spotifyAccentColor = stringOption(
|
||||||
|
key = "accentColor",
|
||||||
|
default = "#FF1ED760",
|
||||||
|
title = "Accent color",
|
||||||
|
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
|
||||||
|
required = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val spotifyAccentColorPressed = stringOption(
|
||||||
|
key = "accentColorPressed",
|
||||||
|
default = "#FF169C46",
|
||||||
|
title = "Pressed dark theme accent color",
|
||||||
|
description =
|
||||||
|
"The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
|
||||||
|
required = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val customThemeBytecodePatch = bytecodePatch {
|
||||||
|
dependsOn(sharedExtensionPatch)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||||
|
// Bytecode changes are not needed for legacy app target.
|
||||||
|
// Player background color is changed with existing resource patch.
|
||||||
|
return@execute
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
|
||||||
|
val index = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
index + 1,
|
||||||
|
"""
|
||||||
|
const-string v$register, "$colorString"
|
||||||
|
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getThemeColor(Ljava/lang/String;)J
|
||||||
|
move-result-wide v$register
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val encoreColorsClassName = with(encoreThemeFingerprint.originalMethod) {
|
||||||
|
// "Encore" colors are referenced right before the value of POSITIVE_INFINITY is returned.
|
||||||
|
// Begin the instruction find using the index of where POSITIVE_INFINITY is set into the register.
|
||||||
|
val positiveInfinityIndex = indexOfFirstLiteralInstructionOrThrow(
|
||||||
|
Float.POSITIVE_INFINITY
|
||||||
|
)
|
||||||
|
val encoreColorsFieldReferenceIndex = indexOfFirstInstructionReversedOrThrow(
|
||||||
|
positiveInfinityIndex,
|
||||||
|
Opcode.SGET_OBJECT
|
||||||
|
)
|
||||||
|
|
||||||
|
getInstruction(encoreColorsFieldReferenceIndex)
|
||||||
|
.getReference<FieldReference>()!!.definingClass
|
||||||
|
}
|
||||||
|
|
||||||
|
val encoreColorsConstructorFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||||
|
custom { method, classDef ->
|
||||||
|
classDef.type == encoreColorsClassName &&
|
||||||
|
method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val backgroundColor by spotifyBackgroundColor
|
||||||
|
val backgroundColorSecondary by spotifyBackgroundColorSecondary
|
||||||
|
|
||||||
|
encoreColorsConstructorFingerprint.method.apply {
|
||||||
|
addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL, backgroundColor!!)
|
||||||
|
addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL, backgroundColorSecondary!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
homeCategoryPillColorsFingerprint.method.addColorChangeInstructions(
|
||||||
|
HOME_CATEGORY_PILL_COLOR_LITERAL,
|
||||||
|
backgroundColorSecondary!!
|
||||||
|
)
|
||||||
|
|
||||||
|
settingsHeaderColorFingerprint.method.addColorChangeInstructions(
|
||||||
|
SETTINGS_HEADER_COLOR_LITERAL,
|
||||||
|
backgroundColorSecondary!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val customThemePatch = resourcePatch(
|
val customThemePatch = resourcePatch(
|
||||||
name = "Custom theme",
|
name = "Custom theme",
|
||||||
@ -11,9 +136,10 @@ val customThemePatch = resourcePatch(
|
|||||||
) {
|
) {
|
||||||
compatibleWith("com.spotify.music")
|
compatibleWith("com.spotify.music")
|
||||||
|
|
||||||
dependsOn(customThemeByteCodePatch)
|
dependsOn(customThemeBytecodePatch)
|
||||||
|
|
||||||
val backgroundColor by spotifyBackgroundColor()
|
val backgroundColor by spotifyBackgroundColor()
|
||||||
|
val overridePlayerGradientColor by overridePlayerGradientColor()
|
||||||
val backgroundColorSecondary by spotifyBackgroundColorSecondary()
|
val backgroundColorSecondary by spotifyBackgroundColorSecondary()
|
||||||
val accentColor by spotifyAccentColor()
|
val accentColor by spotifyAccentColor()
|
||||||
val accentColorPressed by spotifyAccentColorPressed()
|
val accentColorPressed by spotifyAccentColorPressed()
|
||||||
@ -25,31 +151,39 @@ val customThemePatch = resourcePatch(
|
|||||||
val childNodes = resourcesNode.childNodes
|
val childNodes = resourcesNode.childNodes
|
||||||
for (i in 0 until childNodes.length) {
|
for (i in 0 until childNodes.length) {
|
||||||
val node = childNodes.item(i) as? Element ?: continue
|
val node = childNodes.item(i) as? Element ?: continue
|
||||||
|
val name = node.getAttribute("name")
|
||||||
|
|
||||||
node.textContent = when (node.getAttribute("name")) {
|
// Skip overriding song/player gradient start color if the option is disabled.
|
||||||
// Gradient next to user photo and "All" in home page
|
// Gradient end color should be themed regardless to allow the gradient to connect with
|
||||||
|
// our primary background color.
|
||||||
|
if (name == "bg_gradient_start_color" && !overridePlayerGradientColor!!) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
node.textContent = when (name) {
|
||||||
|
// Gradient next to user photo and "All" in home page.
|
||||||
"dark_base_background_base",
|
"dark_base_background_base",
|
||||||
// Main background
|
// Main background.
|
||||||
"gray_7",
|
"gray_7",
|
||||||
// Left sidebar background in tablet mode
|
// Left sidebar background in tablet mode.
|
||||||
"gray_10",
|
"gray_10",
|
||||||
// Add account, Settings and privacy, View Profile left sidebar background
|
// "Add account", "Settings and privacy", "View Profile" left sidebar background.
|
||||||
"dark_base_background_elevated_base",
|
"dark_base_background_elevated_base",
|
||||||
// Song/player background
|
// Song/player gradient start/end color.
|
||||||
"bg_gradient_start_color", "bg_gradient_end_color",
|
"bg_gradient_start_color", "bg_gradient_end_color",
|
||||||
// Login screen
|
// Login screen background and gradient start.
|
||||||
"sthlm_blk", "sthlm_blk_grad_start", "stockholm_black",
|
"sthlm_blk", "sthlm_blk_grad_start",
|
||||||
// Misc
|
// Misc.
|
||||||
"image_placeholder_color",
|
"image_placeholder_color",
|
||||||
-> backgroundColor
|
-> backgroundColor
|
||||||
|
|
||||||
// Track credits, merch in song player
|
// Track credits, merch background in song player.
|
||||||
"track_credits_card_bg", "benefit_list_default_color", "merch_card_background",
|
"track_credits_card_bg", "benefit_list_default_color", "merch_card_background",
|
||||||
// Playlist list background in home page
|
// Playlist list background in home page.
|
||||||
"opacity_white_10",
|
"opacity_white_10",
|
||||||
// About artist background in song player
|
// "About the artist" background in song player.
|
||||||
"gray_15",
|
"gray_15",
|
||||||
// What's New pills background
|
// "What's New" pills background.
|
||||||
"dark_base_background_tinted_highlight"
|
"dark_base_background_tinted_highlight"
|
||||||
-> backgroundColorSecondary
|
-> backgroundColorSecondary
|
||||||
|
|
||||||
@ -59,5 +193,13 @@ val customThemePatch = resourcePatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Login screen gradient.
|
||||||
|
document("res/drawable/start_screen_gradient.xml").use { document ->
|
||||||
|
val gradientNode = document.getElementsByTagName("gradient").item(0) as Element
|
||||||
|
|
||||||
|
gradientNode.setAttribute("android:startColor", backgroundColor)
|
||||||
|
gradientNode.setAttribute("android:endColor", backgroundColor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,15 @@ import com.android.tools.smali.dexlib2.AccessFlags
|
|||||||
|
|
||||||
internal val encoreThemeFingerprint = fingerprint {
|
internal val encoreThemeFingerprint = fingerprint {
|
||||||
strings("Encore theme was not provided.") // Partial string match.
|
strings("Encore theme was not provided.") // Partial string match.
|
||||||
|
custom { method, _ ->
|
||||||
|
method.name == "invoke"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal const val SETTINGS_HEADER_COLOR_LITERAL = 0xFF282828
|
|
||||||
internal const val HOME_CATEGORY_PILL_COLOR_LITERAL = 0xFF333333
|
|
||||||
internal const val PLAYLIST_BACKGROUND_COLOR_LITERAL = 0xFF121212
|
internal const val PLAYLIST_BACKGROUND_COLOR_LITERAL = 0xFF121212
|
||||||
internal const val SHARE_MENU_BACKGROUND_COLOR_LITERAL = 0xFF1F1F1F
|
internal const val SHARE_MENU_BACKGROUND_COLOR_LITERAL = 0xFF1F1F1F
|
||||||
|
internal const val HOME_CATEGORY_PILL_COLOR_LITERAL = 0xFF333333
|
||||||
|
internal const val SETTINGS_HEADER_COLOR_LITERAL = 0xFF282828
|
||||||
|
|
||||||
internal val homeCategoryPillColorsFingerprint = fingerprint{
|
internal val homeCategoryPillColorsFingerprint = fingerprint{
|
||||||
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
package app.revanced.patches.spotify.layout.theme
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.stringOption
|
|
||||||
|
|
||||||
internal val spotifyBackgroundColor = stringOption(
|
|
||||||
key = "backgroundColor",
|
|
||||||
default = "@android:color/black",
|
|
||||||
title = "Primary background color",
|
|
||||||
description = "The background color. Can be a hex color or a resource reference.",
|
|
||||||
required = true,
|
|
||||||
)
|
|
||||||
|
|
||||||
internal val spotifyBackgroundColorSecondary = stringOption(
|
|
||||||
key = "backgroundColorSecondary",
|
|
||||||
default = "#FF121212",
|
|
||||||
title = "Secondary background color",
|
|
||||||
description = "The secondary background color. (e.g. playlist list, player arist, credits). Can be a hex color or a resource reference.",
|
|
||||||
required = true,
|
|
||||||
)
|
|
||||||
|
|
||||||
internal val spotifyAccentColor = stringOption(
|
|
||||||
key = "accentColor",
|
|
||||||
default = "#FF1ED760",
|
|
||||||
title = "Accent color",
|
|
||||||
description = "The accent color ('Spotify green' by default). Can be a hex color or a resource reference.",
|
|
||||||
required = true,
|
|
||||||
)
|
|
||||||
|
|
||||||
internal val spotifyAccentColorPressed = stringOption(
|
|
||||||
key = "accentColorPressed",
|
|
||||||
default = "#FF169C46",
|
|
||||||
title = "Pressed dark theme accent color",
|
|
||||||
description =
|
|
||||||
"The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
|
|
||||||
required = true,
|
|
||||||
)
|
|
@ -1,16 +1,29 @@
|
|||||||
package app.revanced.patches.spotify.misc
|
package app.revanced.patches.spotify.misc
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal val accountAttributeFingerprint = fingerprint {
|
internal val accountAttributeFingerprint = fingerprint {
|
||||||
custom { _, classDef -> classDef.endsWith("internal/AccountAttribute;") }
|
custom { _, classDef ->
|
||||||
|
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||||
|
"Lcom/spotify/useraccount/v1/AccountAttribute;"
|
||||||
|
} else {
|
||||||
|
"Lcom/spotify/remoteconfig/internal/AccountAttribute;"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val productStateProtoFingerprint = fingerprint {
|
internal val productStateProtoFingerprint = fingerprint {
|
||||||
returns("Ljava/util/Map;")
|
returns("Ljava/util/Map;")
|
||||||
custom { _, classDef -> classDef.endsWith("ProductStateProto;") }
|
custom { _, classDef ->
|
||||||
|
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||||
|
"Lcom/spotify/ucs/proto/v0/UcsResponseWrapper${'$'}AccountAttributesResponse;"
|
||||||
|
} else {
|
||||||
|
"Lcom/spotify/remoteconfig/internal/ProductStateProto;"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val buildQueryParametersFingerprint = fingerprint {
|
internal val buildQueryParametersFingerprint = fingerprint {
|
||||||
@ -22,6 +35,27 @@ internal val contextMenuExperimentsFingerprint = fingerprint {
|
|||||||
strings("remove_ads_upsell_enabled")
|
strings("remove_ads_upsell_enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val contextFromJsonFingerprint = fingerprint {
|
||||||
|
opcodes(
|
||||||
|
Opcode.INVOKE_STATIC,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.INVOKE_STATIC
|
||||||
|
)
|
||||||
|
custom { methodDef, classDef ->
|
||||||
|
methodDef.name == "fromJson" &&
|
||||||
|
classDef.endsWith("voiceassistants/playermodels/ContextJsonAdapter;")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val readPlayerOptionOverridesFingerprint = fingerprint {
|
||||||
|
custom { methodDef, classDef ->
|
||||||
|
methodDef.name == "readPlayerOptionOverrides" &&
|
||||||
|
classDef.endsWith("voiceassistants/playermodels/PreparePlayOptionsJsonAdapter;")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal val homeSectionFingerprint = fingerprint {
|
internal val homeSectionFingerprint = fingerprint {
|
||||||
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
|
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,26 @@
|
|||||||
package app.revanced.patches.spotify.misc
|
package app.revanced.patches.spotify.misc
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patches.spotify.misc.check.checkEnvironmentPatch
|
||||||
|
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.util.*
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;"
|
||||||
|
|
||||||
@ -24,7 +31,15 @@ val unlockPremiumPatch = bytecodePatch(
|
|||||||
) {
|
) {
|
||||||
compatibleWith("com.spotify.music")
|
compatibleWith("com.spotify.music")
|
||||||
|
|
||||||
dependsOn(sharedExtensionPatch)
|
dependsOn(
|
||||||
|
sharedExtensionPatch,
|
||||||
|
// Currently there is no easy way to make a mandatory patch,
|
||||||
|
// so for now this is a dependent of this patch.
|
||||||
|
//
|
||||||
|
// FIXME: Modifying string resources (such as adding patch strings)
|
||||||
|
// is currently failing with ReVanced manager.
|
||||||
|
// checkEnvironmentPatch,
|
||||||
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
// Make _value accessible so that it can be overridden in the extension.
|
// Make _value accessible so that it can be overridden in the extension.
|
||||||
@ -34,7 +49,7 @@ val unlockPremiumPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Override the attributes map in the getter method.
|
// Override the attributes map in the getter method.
|
||||||
with(productStateProtoFingerprint.method) {
|
productStateProtoFingerprint.method.apply {
|
||||||
val getAttributesMapIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
val getAttributesMapIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
||||||
val attributesMapRegister = getInstruction<TwoRegisterInstruction>(getAttributesMapIndex).registerA
|
val attributesMapRegister = getInstruction<TwoRegisterInstruction>(getAttributesMapIndex).registerA
|
||||||
|
|
||||||
@ -45,25 +60,74 @@ val unlockPremiumPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Add the query parameter trackRows to show popular tracks in the artist page.
|
// Add the query parameter trackRows to show popular tracks in the artist page.
|
||||||
with(buildQueryParametersFingerprint) {
|
buildQueryParametersFingerprint.apply {
|
||||||
val addQueryParameterConditionIndex = method.indexOfFirstInstructionReversedOrThrow(
|
val addQueryParameterConditionIndex = method.indexOfFirstInstructionReversedOrThrow(
|
||||||
stringMatches!!.first().index, Opcode.IF_EQZ
|
stringMatches!!.first().index, Opcode.IF_EQZ
|
||||||
)
|
)
|
||||||
|
|
||||||
method.replaceInstruction(addQueryParameterConditionIndex, "nop")
|
method.replaceInstruction(addQueryParameterConditionIndex, "nop")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||||
|
return@execute Logger.getLogger(this::class.java.name).warning(
|
||||||
|
"Patching a legacy Spotify version. Patch functionality may be limited."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Enable choosing a specific song/artist via Google Assistant.
|
||||||
|
contextFromJsonFingerprint.method.apply {
|
||||||
|
val insertIndex = contextFromJsonFingerprint.patternMatch!!.startIndex
|
||||||
|
// Both the URI and URL need to be modified.
|
||||||
|
val registerUrl = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
|
||||||
|
val registerUri = getInstruction<FiveRegisterInstruction>(insertIndex + 2).registerD
|
||||||
|
|
||||||
|
val extensionMethodDescriptor = "$EXTENSION_CLASS_DESCRIPTOR->" +
|
||||||
|
"removeStationString(Ljava/lang/String;)Ljava/lang/String;"
|
||||||
|
|
||||||
|
addInstructions(
|
||||||
|
insertIndex,
|
||||||
|
"""
|
||||||
|
invoke-static { v$registerUrl }, $extensionMethodDescriptor
|
||||||
|
move-result-object v$registerUrl
|
||||||
|
invoke-static { v$registerUri }, $extensionMethodDescriptor
|
||||||
|
move-result-object v$registerUri
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Disable forced shuffle when asking for an album/playlist via Google Assistant.
|
||||||
|
readPlayerOptionOverridesFingerprint.method.apply {
|
||||||
|
val shufflingContextCallIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
getReference<MethodReference>()?.name == "shufflingContext"
|
||||||
|
}
|
||||||
|
|
||||||
|
val registerBool = getInstruction<FiveRegisterInstruction>(shufflingContextCallIndex).registerD
|
||||||
|
addInstruction(
|
||||||
|
shufflingContextCallIndex,
|
||||||
|
"sget-object v$registerBool, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Disable the "Spotify Premium" upsell experiment in context menus.
|
// Disable the "Spotify Premium" upsell experiment in context menus.
|
||||||
with(contextMenuExperimentsFingerprint) {
|
contextMenuExperimentsFingerprint.apply {
|
||||||
val moveIsEnabledIndex = method.indexOfFirstInstructionOrThrow(
|
val moveIsEnabledIndex = method.indexOfFirstInstructionOrThrow(
|
||||||
stringMatches!!.first().index, Opcode.MOVE_RESULT
|
stringMatches!!.first().index, Opcode.MOVE_RESULT
|
||||||
)
|
)
|
||||||
val isUpsellEnabledRegister = method.getInstruction<OneRegisterInstruction>(moveIsEnabledIndex).registerA
|
val isUpsellEnabledRegister = method.getInstruction<OneRegisterInstruction>(moveIsEnabledIndex).registerA
|
||||||
|
|
||||||
method.replaceInstruction(moveIsEnabledIndex, "const/4 v$isUpsellEnabledRegister, 0")
|
method.replaceInstruction(moveIsEnabledIndex, "const/4 v$isUpsellEnabledRegister, 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Make featureTypeCase_ accessible so we can check the home section type in the extension.
|
// Make featureTypeCase_ accessible so we can check the home section type in the extension.
|
||||||
homeSectionFingerprint.classDef.fields.first { it.name == "featureTypeCase_" }.apply {
|
homeSectionFingerprint.classDef.fields.first { it.name == "featureTypeCase_" }.apply {
|
||||||
|
// Add public flag and remove private.
|
||||||
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv())
|
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +146,7 @@ val unlockPremiumPatch = bytecodePatch(
|
|||||||
// Protobuffer list has an 'isMutable' boolean parameter that sets the mutability.
|
// Protobuffer list has an 'isMutable' boolean parameter that sets the mutability.
|
||||||
// Forcing that always on breaks unrelated code in strange ways.
|
// Forcing that always on breaks unrelated code in strange ways.
|
||||||
// Instead, remove the method call that checks if the list is unmodifiable.
|
// Instead, remove the method call that checks if the list is unmodifiable.
|
||||||
with(protobufListRemoveFingerprint.method) {
|
protobufListRemoveFingerprint.method.apply {
|
||||||
val invokeThrowUnmodifiableIndex = indexOfFirstInstructionOrThrow {
|
val invokeThrowUnmodifiableIndex = indexOfFirstInstructionOrThrow {
|
||||||
val reference = getReference<MethodReference>()
|
val reference = getReference<MethodReference>()
|
||||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||||
@ -94,7 +158,7 @@ val unlockPremiumPatch = bytecodePatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove ads sections from home.
|
// Remove ads sections from home.
|
||||||
with(homeStructureFingerprint.method) {
|
homeStructureFingerprint.method.apply {
|
||||||
val getSectionsIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
val getSectionsIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
||||||
val sectionsRegister = getInstruction<TwoRegisterInstruction>(getSectionsIndex).registerA
|
val sectionsRegister = getInstruction<TwoRegisterInstruction>(getSectionsIndex).registerA
|
||||||
|
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package app.revanced.patches.spotify.misc.check
|
||||||
|
|
||||||
|
import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch
|
||||||
|
import app.revanced.patches.spotify.shared.mainActivityOnCreateFingerprint
|
||||||
|
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||||
|
|
||||||
|
internal val checkEnvironmentPatch = checkEnvironmentPatch(
|
||||||
|
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
|
||||||
|
extensionPatch = sharedExtensionPatch,
|
||||||
|
"com.spotify.music",
|
||||||
|
)
|
@ -1,5 +1,21 @@
|
|||||||
package app.revanced.patches.spotify.misc.extension
|
package app.revanced.patches.spotify.misc.extension
|
||||||
|
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.patches.spotify.shared.SPOTIFY_MAIN_ACTIVITY_LEGACY
|
||||||
|
|
||||||
val sharedExtensionPatch = sharedExtensionPatch("spotify", spotifyMainActivityOnCreate)
|
/**
|
||||||
|
* If patching a legacy 8.x target. This may also be set if patching slightly older/newer app targets,
|
||||||
|
* but the only legacy target of interest is 8.6.98.900 as it's the last version that
|
||||||
|
* supports Spotify integration on Kenwood/Pioneer car stereos.
|
||||||
|
*/
|
||||||
|
internal var IS_SPOTIFY_LEGACY_APP_TARGET = false
|
||||||
|
|
||||||
|
val sharedExtensionPatch = bytecodePatch {
|
||||||
|
dependsOn(sharedExtensionPatch("spotify", mainActivityOnCreateHook))
|
||||||
|
|
||||||
|
execute {
|
||||||
|
IS_SPOTIFY_LEGACY_APP_TARGET = mainActivityOnCreateHook.fingerprint
|
||||||
|
.originalClassDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
package app.revanced.patches.spotify.misc.extension
|
package app.revanced.patches.spotify.misc.extension
|
||||||
|
|
||||||
import app.revanced.patches.shared.misc.extension.extensionHook
|
import app.revanced.patches.shared.misc.extension.extensionHook
|
||||||
|
import app.revanced.patches.spotify.shared.mainActivityOnCreateFingerprint
|
||||||
|
|
||||||
internal val spotifyMainActivityOnCreate = extensionHook {
|
internal val mainActivityOnCreateHook = extensionHook(fingerprint = mainActivityOnCreateFingerprint)
|
||||||
custom { method, classDef ->
|
|
||||||
classDef.type == "Lcom/spotify/music/SpotifyMainActivity;" &&
|
|
||||||
method.name == "onCreate"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,4 +2,8 @@ package app.revanced.patches.spotify.misc.fix
|
|||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
internal val getAppSignatureFingerprint = fingerprint { strings("Failed to get the application signatures") }
|
internal val getPackageInfoFingerprint = fingerprint {
|
||||||
|
strings(
|
||||||
|
"Failed to get the application signatures"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
package app.revanced.patches.spotify.misc.fix
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val spoofPackageInfoPatch = bytecodePatch(
|
||||||
|
name = "Spoof package info",
|
||||||
|
description = "Spoofs the package info of the app to fix various functions of the app.",
|
||||||
|
) {
|
||||||
|
compatibleWith("com.spotify.music")
|
||||||
|
|
||||||
|
execute {
|
||||||
|
getPackageInfoFingerprint.method.apply {
|
||||||
|
// region Spoof signature.
|
||||||
|
|
||||||
|
val failedToGetSignaturesStringIndex =
|
||||||
|
getPackageInfoFingerprint.stringMatches!!.first().index
|
||||||
|
|
||||||
|
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
|
||||||
|
failedToGetSignaturesStringIndex,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
)
|
||||||
|
|
||||||
|
val signatureRegister = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
|
||||||
|
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
|
||||||
|
|
||||||
|
replaceInstruction(concatSignaturesIndex, "const-string v$signatureRegister, \"$expectedSignature\"")
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Spoof installer name.
|
||||||
|
|
||||||
|
val expectedInstallerName = "com.android.vending"
|
||||||
|
|
||||||
|
findInstructionIndicesReversedOrThrow {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
reference?.name == "getInstallerPackageName" || reference?.name == "getInstallingPackageName"
|
||||||
|
}.forEach { index ->
|
||||||
|
val returnObjectIndex = index + 1
|
||||||
|
|
||||||
|
val installerPackageNameRegister = getInstruction<OneRegisterInstruction>(
|
||||||
|
returnObjectIndex
|
||||||
|
).registerA
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
returnObjectIndex + 1,
|
||||||
|
"const-string v$installerPackageNameRegister, \"$expectedInstallerName\""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +1,13 @@
|
|||||||
package app.revanced.patches.spotify.misc.fix
|
package app.revanced.patches.spotify.misc.fix
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|
||||||
|
|
||||||
|
@Deprecated("Superseded by spoofPackageInfoPatch", ReplaceWith("spoofPackageInfoPatch"))
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val spoofSignaturePatch = bytecodePatch(
|
val spoofSignaturePatch = bytecodePatch(
|
||||||
name = "Spoof signature",
|
description = "Spoofs the signature of the app fix various functions of the app.",
|
||||||
description = "Spoofs the signature of the app to fix various functions of the app.",
|
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.spotify.music")
|
compatibleWith("com.spotify.music")
|
||||||
|
|
||||||
execute {
|
dependsOn(spoofPackageInfoPatch)
|
||||||
getAppSignatureFingerprint.method.apply {
|
|
||||||
val failedToGetSignaturesStringMatch = getAppSignatureFingerprint.stringMatches!!.first()
|
|
||||||
|
|
||||||
val concatSignaturesIndex = indexOfFirstInstructionReversedOrThrow(
|
|
||||||
failedToGetSignaturesStringMatch.index,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
)
|
|
||||||
|
|
||||||
val register = getInstruction<OneRegisterInstruction>(concatSignaturesIndex).registerA
|
|
||||||
|
|
||||||
val expectedSignature = "d6a6dced4a85f24204bf9505ccc1fce114cadb32"
|
|
||||||
|
|
||||||
replaceInstruction(concatSignaturesIndex, "const-string v$register, \"$expectedSignature\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package app.revanced.patches.spotify.shared
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
|
|
||||||
|
private const val SPOTIFY_MAIN_ACTIVITY = "Lcom/spotify/music/SpotifyMainActivity;"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main activity of target 8.6.98.900.
|
||||||
|
*/
|
||||||
|
internal const val SPOTIFY_MAIN_ACTIVITY_LEGACY = "Lcom/spotify/music/MainActivity;"
|
||||||
|
|
||||||
|
internal val mainActivityOnCreateFingerprint = fingerprint {
|
||||||
|
custom { method, classDef ->
|
||||||
|
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|
||||||
|
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
|||||||
val disableSubscriptionSuggestionsPatch = bytecodePatch(
|
val disableSubscriptionSuggestionsPatch = bytecodePatch(
|
||||||
name = "Disable subscription suggestions",
|
name = "Disable subscription suggestions",
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.strava"("320.12"))
|
compatibleWith("com.strava")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
val helperMethodName = "getModulesIfNotUpselling"
|
val helperMethodName = "getModulesIfNotUpselling"
|
||||||
|
@ -12,7 +12,7 @@ val dynamicColorPatch = resourcePatch(
|
|||||||
) {
|
) {
|
||||||
compatibleWith(
|
compatibleWith(
|
||||||
"com.twitter.android"(
|
"com.twitter.android"(
|
||||||
"10.84.0-release.0",
|
"10.86.0-release.0",
|
||||||
"10.60.0-release.0",
|
"10.60.0-release.0",
|
||||||
"10.48.0-release.0"
|
"10.48.0-release.0"
|
||||||
)
|
)
|
||||||
|
@ -13,8 +13,8 @@ fun hookPatch(
|
|||||||
|
|
||||||
compatibleWith(
|
compatibleWith(
|
||||||
"com.twitter.android"(
|
"com.twitter.android"(
|
||||||
// 10.85+ uses Pairip and requires additional changes to work.
|
// Only v10.85 uses Pairip and requires additional changes to work.
|
||||||
"10.84.0-release.0",
|
"10.86.0-release.0",
|
||||||
// Confirmed to not show reply ads. Slightly newer versions may also work.
|
// Confirmed to not show reply ads. Slightly newer versions may also work.
|
||||||
"10.60.0-release.0",
|
"10.60.0-release.0",
|
||||||
"10.48.0-release.0"
|
"10.48.0-release.0"
|
||||||
|
@ -39,7 +39,7 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
|||||||
|
|
||||||
compatibleWith(
|
compatibleWith(
|
||||||
"com.twitter.android"(
|
"com.twitter.android"(
|
||||||
"10.84.0-release.0",
|
"10.86.0-release.0",
|
||||||
"10.60.0-release.0",
|
"10.60.0-release.0",
|
||||||
"10.48.0-release.0"
|
"10.48.0-release.0"
|
||||||
)
|
)
|
||||||
|
@ -10,7 +10,7 @@ val sanitizeSharingLinksPatch = bytecodePatch(
|
|||||||
) {
|
) {
|
||||||
compatibleWith(
|
compatibleWith(
|
||||||
"com.twitter.android"(
|
"com.twitter.android"(
|
||||||
"10.84.0-release.0",
|
"10.86.0-release.0",
|
||||||
"10.60.0-release.0",
|
"10.60.0-release.0",
|
||||||
"10.48.0-release.0"
|
"10.48.0-release.0"
|
||||||
)
|
)
|
||||||
|
@ -84,7 +84,8 @@ val hideAdsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -31,7 +31,8 @@ val hideGetPremiumPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -29,7 +29,8 @@ val videoAdsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -59,7 +59,8 @@ val copyVideoUrlPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -30,7 +30,8 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val extensionMethodDescriptor =
|
val extensionMethodDescriptor =
|
||||||
|
@ -74,7 +74,8 @@ val downloadsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -10,10 +10,15 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
|
import app.revanced.util.findFreeRegister
|
||||||
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
|
||||||
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;"
|
||||||
|
|
||||||
val enableSeekbarTappingPatch = bytecodePatch(
|
val enableSeekbarTappingPatch = bytecodePatch(
|
||||||
description = "Adds an option to enable tap to seek on the seekbar of the video player.",
|
description = "Adds an option to enable tap to seek on the seekbar of the video player.",
|
||||||
) {
|
) {
|
||||||
@ -31,39 +36,37 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Find the required methods to tap the seekbar.
|
// Find the required methods to tap the seekbar.
|
||||||
val patternMatch = onTouchEventHandlerFingerprint.patternMatch!!
|
val seekbarTappingMethods = onTouchEventHandlerFingerprint.let {
|
||||||
|
fun getMethodReference(index: Int) = it.method.getInstruction<ReferenceInstruction>(index)
|
||||||
|
.reference as MethodReference
|
||||||
|
|
||||||
fun getReference(index: Int) = onTouchEventHandlerFingerprint.method.getInstruction<ReferenceInstruction>(index)
|
listOf(
|
||||||
.reference as MethodReference
|
getMethodReference(it.patternMatch!!.startIndex),
|
||||||
|
getMethodReference(it.patternMatch!!.endIndex)
|
||||||
val seekbarTappingMethods = buildMap {
|
)
|
||||||
put("N", getReference(patternMatch.startIndex))
|
|
||||||
put("O", getReference(patternMatch.endIndex))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val insertIndex = seekbarTappingFingerprint.patternMatch!!.endIndex - 1
|
|
||||||
|
|
||||||
seekbarTappingFingerprint.method.apply {
|
seekbarTappingFingerprint.method.apply {
|
||||||
val thisInstanceRegister = getInstruction<Instruction35c>(insertIndex - 1).registerC
|
val pointIndex = indexOfNewPointInstruction(this)
|
||||||
|
val invokeIndex = indexOfFirstInstructionOrThrow(pointIndex, Opcode.INVOKE_VIRTUAL)
|
||||||
|
val insertIndex = invokeIndex + 1
|
||||||
|
|
||||||
val freeRegister = 0
|
val thisInstanceRegister = getInstruction<FiveRegisterInstruction>(invokeIndex).registerC
|
||||||
val xAxisRegister = 2
|
val xAxisRegister = this.getInstruction<FiveRegisterInstruction>(pointIndex).registerD
|
||||||
|
val freeRegister = findFreeRegister(insertIndex, thisInstanceRegister, xAxisRegister)
|
||||||
|
|
||||||
val oMethod = seekbarTappingMethods["O"]!!
|
val oMethod = seekbarTappingMethods[0]
|
||||||
val nMethod = seekbarTappingMethods["N"]!!
|
val nMethod = seekbarTappingMethods[1]
|
||||||
|
|
||||||
fun MethodReference.toInvokeInstructionString() =
|
|
||||||
"invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $this"
|
|
||||||
|
|
||||||
addInstructionsWithLabels(
|
addInstructionsWithLabels(
|
||||||
insertIndex,
|
insertIndex,
|
||||||
"""
|
"""
|
||||||
invoke-static { }, Lapp/revanced/extension/youtube/patches/SeekbarTappingPatch;->seekbarTappingEnabled()Z
|
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->seekbarTappingEnabled()Z
|
||||||
move-result v$freeRegister
|
move-result v$freeRegister
|
||||||
if-eqz v$freeRegister, :disabled
|
if-eqz v$freeRegister, :disabled
|
||||||
${oMethod.toInvokeInstructionString()}
|
invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $oMethod
|
||||||
${nMethod.toInvokeInstructionString()}
|
invoke-virtual { v$thisInstanceRegister, v$xAxisRegister }, $nMethod
|
||||||
""",
|
""",
|
||||||
ExternalLabel("disabled", getInstruction(insertIndex)),
|
ExternalLabel("disabled", getInstruction(insertIndex)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package app.revanced.patches.youtube.interaction.seekbar
|
package app.revanced.patches.youtube.interaction.seekbar
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.containsLiteralInstruction
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstruction
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
|
import app.revanced.util.indexOfFirstInstructionReversed
|
||||||
import app.revanced.util.literal
|
import app.revanced.util.literal
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
|
|
||||||
internal val swipingUpGestureParentFingerprint = fingerprint {
|
internal val swipingUpGestureParentFingerprint = fingerprint {
|
||||||
@ -101,14 +105,17 @@ internal val seekbarTappingFingerprint = fingerprint {
|
|||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
returns("Z")
|
returns("Z")
|
||||||
parameters("L")
|
parameters("L")
|
||||||
opcodes(
|
custom { method, _ ->
|
||||||
Opcode.IPUT_OBJECT,
|
method.name == "onTouchEvent"
|
||||||
Opcode.INVOKE_VIRTUAL,
|
&& method.containsLiteralInstruction(Integer.MAX_VALUE.toLong())
|
||||||
// Insert seekbar tapping instructions here.
|
&& indexOfNewPointInstruction(method) >= 0
|
||||||
Opcode.RETURN,
|
}
|
||||||
Opcode.INVOKE_VIRTUAL,
|
}
|
||||||
)
|
|
||||||
literal { Integer.MAX_VALUE.toLong() }
|
internal fun indexOfNewPointInstruction(method: Method) = method.indexOfFirstInstructionReversed {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
reference?.definingClass == "Landroid/graphics/Point;"
|
||||||
|
&& reference.name == "<init>"
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val slideToSeekFingerprint = fingerprint {
|
internal val slideToSeekFingerprint = fingerprint {
|
||||||
|
@ -26,6 +26,7 @@ val seekbarPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
|
"20.12.46",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
|
|||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.InputType
|
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
@ -42,11 +43,16 @@ private val swipeControlsResourcePatch = resourcePatch {
|
|||||||
SwitchPreference("revanced_swipe_haptic_feedback"),
|
SwitchPreference("revanced_swipe_haptic_feedback"),
|
||||||
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
|
SwitchPreference("revanced_swipe_save_and_restore_brightness"),
|
||||||
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
|
SwitchPreference("revanced_swipe_lowest_value_enable_auto_brightness"),
|
||||||
SwitchPreference("revanced_swipe_show_circular_overlay"),
|
ListPreference(
|
||||||
SwitchPreference("revanced_swipe_overlay_minimal_style"),
|
"revanced_swipe_overlay_style",
|
||||||
|
summaryKey = null,
|
||||||
|
),
|
||||||
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
TextPreference("revanced_swipe_overlay_background_opacity", inputType = InputType.NUMBER),
|
||||||
|
TextPreference("revanced_swipe_overlay_progress_color", inputType = InputType.TEXT_CAP_CHARACTERS),
|
||||||
|
TextPreference("revanced_swipe_text_overlay_size", inputType = InputType.NUMBER),
|
||||||
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
|
TextPreference("revanced_swipe_overlay_timeout", inputType = InputType.NUMBER),
|
||||||
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
|
TextPreference("revanced_swipe_threshold", inputType = InputType.NUMBER),
|
||||||
|
TextPreference("revanced_swipe_volume_sensitivity", inputType = InputType.NUMBER),
|
||||||
)
|
)
|
||||||
|
|
||||||
copyResources(
|
copyResources(
|
||||||
@ -86,7 +92,8 @@ val swipeControlsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
@ -117,7 +124,7 @@ val swipeControlsPatch = bytecodePatch(
|
|||||||
// region patch to enable/disable swipe to change video.
|
// region patch to enable/disable swipe to change video.
|
||||||
|
|
||||||
if (is_19_43_or_greater) {
|
if (is_19_43_or_greater) {
|
||||||
swipeChangeVideoFingerprint.method.insertFeatureFlagBooleanOverride(
|
swipeChangeVideoFingerprint.method.insertLiteralOverride(
|
||||||
SWIPE_CHANGE_VIDEO_FEATURE_FLAG,
|
SWIPE_CHANGE_VIDEO_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z"
|
"$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z"
|
||||||
)
|
)
|
||||||
|
@ -8,7 +8,9 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.patches.youtube.shared.subtitleButtonControllerFingerprint
|
|
||||||
|
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
|
"Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;"
|
||||||
|
|
||||||
val autoCaptionsPatch = bytecodePatch(
|
val autoCaptionsPatch = bytecodePatch(
|
||||||
name = "Disable auto captions",
|
name = "Disable auto captions",
|
||||||
@ -28,42 +30,41 @@ val autoCaptionsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
addResources("youtube", "layout.autocaptions.autoCaptionsPatch")
|
addResources("youtube", "layout.autocaptions.autoCaptionsPatch")
|
||||||
|
|
||||||
PreferenceScreen.PLAYER.addPreferences(
|
PreferenceScreen.PLAYER.addPreferences(
|
||||||
SwitchPreference("revanced_auto_captions"),
|
SwitchPreference("revanced_disable_auto_captions"),
|
||||||
|
)
|
||||||
|
|
||||||
|
subtitleTrackFingerprint.method.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableAutoCaptions()Z
|
||||||
|
move-result v0
|
||||||
|
if-eqz v0, :auto_captions_enabled
|
||||||
|
const/4 v0, 0x1
|
||||||
|
return v0
|
||||||
|
:auto_captions_enabled
|
||||||
|
nop
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
mapOf(
|
mapOf(
|
||||||
startVideoInformerFingerprint to 0,
|
startVideoInformerFingerprint to 0,
|
||||||
subtitleButtonControllerFingerprint to 1,
|
storyboardRendererDecoderRecommendedLevelFingerprint to 1
|
||||||
).forEach { (fingerprint, enabled) ->
|
).forEach { (fingerprint, enabled) ->
|
||||||
fingerprint.method.addInstructions(
|
fingerprint.method.addInstructions(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
const/4 v0, 0x$enabled
|
const/4 v0, 0x$enabled
|
||||||
sput-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z
|
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setCaptionsButtonStatus(Z)V
|
||||||
""",
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
subtitleTrackFingerprint.method.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static {}, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->autoCaptionsEnabled()Z
|
|
||||||
move-result v0
|
|
||||||
if-eqz v0, :auto_captions_enabled
|
|
||||||
sget-boolean v0, Lapp/revanced/extension/youtube/patches/DisableAutoCaptionsPatch;->captionsButtonDisabled:Z
|
|
||||||
if-nez v0, :auto_captions_enabled
|
|
||||||
const/4 v0, 0x1
|
|
||||||
return v0
|
|
||||||
:auto_captions_enabled
|
|
||||||
nop
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,13 @@ internal val startVideoInformerFingerprint = fingerprint {
|
|||||||
strings("pc")
|
strings("pc")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val storyboardRendererDecoderRecommendedLevelFingerprint = fingerprint {
|
||||||
|
returns("V")
|
||||||
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
parameters("L")
|
||||||
|
strings("#-1#")
|
||||||
|
}
|
||||||
|
|
||||||
internal val subtitleTrackFingerprint = fingerprint {
|
internal val subtitleTrackFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
returns("Z")
|
returns("Z")
|
||||||
@ -28,6 +35,6 @@ internal val subtitleTrackFingerprint = fingerprint {
|
|||||||
)
|
)
|
||||||
strings("DISABLE_CAPTIONS_OPTION")
|
strings("DISABLE_CAPTIONS_OPTION")
|
||||||
custom { _, classDef ->
|
custom { _, classDef ->
|
||||||
classDef.endsWith("SubtitleTrack;")
|
classDef.endsWith("/SubtitleTrack;")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,8 @@ val customBrandingPatch = resourcePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val appName by stringOption(
|
val appName by stringOption(
|
||||||
|
@ -47,6 +47,7 @@ val changeHeaderPatch = resourcePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
|
"20.12.46",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,7 +28,8 @@ val hideButtonsPatch = resourcePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
@ -45,10 +46,11 @@ val hideButtonsPatch = resourcePatch(
|
|||||||
SwitchPreference("revanced_hide_remix_button"),
|
SwitchPreference("revanced_hide_remix_button"),
|
||||||
SwitchPreference("revanced_hide_download_button"),
|
SwitchPreference("revanced_hide_download_button"),
|
||||||
SwitchPreference("revanced_hide_thanks_button"),
|
SwitchPreference("revanced_hide_thanks_button"),
|
||||||
|
SwitchPreference("revanced_hide_ask_button"),
|
||||||
SwitchPreference("revanced_hide_clip_button"),
|
SwitchPreference("revanced_hide_clip_button"),
|
||||||
SwitchPreference("revanced_hide_playlist_button"),
|
SwitchPreference("revanced_hide_playlist_button"),
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;")
|
addLithoFilter("Lapp/revanced/extension/youtube/patches/components/ButtonsFilter;")
|
||||||
|
@ -18,7 +18,7 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
|||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
import app.revanced.util.insertLiteralOverride
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
@ -46,7 +46,8 @@ val navigationButtonsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
@ -119,17 +120,17 @@ val navigationButtonsPatch = bytecodePatch(
|
|||||||
|
|
||||||
// Force on/off translucent effect on status bar and navigation buttons.
|
// Force on/off translucent effect on status bar and navigation buttons.
|
||||||
if (is_19_25_or_greater) {
|
if (is_19_25_or_greater) {
|
||||||
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||||
TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG,
|
TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
|
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
|
||||||
)
|
)
|
||||||
|
|
||||||
translucentNavigationButtonsFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
translucentNavigationButtonsFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||||
TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG,
|
TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||||
)
|
)
|
||||||
|
|
||||||
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertLiteralOverride(
|
||||||
TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG,
|
TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||||
)
|
)
|
||||||
|
@ -60,7 +60,8 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -39,7 +39,8 @@ val changeFormFactorPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -65,7 +65,8 @@ val hideEndscreenCardsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -37,7 +37,8 @@ val hideEndScreenSuggestedVideoPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -35,7 +35,8 @@ val disableFullscreenAmbientModePatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package app.revanced.patches.youtube.layout.hide.general
|
package app.revanced.patches.youtube.layout.hide.general
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutFingerprint
|
||||||
import app.revanced.util.literal
|
import app.revanced.util.literal
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
@ -16,9 +17,22 @@ internal val hideShowMoreButtonFingerprint = fingerprint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 20.07+
|
* 20.12+
|
||||||
*/
|
*/
|
||||||
internal val parseElementFromBufferFingerprint = fingerprint {
|
internal val parseElementFromBufferFingerprint = fingerprint {
|
||||||
|
parameters("L", "L", "[B", "L", "L")
|
||||||
|
opcodes(
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
)
|
||||||
|
strings("Failed to parse Element") // String is a partial match.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 20.07+
|
||||||
|
*/
|
||||||
|
internal val parseElementFromBufferLegacy2007Fingerprint = fingerprint {
|
||||||
parameters("L", "L", "[B", "L", "L")
|
parameters("L", "L", "[B", "L", "L")
|
||||||
opcodes(
|
opcodes(
|
||||||
Opcode.IGET_OBJECT,
|
Opcode.IGET_OBJECT,
|
||||||
@ -29,7 +43,10 @@ internal val parseElementFromBufferFingerprint = fingerprint {
|
|||||||
strings("Failed to parse Element") // String is a partial match.
|
strings("Failed to parse Element") // String is a partial match.
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val parseElementFromBufferLegacyFingerprint = fingerprint {
|
/**
|
||||||
|
* 19.01 - 20.06
|
||||||
|
*/
|
||||||
|
internal val parseElementFromBufferLegacy1901Fingerprint = fingerprint {
|
||||||
parameters("L", "L", "[B", "L", "L")
|
parameters("L", "L", "[B", "L", "L")
|
||||||
opcodes(
|
opcodes(
|
||||||
Opcode.IGET_OBJECT,
|
Opcode.IGET_OBJECT,
|
||||||
@ -51,6 +68,9 @@ internal val showWatermarkFingerprint = fingerprint {
|
|||||||
parameters("L", "L")
|
parameters("L", "L")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches same method as [wideSearchbarLayoutFingerprint].
|
||||||
|
*/
|
||||||
internal val yoodlesImageViewFingerprint = fingerprint {
|
internal val yoodlesImageViewFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
returns("Landroid/view/View;")
|
returns("Landroid/view/View;")
|
||||||
|
@ -21,6 +21,7 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
|||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_20_09_or_greater
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||||
import app.revanced.util.findFreeRegister
|
import app.revanced.util.findFreeRegister
|
||||||
@ -43,14 +44,12 @@ var crowdfundingBoxId = -1L
|
|||||||
private set
|
private set
|
||||||
var youTubeLogo = -1L
|
var youTubeLogo = -1L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var filterBarHeightId = -1L
|
var filterBarHeightId = -1L
|
||||||
private set
|
private set
|
||||||
var relatedChipCloudMarginId = -1L
|
var relatedChipCloudMarginId = -1L
|
||||||
private set
|
private set
|
||||||
var barContainerHeightId = -1L
|
var barContainerHeightId = -1L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var fabButtonId = -1L
|
var fabButtonId = -1L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@ -130,11 +129,10 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
|||||||
"19.25.37",
|
"19.25.37",
|
||||||
"19.34.42",
|
"19.34.42",
|
||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.45.38",
|
|
||||||
"19.46.42",
|
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
@ -249,8 +247,9 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
|||||||
|
|
||||||
// region Mix playlists
|
// region Mix playlists
|
||||||
|
|
||||||
(if (is_20_07_or_greater) parseElementFromBufferFingerprint
|
(if (is_20_09_or_greater) parseElementFromBufferFingerprint
|
||||||
else parseElementFromBufferLegacyFingerprint).let {
|
else if (is_20_07_or_greater) parseElementFromBufferLegacy2007Fingerprint
|
||||||
|
else parseElementFromBufferLegacy1901Fingerprint).let {
|
||||||
it.method.apply {
|
it.method.apply {
|
||||||
val byteArrayParameter = "p3"
|
val byteArrayParameter = "p3"
|
||||||
val startIndex = it.patternMatch!!.startIndex
|
val startIndex = it.patternMatch!!.startIndex
|
||||||
|
@ -63,7 +63,8 @@ val hideInfoCardsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -30,7 +30,8 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
@ -51,7 +52,10 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
|
|||||||
SwitchPreference("revanced_hide_player_flyout_speed"),
|
SwitchPreference("revanced_hide_player_flyout_speed"),
|
||||||
SwitchPreference("revanced_hide_player_flyout_lock_screen"),
|
SwitchPreference("revanced_hide_player_flyout_lock_screen"),
|
||||||
SwitchPreference("revanced_hide_player_flyout_more_info"),
|
SwitchPreference("revanced_hide_player_flyout_more_info"),
|
||||||
SwitchPreference("revanced_hide_player_flyout_audio_track"),
|
SwitchPreference(
|
||||||
|
key = "revanced_hide_player_flyout_audio_track",
|
||||||
|
tag = "app.revanced.extension.youtube.settings.preference.HideAudioFlyoutMenuPreference"
|
||||||
|
),
|
||||||
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
|
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
|
||||||
SwitchPreference("revanced_hide_player_flyout_sleep_timer"),
|
SwitchPreference("revanced_hide_player_flyout_sleep_timer"),
|
||||||
SwitchPreference("revanced_hide_player_flyout_video_quality_footer"),
|
SwitchPreference("revanced_hide_player_flyout_video_quality_footer"),
|
||||||
|
@ -35,7 +35,8 @@ val disableRollingNumberAnimationPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -178,7 +178,8 @@ val hideShortsComponentsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
hideShortsAppShortcutOption()
|
hideShortsAppShortcutOption()
|
||||||
|
@ -1,24 +1,16 @@
|
|||||||
package app.revanced.patches.youtube.layout.hide.time
|
package app.revanced.patches.youtube.layout.hide.time
|
||||||
|
|
||||||
|
import app.revanced.patcher.fingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
internal val timeCounterFingerprint = fingerprint(
|
internal val timeCounterFingerprint = fingerprint {
|
||||||
fuzzyPatternScanThreshold = 1,
|
|
||||||
) {
|
|
||||||
returns("V")
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||||
|
returns("V")
|
||||||
parameters()
|
parameters()
|
||||||
opcodes(
|
opcodes(
|
||||||
Opcode.SUB_LONG_2ADDR,
|
Opcode.SUB_LONG_2ADDR,
|
||||||
Opcode.IGET_WIDE,
|
Opcode.IGET_WIDE,
|
||||||
Opcode.SUB_LONG_2ADDR,
|
|
||||||
Opcode.IGET_OBJECT,
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IGET_WIDE,
|
|
||||||
Opcode.IGET_WIDE,
|
|
||||||
Opcode.SUB_LONG_2ADDR
|
Opcode.SUB_LONG_2ADDR
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,8 @@ val hideTimestampPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -30,6 +30,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstructio
|
|||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||||
@ -168,14 +169,12 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
// 19.30.39 // Modern 3 is less broken when double tap expand is enabled, but cannot swipe to expand when double tap is off.
|
// 19.30.39 // Modern 3 is less broken when double tap expand is enabled, but cannot swipe to expand when double tap is off.
|
||||||
// 19.31.36 // All Modern 1 buttons are missing. Unusable.
|
// 19.31.36 // All Modern 1 buttons are missing. Unusable.
|
||||||
// 19.32.36 // 19.32+ and beyond all work without issues.
|
// 19.32.36 // 19.32+ and beyond all work without issues.
|
||||||
// 19.33.35
|
|
||||||
"19.34.42",
|
"19.34.42",
|
||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.45.38",
|
|
||||||
"19.46.42",
|
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
@ -281,7 +280,7 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride(
|
||||||
literal: Long,
|
literal: Long,
|
||||||
extensionMethod: String,
|
extensionMethod: String,
|
||||||
) = method.insertFeatureFlagBooleanOverride(
|
) = method.insertLiteralOverride(
|
||||||
literal,
|
literal,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z"
|
"$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z"
|
||||||
)
|
)
|
||||||
@ -349,7 +348,12 @@ val miniplayerPatch = bytecodePatch(
|
|||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region Legacy tablet miniplayer hooks.
|
// region Legacy tablet miniplayer hooks.
|
||||||
val appNameStringIndex = miniplayerOverrideFingerprint.stringMatches!!.first().index + 2
|
val appNameStringIndex = miniplayerOverrideFingerprint.let {
|
||||||
|
it.method.indexOfFirstInstructionOrThrow(it.stringMatches!!.first().index) {
|
||||||
|
val reference = getReference<MethodReference>()
|
||||||
|
reference?.parameterTypes?.firstOrNull() == "Landroid/content/Context;"
|
||||||
|
}
|
||||||
|
}
|
||||||
navigate(miniplayerOverrideFingerprint.originalMethod).to(appNameStringIndex).stop().apply {
|
navigate(miniplayerOverrideFingerprint.originalMethod).to(appNameStringIndex).stop().apply {
|
||||||
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
|
findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) }
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,8 @@ val playerPopupPanelsPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -18,7 +18,8 @@ val playerControlsBackgroundPatch = resourcePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -27,6 +27,7 @@ internal val exitFullscreenPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
|
"20.12.46",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import app.revanced.patches.youtube.layout.shortsplayer.openShortsInRegularPlaye
|
|||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
import app.revanced.util.insertLiteralOverride
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
||||||
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;"
|
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;"
|
||||||
@ -24,7 +24,7 @@ internal val openVideosFullscreenHookPatch = bytecodePatch {
|
|||||||
return@execute
|
return@execute
|
||||||
}
|
}
|
||||||
|
|
||||||
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
|
openVideosFullscreenPortraitFingerprint.method.insertLiteralOverride(
|
||||||
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
|
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
|
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
|
||||||
)
|
)
|
||||||
|
@ -25,9 +25,9 @@ val openVideosFullscreenPatch = bytecodePatch(
|
|||||||
|
|
||||||
compatibleWith(
|
compatibleWith(
|
||||||
"com.google.android.youtube"(
|
"com.google.android.youtube"(
|
||||||
"19.46.42",
|
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
|
"20.12.46",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,7 +58,8 @@ val customPlayerOverlayOpacityPatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package app.revanced.patches.youtube.layout.returnyoutubedislike
|
package app.revanced.patches.youtube.layout.returnyoutubedislike
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
import app.revanced.util.literal
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
@ -121,3 +122,12 @@ internal val textComponentLookupFingerprint = fingerprint {
|
|||||||
parameters("L")
|
parameters("L")
|
||||||
strings("…")
|
strings("…")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal const val LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG = 45675738L
|
||||||
|
|
||||||
|
internal val textComponentFeatureFlagFingerprint = fingerprint {
|
||||||
|
accessFlags(AccessFlags.FINAL)
|
||||||
|
returns("Z")
|
||||||
|
parameters()
|
||||||
|
literal { LITHO_NEW_TEXT_COMPONENT_FEATURE_FLAG }
|
||||||
|
}
|
||||||
|
@ -12,6 +12,7 @@ import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
|||||||
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
|
||||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_19_33_or_greater
|
||||||
|
import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater
|
import app.revanced.patches.youtube.misc.playservice.is_20_10_or_greater
|
||||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.addSettingPreference
|
import app.revanced.patches.youtube.misc.settings.addSettingPreference
|
||||||
@ -59,7 +60,8 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
|||||||
"19.43.41",
|
"19.43.41",
|
||||||
"19.47.53",
|
"19.47.53",
|
||||||
"20.07.39",
|
"20.07.39",
|
||||||
),
|
"20.12.46",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
@ -174,6 +176,14 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
|||||||
// Filter that parses the video id from the UI
|
// Filter that parses the video id from the UI
|
||||||
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
|
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
|
||||||
|
|
||||||
|
if (is_20_07_or_greater) {
|
||||||
|
// Turn off a/b flag that enables new code for creating litho spans.
|
||||||
|
// If enabled then the litho text span hook is never called.
|
||||||
|
// Target code is very obfuscated and exactly what the code does is not clear.
|
||||||
|
// Return late so debug patch logs if the flag is enabled.
|
||||||
|
textComponentFeatureFlagFingerprint.method.returnLate(false)
|
||||||
|
}
|
||||||
|
|
||||||
// Player response video id is needed to search for the video ids in Shorts litho components.
|
// Player response video id is needed to search for the video ids in Shorts litho components.
|
||||||
hookPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
|
hookPlayerResponseVideoId("$FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user