mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-26 10:54:07 +01:00
Compare commits
76 Commits
v5.42.0-de
...
v5.45.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12e7c0943a | ||
|
|
a0c5604951 | ||
|
|
38d9299dfe | ||
|
|
dfdbbfa047 | ||
|
|
e9f45ce926 | ||
|
|
1b38b1a3c8 | ||
|
|
13cf1724bf | ||
|
|
6d01863ec7 | ||
|
|
a32ed30b4c | ||
|
|
ef44eaa119 | ||
|
|
c73a03c9e1 | ||
|
|
c1681f982a | ||
|
|
1502bf7524 | ||
|
|
dffb1e6525 | ||
|
|
b8c2ede2bf | ||
|
|
591e106098 | ||
|
|
e7336d2ef3 | ||
|
|
7a53f8f62d | ||
|
|
3466d9d210 | ||
|
|
876d7a6b03 | ||
|
|
a2aa9cac27 | ||
|
|
08baa19b4a | ||
|
|
7283b93cea | ||
|
|
754b71959a | ||
|
|
c64e29ec57 | ||
|
|
b2dd008aee | ||
|
|
de97562c5d | ||
|
|
6373829fd6 | ||
|
|
cfd244b408 | ||
|
|
e8e28e2b6a | ||
|
|
2e4c6fdcad | ||
|
|
644d6dcb51 | ||
|
|
14dd7346a8 | ||
|
|
0af8c8a766 | ||
|
|
96454c843b | ||
|
|
476ef0fae1 | ||
|
|
bbec724afb | ||
|
|
7a1dcbd4ee | ||
|
|
2a1e31860f | ||
|
|
949d6bdd19 | ||
|
|
7563990750 | ||
|
|
4b605eb270 | ||
|
|
c2d7a7fb8b | ||
|
|
a55560dc25 | ||
|
|
e8522d703e | ||
|
|
068d029a03 | ||
|
|
0c19dbaf30 | ||
|
|
bf73ac8316 | ||
|
|
95eee59a87 | ||
|
|
566875ea53 | ||
|
|
10ea250d4a | ||
|
|
5bd0f11630 | ||
|
|
4547ecb73c | ||
|
|
50f0b9c5ee | ||
|
|
a8c4bdb8a6 | ||
|
|
6555f6e6f8 | ||
|
|
a0e2c5c7b9 | ||
|
|
54846253d7 | ||
|
|
a98e8f7370 | ||
|
|
2d928e0cd6 | ||
|
|
be2b144cc9 | ||
|
|
52c0bb6aa2 | ||
|
|
38a49cc2a1 | ||
|
|
91044b3a50 | ||
|
|
fd4b2e1bb9 | ||
|
|
d0f20c8c7f | ||
|
|
d65dbc749c | ||
|
|
143dcef2b8 | ||
|
|
dbfc5be464 | ||
|
|
0fe545cad6 | ||
|
|
feca17be68 | ||
|
|
7afeaebb5c | ||
|
|
60a581a632 | ||
|
|
104d096ada | ||
|
|
19dcbd8efb | ||
|
|
a50f3b5177 |
284
CHANGELOG.md
284
CHANGELOG.md
@@ -1,3 +1,287 @@
|
||||
# [5.45.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.45.0-dev.3...v5.45.0-dev.4) (2025-10-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Change header:** Do not mirror header graphic with RTL languages ([a0c5604](https://github.com/ReVanced/revanced-patches/commit/a0c56049510ce040e1ccd49257864672c343344d))
|
||||
|
||||
# [5.45.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.45.0-dev.2...v5.45.0-dev.3) (2025-10-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Change Header:** Use SVG for header logo ([#6178](https://github.com/ReVanced/revanced-patches/issues/6178)) ([e9f45ce](https://github.com/ReVanced/revanced-patches/commit/e9f45ce92695d5857473ff71c14b190bded28a73))
|
||||
|
||||
# [5.45.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.45.0-dev.1...v5.45.0-dev.2) (2025-10-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Force original audio:** Fall back to visionOS and not Android Studio if Android VR is not available ([6d01863](https://github.com/ReVanced/revanced-patches/commit/6d01863ec70617d9abc864ce6686ed9764dd151d))
|
||||
* **YouTube Music - Hide category bar:** Correctly hide the category bar in newer app targets ([#6175](https://github.com/ReVanced/revanced-patches/issues/6175)) ([13cf172](https://github.com/ReVanced/revanced-patches/commit/13cf1724bf2f946c7129cab0db96721c90f9fe89))
|
||||
|
||||
# [5.45.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.44.0...v5.45.0-dev.1) (2025-10-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Instagram:** Update failing fingerprints on newer versions ([#6181](https://github.com/ReVanced/revanced-patches/issues/6181)) ([c73a03c](https://github.com/ReVanced/revanced-patches/commit/c73a03c9e18a12262939c974cdf16221221d1487))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **TikTok:** Add `Sanitize sharing links` patch ([#6176](https://github.com/ReVanced/revanced-patches/issues/6176)) ([ef44eaa](https://github.com/ReVanced/revanced-patches/commit/ef44eaa119b9d6c5faec051e22d20f883d0da4f1))
|
||||
|
||||
# [5.44.0](https://github.com/ReVanced/revanced-patches/compare/v5.43.1...v5.44.0) (2025-10-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos - Spoof features:** Add support for Pixel 10 devices ([#6161](https://github.com/ReVanced/revanced-patches/issues/6161)) ([754b719](https://github.com/ReVanced/revanced-patches/commit/754b71959a0155413eb33cf1bdc2c8976eaca634))
|
||||
* **X / Twitter - Change link sharing domain:** Use bytecode patching to resolve patching with Manager ([#6125](https://github.com/ReVanced/revanced-patches/issues/6125)) ([0af8c8a](https://github.com/ReVanced/revanced-patches/commit/0af8c8a766ae4ba6926404d59da2f14d649f91f7))
|
||||
* **YouTube - Hide layout components:** Hide new kind of community post ([#6146](https://github.com/ReVanced/revanced-patches/issues/6146)) ([cfd244b](https://github.com/ReVanced/revanced-patches/commit/cfd244b4088daacd2788ec38357ac521e4b296d5))
|
||||
* **YouTube Music:** Resolve patching 7.29 target ([2e4c6fd](https://github.com/ReVanced/revanced-patches/commit/2e4c6fdcadeef45a80733e374421d52e5e8af910))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Custom network security` patch ([#6151](https://github.com/ReVanced/revanced-patches/issues/6151)) ([e7336d2](https://github.com/ReVanced/revanced-patches/commit/e7336d2ef361cc5d6fe6e8442b36d9cf1f542931))
|
||||
* **Duolingo - Enable debug menu:** Support latest app target ([#6163](https://github.com/ReVanced/revanced-patches/issues/6163)) ([08baa19](https://github.com/ReVanced/revanced-patches/commit/08baa19b4a62e62bd103d177c3f4454de199cf16))
|
||||
* **Duolingo:** Add `Skip energy recharge ads` patch ([#6167](https://github.com/ReVanced/revanced-patches/issues/6167)) ([591e106](https://github.com/ReVanced/revanced-patches/commit/591e106098c6eff431b8b7ac7d985ce7373d701e))
|
||||
* **Samsung Radio:** Add `Disable device checks` patch ([#6145](https://github.com/ReVanced/revanced-patches/issues/6145)) ([de97562](https://github.com/ReVanced/revanced-patches/commit/de97562c5ddc8ec707761c1e04e74c4e18f9c158))
|
||||
|
||||
# [5.44.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.44.0-dev.3...v5.44.0-dev.4) (2025-10-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Custom network security` patch ([#6151](https://github.com/ReVanced/revanced-patches/issues/6151)) ([e7336d2](https://github.com/ReVanced/revanced-patches/commit/e7336d2ef361cc5d6fe6e8442b36d9cf1f542931))
|
||||
* **Duolingo:** Add `Skip energy recharge ads` patch ([#6167](https://github.com/ReVanced/revanced-patches/issues/6167)) ([591e106](https://github.com/ReVanced/revanced-patches/commit/591e106098c6eff431b8b7ac7d985ce7373d701e))
|
||||
|
||||
# [5.44.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.44.0-dev.2...v5.44.0-dev.3) (2025-10-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Duolingo - Enable debug menu:** Support latest app target ([#6163](https://github.com/ReVanced/revanced-patches/issues/6163)) ([08baa19](https://github.com/ReVanced/revanced-patches/commit/08baa19b4a62e62bd103d177c3f4454de199cf16))
|
||||
|
||||
# [5.44.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.44.0-dev.1...v5.44.0-dev.2) (2025-10-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Google Photos - Spoof features:** Add support for Pixel 10 devices ([#6161](https://github.com/ReVanced/revanced-patches/issues/6161)) ([754b719](https://github.com/ReVanced/revanced-patches/commit/754b71959a0155413eb33cf1bdc2c8976eaca634))
|
||||
|
||||
# [5.44.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.43.2-dev.3...v5.44.0-dev.1) (2025-10-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Samsung Radio:** Add `Disable device checks` patch ([#6145](https://github.com/ReVanced/revanced-patches/issues/6145)) ([de97562](https://github.com/ReVanced/revanced-patches/commit/de97562c5ddc8ec707761c1e04e74c4e18f9c158))
|
||||
|
||||
## [5.43.2-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.43.2-dev.2...v5.43.2-dev.3) (2025-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Hide new kind of community post ([#6146](https://github.com/ReVanced/revanced-patches/issues/6146)) ([cfd244b](https://github.com/ReVanced/revanced-patches/commit/cfd244b4088daacd2788ec38357ac521e4b296d5))
|
||||
|
||||
## [5.43.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.43.2-dev.1...v5.43.2-dev.2) (2025-10-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube Music:** Resolve patching 7.29 target ([2e4c6fd](https://github.com/ReVanced/revanced-patches/commit/2e4c6fdcadeef45a80733e374421d52e5e8af910))
|
||||
|
||||
## [5.43.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.43.1...v5.43.2-dev.1) (2025-10-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **X / Twitter - Change link sharing domain:** Use bytecode patching to resolve patching with Manager ([#6125](https://github.com/ReVanced/revanced-patches/issues/6125)) ([0af8c8a](https://github.com/ReVanced/revanced-patches/commit/0af8c8a766ae4ba6926404d59da2f14d649f91f7))
|
||||
|
||||
## [5.43.1](https://github.com/ReVanced/revanced-patches/compare/v5.43.0...v5.43.1) (2025-10-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **X / Twitter - Change link sharing domain:** Resolve duplicate patch option ([#6119](https://github.com/ReVanced/revanced-patches/issues/6119)) ([7563990](https://github.com/ReVanced/revanced-patches/commit/75639907502382f63fa127a886362d4a4573e6e3))
|
||||
* **X / Twitter:** Do not crash Manager when clicking on domain patch option ([2a1e318](https://github.com/ReVanced/revanced-patches/commit/2a1e31860f22f537d51b40a5b71d9ad9d538789e))
|
||||
|
||||
## [5.43.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.43.1-dev.1...v5.43.1-dev.2) (2025-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **X / Twitter:** Do not crash Manager when clicking on domain patch option ([2a1e318](https://github.com/ReVanced/revanced-patches/commit/2a1e31860f22f537d51b40a5b71d9ad9d538789e))
|
||||
|
||||
## [5.43.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.43.0...v5.43.1-dev.1) (2025-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **X / Twitter - Change link sharing domain:** Resolve duplicate patch option ([#6119](https://github.com/ReVanced/revanced-patches/issues/6119)) ([7563990](https://github.com/ReVanced/revanced-patches/commit/75639907502382f63fa127a886362d4a4573e6e3))
|
||||
|
||||
# [5.43.0](https://github.com/ReVanced/revanced-patches/compare/v5.42.1...v5.43.0) (2025-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Custom branding:** Use white notification icon for expanded status bar panel ([95eee59](https://github.com/ReVanced/revanced-patches/commit/95eee59a87a680e212a3ba06e1afefee8d91ee9d))
|
||||
* **Instagram - Change sharing domain:** Display patch option ([#6089](https://github.com/ReVanced/revanced-patches/issues/6089)) ([be2b144](https://github.com/ReVanced/revanced-patches/commit/be2b144cc9c4108ec37e16f3dd20573d88ffaa2b))
|
||||
* **X / Twitter - Change Link Sharing Domain:** Change link domain of share copy action ([#6091](https://github.com/ReVanced/revanced-patches/issues/6091)) ([5484625](https://github.com/ReVanced/revanced-patches/commit/54846253d748f4e7e30b2bba427c7d2fb9c341e2))
|
||||
* **YouTube - Custom branding:** Do not add a broken custom icon if the user provides an invalid custom icon path ([6555f6e](https://github.com/ReVanced/revanced-patches/commit/6555f6e6f8b52c2f1ddab1f52c6704cd2d8cfc12))
|
||||
* **YouTube - Custom branding:** Use ReVanced icon for status bar notification icon ([#6108](https://github.com/ReVanced/revanced-patches/issues/6108)) ([10ea250](https://github.com/ReVanced/revanced-patches/commit/10ea250d4a91f8ab3b7f865612a403fc93a857b5))
|
||||
* **YouTube - Force original audio:** Do not use translated audio if stream spoofing is off and force audio is on ([0c19dba](https://github.com/ReVanced/revanced-patches/commit/0c19dbaf30bcb95a29448d98b028ebeea54cc7d3))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Instagram:** Add `Hide suggested content` patch ([#6075](https://github.com/ReVanced/revanced-patches/issues/6075)) ([50f0b9c](https://github.com/ReVanced/revanced-patches/commit/50f0b9c5eee95ff5f9974e344802e1d2a4aab47b))
|
||||
|
||||
# [5.43.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.43.0-dev.3...v5.43.0-dev.4) (2025-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Force original audio:** Do not use translated audio if stream spoofing is off and force audio is on ([0c19dba](https://github.com/ReVanced/revanced-patches/commit/0c19dbaf30bcb95a29448d98b028ebeea54cc7d3))
|
||||
|
||||
# [5.43.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.43.0-dev.2...v5.43.0-dev.3) (2025-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Custom branding:** Use white notification icon for expanded status bar panel ([95eee59](https://github.com/ReVanced/revanced-patches/commit/95eee59a87a680e212a3ba06e1afefee8d91ee9d))
|
||||
|
||||
# [5.43.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.43.0-dev.1...v5.43.0-dev.2) (2025-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Custom branding:** Use ReVanced icon for status bar notification icon ([#6108](https://github.com/ReVanced/revanced-patches/issues/6108)) ([10ea250](https://github.com/ReVanced/revanced-patches/commit/10ea250d4a91f8ab3b7f865612a403fc93a857b5))
|
||||
|
||||
# [5.43.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.42.2-dev.3...v5.43.0-dev.1) (2025-10-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Instagram:** Add `Hide suggested content` patch ([#6075](https://github.com/ReVanced/revanced-patches/issues/6075)) ([50f0b9c](https://github.com/ReVanced/revanced-patches/commit/50f0b9c5eee95ff5f9974e344802e1d2a4aab47b))
|
||||
|
||||
## [5.42.2-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.42.2-dev.2...v5.42.2-dev.3) (2025-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Custom branding:** Do not add a broken custom icon if the user provides an invalid custom icon path ([6555f6e](https://github.com/ReVanced/revanced-patches/commit/6555f6e6f8b52c2f1ddab1f52c6704cd2d8cfc12))
|
||||
|
||||
## [5.42.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.42.2-dev.1...v5.42.2-dev.2) (2025-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **X / Twitter - Change Link Sharing Domain:** Change link domain of share copy action ([#6091](https://github.com/ReVanced/revanced-patches/issues/6091)) ([5484625](https://github.com/ReVanced/revanced-patches/commit/54846253d748f4e7e30b2bba427c7d2fb9c341e2))
|
||||
|
||||
## [5.42.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.42.1...v5.42.2-dev.1) (2025-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Instagram - Change sharing domain:** Display patch option ([#6089](https://github.com/ReVanced/revanced-patches/issues/6089)) ([be2b144](https://github.com/ReVanced/revanced-patches/commit/be2b144cc9c4108ec37e16f3dd20573d88ffaa2b))
|
||||
|
||||
## [5.42.1](https://github.com/ReVanced/revanced-patches/compare/v5.42.0...v5.42.1) (2025-10-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Custom Branding:** Resolve startup crash with root installation ([fd4b2e1](https://github.com/ReVanced/revanced-patches/commit/fd4b2e1bb98c6e507178e5b46b896ef7d320bc3d))
|
||||
|
||||
## [5.42.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.42.0...v5.42.1-dev.1) (2025-10-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Custom Branding:** Resolve startup crash with root installation ([fd4b2e1](https://github.com/ReVanced/revanced-patches/commit/fd4b2e1bb98c6e507178e5b46b896ef7d320bc3d))
|
||||
|
||||
# [5.42.0](https://github.com/ReVanced/revanced-patches/compare/v5.41.0...v5.42.0) (2025-10-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Custom branding:** Update ReVanced logo ([#6049](https://github.com/ReVanced/revanced-patches/issues/6049)) ([9441e7a](https://github.com/ReVanced/revanced-patches/commit/9441e7acb4817e12d1443d438ef6c448518bd614))
|
||||
* **Custom branding:** Update ReVanced logo sizing ([#6029](https://github.com/ReVanced/revanced-patches/issues/6029)) ([ae4b947](https://github.com/ReVanced/revanced-patches/commit/ae4b9474d3fb62528fc21397c19954d31605e9da))
|
||||
* **Instagram - Hide navigation buttons:** Resolve app startup crash ([080a226](https://github.com/ReVanced/revanced-patches/commit/080a2266146798be71789c939deef2f289697523))
|
||||
* **Spotify:** Change `Hide Create button` patch to default off ([#6067](https://github.com/ReVanced/revanced-patches/issues/6067)) ([19949e1](https://github.com/ReVanced/revanced-patches/commit/19949e1695cc252ff0f94a33b6e3fb62e967d7fd))
|
||||
* **X / Twitter:** Remove non functional and obsolete patch `Open links with app chooser` ([#6033](https://github.com/ReVanced/revanced-patches/issues/6033)) ([673609c](https://github.com/ReVanced/revanced-patches/commit/673609c2aa87988cdc138eab101b9750fe6a7b62))
|
||||
* **YouTube - Force original audio:** Change patch to default on ([#6070](https://github.com/ReVanced/revanced-patches/issues/6070)) ([bd4ba2d](https://github.com/ReVanced/revanced-patches/commit/bd4ba2dae85ee6fd8d7e6078c3de775ca336e0b6))
|
||||
* **YouTube - Force original language:** Resolve some videos using Swedish audio track ([9d67316](https://github.com/ReVanced/revanced-patches/commit/9d6731660ba0e19b863d05d54aa04f74a879f69b))
|
||||
* **YouTube - Hide end screen cards:** Hide new type of end screen card ([#6027](https://github.com/ReVanced/revanced-patches/issues/6027)) ([76b0364](https://github.com/ReVanced/revanced-patches/commit/76b0364c5b5562c6a0d178d2bbe5b220f48aaca9))
|
||||
* **YouTube - Spoof video streams:** Add "Allow Android VR AV1" setting ([#6071](https://github.com/ReVanced/revanced-patches/issues/6071)) ([f03256c](https://github.com/ReVanced/revanced-patches/commit/f03256c471e1ee6a12267c1b56b531ca8f89278c))
|
||||
* **YouTube - Spoof video streams:** Do not allow VR AV1 if "Force AVC" is enabled ([7afeaeb](https://github.com/ReVanced/revanced-patches/commit/7afeaebb5cc22eb4f4512d8aa0cf4e835e7a2daf))
|
||||
* **YouTube - Spoof video streams:** Resolve playback dropping frames ([#6051](https://github.com/ReVanced/revanced-patches/issues/6051)) ([a62ee43](https://github.com/ReVanced/revanced-patches/commit/a62ee43441b197f5c8352ae373bb8919ad66f0bd))
|
||||
* **YouTube Music - GmsCore support:** Handle sharing links to certain apps such as Instagram ([#6026](https://github.com/ReVanced/revanced-patches/issues/6026)) ([328234f](https://github.com/ReVanced/revanced-patches/commit/328234f39ada81542e596f04e8ce410c787c15c8))
|
||||
* **YouTube Music - Hide cast button:** Fix patching error ([28799a5](https://github.com/ReVanced/revanced-patches/commit/28799a548a73651134ef304cb6cb542cf8e55abe))
|
||||
* **YouTube Music - Hide cast button:** Resolve button not hiding ([7817885](https://github.com/ReVanced/revanced-patches/commit/7817885cffed66608039ab45881537cbd3069c9d))
|
||||
* **YouTube:** Resolve UI components not hiding for some users ([#6054](https://github.com/ReVanced/revanced-patches/issues/6054)) ([6b26346](https://github.com/ReVanced/revanced-patches/commit/6b2634691423f5ce25a28b3f2fbc420977b81748))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Custom branding:** Add in-app settings to change icon and name ([#6059](https://github.com/ReVanced/revanced-patches/issues/6059)) ([a50f3b5](https://github.com/ReVanced/revanced-patches/commit/a50f3b5177808f07d84041c946caccb5a08ad387))
|
||||
* **Instagram:** Add `Custom share domain` patch ([#5998](https://github.com/ReVanced/revanced-patches/issues/5998)) ([20c4131](https://github.com/ReVanced/revanced-patches/commit/20c413120bad97af6121718e76b22a1b5540aa44))
|
||||
* **Instagram:** Add `Enable developer menu` patch ([#6043](https://github.com/ReVanced/revanced-patches/issues/6043)) ([2154d89](https://github.com/ReVanced/revanced-patches/commit/2154d89242fd8d7f7460145d5d35a4f1986944a3))
|
||||
* **Instagram:** Add `Open links externally` patch ([#6012](https://github.com/ReVanced/revanced-patches/issues/6012)) ([08e8ead](https://github.com/ReVanced/revanced-patches/commit/08e8ead04ffff47a4608a3db7aadc8d5feccd4ad))
|
||||
* **Instagram:** Add `Sanitize sharing links` patch ([#5986](https://github.com/ReVanced/revanced-patches/issues/5986)) ([963a4ef](https://github.com/ReVanced/revanced-patches/commit/963a4ef43fd513de7a2d7d019992f06b62fdcc10))
|
||||
* **Viber:** Add `Hide navigation buttons` patch ([#5991](https://github.com/ReVanced/revanced-patches/issues/5991)) ([5cb46c4](https://github.com/ReVanced/revanced-patches/commit/5cb46c4e9180ebc16eddb983dad73d137d8ec047))
|
||||
* **YouTube Music:** Add `Custom branding` patch ([#6007](https://github.com/ReVanced/revanced-patches/issues/6007)) ([4c8b56f](https://github.com/ReVanced/revanced-patches/commit/4c8b56f5466b244737f501654eb7c5d34b6b2f88))
|
||||
* **YouTube Music:** Add `Force original audio` patch ([#6036](https://github.com/ReVanced/revanced-patches/issues/6036)) ([d0d53d1](https://github.com/ReVanced/revanced-patches/commit/d0d53d109e451759a029326873adfa36fba12b23))
|
||||
|
||||
# [5.42.0](https://github.com/ReVanced/revanced-patches/compare/v5.41.0...v5.42.0) (2025-10-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Custom branding:** Update ReVanced logo ([#6049](https://github.com/ReVanced/revanced-patches/issues/6049)) ([9441e7a](https://github.com/ReVanced/revanced-patches/commit/9441e7acb4817e12d1443d438ef6c448518bd614))
|
||||
* **Custom branding:** Update ReVanced logo sizing ([#6029](https://github.com/ReVanced/revanced-patches/issues/6029)) ([ae4b947](https://github.com/ReVanced/revanced-patches/commit/ae4b9474d3fb62528fc21397c19954d31605e9da))
|
||||
* **Instagram - Hide navigation buttons:** Resolve app startup crash ([080a226](https://github.com/ReVanced/revanced-patches/commit/080a2266146798be71789c939deef2f289697523))
|
||||
* **Spotify:** Change `Hide Create button` patch to default off ([#6067](https://github.com/ReVanced/revanced-patches/issues/6067)) ([19949e1](https://github.com/ReVanced/revanced-patches/commit/19949e1695cc252ff0f94a33b6e3fb62e967d7fd))
|
||||
* **X / Twitter:** Remove non functional and obsolete patch `Open links with app chooser` ([#6033](https://github.com/ReVanced/revanced-patches/issues/6033)) ([673609c](https://github.com/ReVanced/revanced-patches/commit/673609c2aa87988cdc138eab101b9750fe6a7b62))
|
||||
* **YouTube - Force original audio:** Change patch to default on ([#6070](https://github.com/ReVanced/revanced-patches/issues/6070)) ([bd4ba2d](https://github.com/ReVanced/revanced-patches/commit/bd4ba2dae85ee6fd8d7e6078c3de775ca336e0b6))
|
||||
* **YouTube - Force original language:** Resolve some videos using Swedish audio track ([9d67316](https://github.com/ReVanced/revanced-patches/commit/9d6731660ba0e19b863d05d54aa04f74a879f69b))
|
||||
* **YouTube - Hide end screen cards:** Hide new type of end screen card ([#6027](https://github.com/ReVanced/revanced-patches/issues/6027)) ([76b0364](https://github.com/ReVanced/revanced-patches/commit/76b0364c5b5562c6a0d178d2bbe5b220f48aaca9))
|
||||
* **YouTube - Spoof video streams:** Add "Allow Android VR AV1" setting ([#6071](https://github.com/ReVanced/revanced-patches/issues/6071)) ([f03256c](https://github.com/ReVanced/revanced-patches/commit/f03256c471e1ee6a12267c1b56b531ca8f89278c))
|
||||
* **YouTube - Spoof video streams:** Do not allow VR AV1 if "Force AVC" is enabled ([7afeaeb](https://github.com/ReVanced/revanced-patches/commit/7afeaebb5cc22eb4f4512d8aa0cf4e835e7a2daf))
|
||||
* **YouTube - Spoof video streams:** Resolve playback dropping frames ([#6051](https://github.com/ReVanced/revanced-patches/issues/6051)) ([a62ee43](https://github.com/ReVanced/revanced-patches/commit/a62ee43441b197f5c8352ae373bb8919ad66f0bd))
|
||||
* **YouTube Music - GmsCore support:** Handle sharing links to certain apps such as Instagram ([#6026](https://github.com/ReVanced/revanced-patches/issues/6026)) ([328234f](https://github.com/ReVanced/revanced-patches/commit/328234f39ada81542e596f04e8ce410c787c15c8))
|
||||
* **YouTube Music - Hide cast button:** Fix patching error ([28799a5](https://github.com/ReVanced/revanced-patches/commit/28799a548a73651134ef304cb6cb542cf8e55abe))
|
||||
* **YouTube Music - Hide cast button:** Resolve button not hiding ([7817885](https://github.com/ReVanced/revanced-patches/commit/7817885cffed66608039ab45881537cbd3069c9d))
|
||||
* **YouTube:** Resolve UI components not hiding for some users ([#6054](https://github.com/ReVanced/revanced-patches/issues/6054)) ([6b26346](https://github.com/ReVanced/revanced-patches/commit/6b2634691423f5ce25a28b3f2fbc420977b81748))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Custom branding:** Add in-app settings to change icon and name ([#6059](https://github.com/ReVanced/revanced-patches/issues/6059)) ([a50f3b5](https://github.com/ReVanced/revanced-patches/commit/a50f3b5177808f07d84041c946caccb5a08ad387))
|
||||
* **Instagram:** Add `Custom share domain` patch ([#5998](https://github.com/ReVanced/revanced-patches/issues/5998)) ([20c4131](https://github.com/ReVanced/revanced-patches/commit/20c413120bad97af6121718e76b22a1b5540aa44))
|
||||
* **Instagram:** Add `Enable developer menu` patch ([#6043](https://github.com/ReVanced/revanced-patches/issues/6043)) ([2154d89](https://github.com/ReVanced/revanced-patches/commit/2154d89242fd8d7f7460145d5d35a4f1986944a3))
|
||||
* **Instagram:** Add `Open links externally` patch ([#6012](https://github.com/ReVanced/revanced-patches/issues/6012)) ([08e8ead](https://github.com/ReVanced/revanced-patches/commit/08e8ead04ffff47a4608a3db7aadc8d5feccd4ad))
|
||||
* **Instagram:** Add `Sanitize sharing links` patch ([#5986](https://github.com/ReVanced/revanced-patches/issues/5986)) ([963a4ef](https://github.com/ReVanced/revanced-patches/commit/963a4ef43fd513de7a2d7d019992f06b62fdcc10))
|
||||
* **Viber:** Add `Hide navigation buttons` patch ([#5991](https://github.com/ReVanced/revanced-patches/issues/5991)) ([5cb46c4](https://github.com/ReVanced/revanced-patches/commit/5cb46c4e9180ebc16eddb983dad73d137d8ec047))
|
||||
* **YouTube Music:** Add `Custom branding` patch ([#6007](https://github.com/ReVanced/revanced-patches/issues/6007)) ([4c8b56f](https://github.com/ReVanced/revanced-patches/commit/4c8b56f5466b244737f501654eb7c5d34b6b2f88))
|
||||
* **YouTube Music:** Add `Force original audio` patch ([#6036](https://github.com/ReVanced/revanced-patches/issues/6036)) ([d0d53d1](https://github.com/ReVanced/revanced-patches/commit/d0d53d109e451759a029326873adfa36fba12b23))
|
||||
|
||||
# [5.42.0-dev.19](https://github.com/ReVanced/revanced-patches/compare/v5.42.0-dev.18...v5.42.0-dev.19) (2025-10-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Do not allow VR AV1 if "Force AVC" is enabled ([7afeaeb](https://github.com/ReVanced/revanced-patches/commit/7afeaebb5cc22eb4f4512d8aa0cf4e835e7a2daf))
|
||||
|
||||
# [5.42.0-dev.18](https://github.com/ReVanced/revanced-patches/compare/v5.42.0-dev.17...v5.42.0-dev.18) (2025-10-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Custom branding:** Add in-app settings to change icon and name ([#6059](https://github.com/ReVanced/revanced-patches/issues/6059)) ([a50f3b5](https://github.com/ReVanced/revanced-patches/commit/a50f3b5177808f07d84041c946caccb5a08ad387))
|
||||
|
||||
# [5.42.0-dev.17](https://github.com/ReVanced/revanced-patches/compare/v5.42.0-dev.16...v5.42.0-dev.17) (2025-10-07)
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,6 @@ public class HideCastButtonPatch {
|
||||
* Injection point
|
||||
*/
|
||||
public static void hideCastButton(View view) {
|
||||
hideViewBy0dpUnderCondition(Settings.HIDE_CAST_BUTTON.get(), view);
|
||||
hideViewBy0dpUnderCondition(Settings.HIDE_CAST_BUTTON, view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package app.revanced.extension.music.patches;
|
||||
|
||||
import static app.revanced.extension.shared.Utils.hideViewBy0dpUnderCondition;
|
||||
|
||||
import android.view.View;
|
||||
import app.revanced.extension.music.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -8,7 +11,7 @@ public class HideCategoryBarPatch {
|
||||
/**
|
||||
* Injection point
|
||||
*/
|
||||
public static boolean hideCategoryBar() {
|
||||
return Settings.HIDE_CATEGORY_BAR.get();
|
||||
public static void hideCategoryBar(View view) {
|
||||
hideViewBy0dpUnderCondition(Settings.HIDE_CATEGORY_BAR, view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import android.preference.PreferenceScreen;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import app.revanced.extension.music.settings.MusicActivityHook;
|
||||
import app.revanced.extension.shared.GmsCoreSupport;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment;
|
||||
|
||||
/**
|
||||
@@ -30,6 +32,17 @@ public class MusicPreferenceFragment extends ToolbarPreferenceFragment {
|
||||
preferenceScreen = getPreferenceScreen();
|
||||
Utils.sortPreferenceGroups(preferenceScreen);
|
||||
setPreferenceScreenToolbar(preferenceScreen);
|
||||
|
||||
// Clunky work around until preferences are custom classes that manage themselves.
|
||||
// Custom branding only works with non-root install. But the preferences must be
|
||||
// added during patched because of difficulties detecting during patching if it's
|
||||
// a root install. So instead the non-functional preferences are removed during
|
||||
// runtime if the app is mount (root) installation.
|
||||
if (GmsCoreSupport.isPackageNameOriginal()) {
|
||||
removePreferences(
|
||||
BaseSettings.CUSTOM_BRANDING_ICON.key,
|
||||
BaseSettings.CUSTOM_BRANDING_NAME.key);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "initialize failure", ex);
|
||||
}
|
||||
|
||||
4
extensions/samsung/radio/build.gradle.kts
Normal file
4
extensions/samsung/radio/build.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
compileOnly(project(":extensions:shared:library"))
|
||||
compileOnly(project(":extensions:samsung:radio:stub"))
|
||||
}
|
||||
1
extensions/samsung/radio/src/main/AndroidManifest.xml
Normal file
1
extensions/samsung/radio/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest/>
|
||||
@@ -0,0 +1,24 @@
|
||||
package app.revanced.extension.samsung.radio.misc.fix.crash;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class FixCrashPatch {
|
||||
/**
|
||||
* Injection point.
|
||||
* <p>
|
||||
* Add the required permissions to the request list to avoid crashes on API 34+.
|
||||
**/
|
||||
public static final String[] fixPermissionRequestList(String[] perms) {
|
||||
List<String> permsList = new ArrayList<>(Arrays.asList(perms));
|
||||
if (permsList.contains("android.permission.POST_NOTIFICATIONS")) {
|
||||
permsList.addAll(Arrays.asList("android.permission.RECORD_AUDIO", "android.permission.READ_PHONE_STATE", "android.permission.FOREGROUND_SERVICE_MICROPHONE"));
|
||||
}
|
||||
if (permsList.contains("android.permission.RECORD_AUDIO")) {
|
||||
permsList.add("android.permission.FOREGROUND_SERVICE_MICROPHONE");
|
||||
}
|
||||
return permsList.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package app.revanced.extension.samsung.radio.restrictions.device;
|
||||
|
||||
import android.os.SemSystemProperties;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class BypassDeviceChecksPatch {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* <p>
|
||||
* Check if the device has the required hardware
|
||||
**/
|
||||
public static final boolean checkIfDeviceIsIncompatible(String[] deviceList) {
|
||||
String currentDevice = SemSystemProperties.getSalesCode();
|
||||
return Arrays.asList(deviceList).contains(currentDevice);
|
||||
}
|
||||
}
|
||||
17
extensions/samsung/radio/stub/build.gradle.kts
Normal file
17
extensions/samsung/radio/stub/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.revanced.extension"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<manifest/>
|
||||
@@ -0,0 +1,7 @@
|
||||
package android.os;
|
||||
|
||||
public class SemSystemProperties {
|
||||
public static String getSalesCode() {
|
||||
throw new UnsupportedOperationException("Stub");
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,6 @@ import app.revanced.extension.shared.ui.CustomDialog;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class GmsCoreSupport {
|
||||
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
|
||||
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";
|
||||
|
||||
private static final String GMS_CORE_PACKAGE_NAME
|
||||
= getGmsCoreVendorGroupId() + ".android.gms";
|
||||
private static final Uri GMS_CORE_PROVIDER
|
||||
@@ -53,6 +50,20 @@ public class GmsCoreSupport {
|
||||
@Nullable
|
||||
private static volatile Boolean DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
||||
|
||||
private static String getOriginalPackageName() {
|
||||
return null; // Modified during patching.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the current package name is the same as the original unpatched app.
|
||||
* If `GmsCore support` was not included during patching, this returns true;
|
||||
*/
|
||||
public static boolean isPackageNameOriginal() {
|
||||
String originalPackageName = getOriginalPackageName();
|
||||
return originalPackageName == null
|
||||
|| originalPackageName.equals(Utils.getContext().getPackageName());
|
||||
}
|
||||
|
||||
private static void open(String queryOrLink) {
|
||||
Logger.printInfo(() -> "Opening link: " + queryOrLink);
|
||||
|
||||
@@ -113,11 +124,10 @@ public class GmsCoreSupport {
|
||||
// Verify the user has not included GmsCore for a root installation.
|
||||
// GmsCore Support changes the package name, but with a mounted installation
|
||||
// all manifest changes are ignored and the original package name is used.
|
||||
String packageName = context.getPackageName();
|
||||
if (packageName.equals(PACKAGE_NAME_YOUTUBE) || packageName.equals(PACKAGE_NAME_YOUTUBE_MUSIC)) {
|
||||
if (isPackageNameOriginal()) {
|
||||
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
|
||||
// Cannot use localize text here, since the app will load
|
||||
// resources from the unpatched app and all patch strings are missing.
|
||||
// Cannot use localize text here, since the app will load resources
|
||||
// from the unpatched app and all patch strings are missing.
|
||||
Utils.showToastLong("The 'GmsCore support' patch breaks mount installations");
|
||||
|
||||
// Do not exit. If the app exits before launch completes (and without
|
||||
@@ -250,8 +260,7 @@ public class GmsCoreSupport {
|
||||
};
|
||||
}
|
||||
|
||||
// Modified by a patch. Do not touch.
|
||||
private static String getGmsCoreVendorGroupId() {
|
||||
return "app.revanced";
|
||||
return "app.revanced"; // Modified during patching.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package app.revanced.extension.shared.patches;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import app.revanced.extension.shared.GmsCoreSupport;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
|
||||
/**
|
||||
* Patch shared by YouTube and YT Music.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class CustomBrandingPatch {
|
||||
|
||||
// Important: In the future, additional branding themes can be added but all existing and prior
|
||||
// themes cannot be removed or renamed.
|
||||
//
|
||||
// This is because if a user has a branding theme selected, then only that launch alias is enabled.
|
||||
// If a future update removes or renames that alias, then after updating the app is effectively
|
||||
// broken and it cannot be opened and not even clearing the app data will fix it.
|
||||
// In that situation the only fix is to completely uninstall and reinstall again.
|
||||
//
|
||||
// The most that can be done is to hide a theme from the UI and keep the alias with dummy data.
|
||||
public enum BrandingTheme {
|
||||
/**
|
||||
* Original unpatched icon.
|
||||
*/
|
||||
ORIGINAL,
|
||||
ROUNDED,
|
||||
MINIMAL,
|
||||
SCALED,
|
||||
/**
|
||||
* User provided custom icon.
|
||||
*/
|
||||
CUSTOM;
|
||||
|
||||
private String packageAndNameIndexToClassAlias(String packageName, int appIndex) {
|
||||
if (appIndex <= 0) {
|
||||
throw new IllegalArgumentException("App index starts at index 1");
|
||||
}
|
||||
return packageName + ".revanced_" + name().toLowerCase(Locale.US) + '_' + appIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int notificationSmallIcon;
|
||||
|
||||
static {
|
||||
BrandingTheme branding = BaseSettings.CUSTOM_BRANDING_ICON.get();
|
||||
if (branding == BrandingTheme.ORIGINAL) {
|
||||
notificationSmallIcon = 0;
|
||||
} else {
|
||||
// Original icon is quantum_ic_video_youtube_white_24
|
||||
String iconName = "revanced_notification_icon";
|
||||
if (branding == BrandingTheme.CUSTOM) {
|
||||
iconName += "_custom";
|
||||
}
|
||||
|
||||
notificationSmallIcon = Utils.getResourceIdentifier(iconName, "drawable");
|
||||
if (notificationSmallIcon == 0) {
|
||||
Logger.printException(() -> "Could not load notification small icon");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void setNotificationIcon(Notification.Builder builder) {
|
||||
try {
|
||||
if (notificationSmallIcon != 0) {
|
||||
builder.setSmallIcon(notificationSmallIcon)
|
||||
.setColor(Color.TRANSPARENT); // Remove YT red tint.
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setNotificationIcon failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
* The total number of app name aliases, including dummy aliases.
|
||||
*/
|
||||
private static int numberOfPresetAppNames() {
|
||||
// Modified during patching.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static void setBranding() {
|
||||
try {
|
||||
if (GmsCoreSupport.isPackageNameOriginal()) {
|
||||
Logger.printInfo(() -> "App is root mounted. Cannot dynamically change app icon");
|
||||
return;
|
||||
}
|
||||
|
||||
Context context = Utils.getContext();
|
||||
PackageManager pm = context.getPackageManager();
|
||||
String packageName = context.getPackageName();
|
||||
|
||||
BrandingTheme selectedBranding = BaseSettings.CUSTOM_BRANDING_ICON.get();
|
||||
final int selectedNameIndex = BaseSettings.CUSTOM_BRANDING_NAME.get();
|
||||
ComponentName componentToEnable = null;
|
||||
ComponentName defaultComponent = null;
|
||||
List<ComponentName> componentsToDisable = new ArrayList<>();
|
||||
|
||||
for (BrandingTheme theme : BrandingTheme.values()) {
|
||||
// Must always update all aliases including custom alias (last index).
|
||||
final int numberOfPresetAppNames = numberOfPresetAppNames();
|
||||
|
||||
// App name indices starts at 1.
|
||||
for (int index = 1; index <= numberOfPresetAppNames; index++) {
|
||||
String aliasClass = theme.packageAndNameIndexToClassAlias(packageName, index);
|
||||
ComponentName component = new ComponentName(packageName, aliasClass);
|
||||
if (defaultComponent == null) {
|
||||
// Default is always the first alias.
|
||||
defaultComponent = component;
|
||||
}
|
||||
|
||||
if (index == selectedNameIndex && theme == selectedBranding) {
|
||||
componentToEnable = component;
|
||||
} else {
|
||||
componentsToDisable.add(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (componentToEnable == null) {
|
||||
// User imported a bad app name index value. Either the imported data
|
||||
// was corrupted, or they previously had custom name enabled and the app
|
||||
// no longer has a custom name specified.
|
||||
Utils.showToastLong("Custom branding reset");
|
||||
BaseSettings.CUSTOM_BRANDING_ICON.resetToDefault();
|
||||
BaseSettings.CUSTOM_BRANDING_NAME.resetToDefault();
|
||||
|
||||
componentToEnable = defaultComponent;
|
||||
componentsToDisable.remove(defaultComponent);
|
||||
}
|
||||
|
||||
for (ComponentName disable : componentsToDisable) {
|
||||
pm.setComponentEnabledSetting(disable,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
||||
}
|
||||
|
||||
// Use info logging because if the alias status become corrupt the app cannot launch.
|
||||
ComponentName componentToEnableFinal = componentToEnable;
|
||||
Logger.printInfo(() -> "Enabling: " + componentToEnableFinal.getClassName());
|
||||
|
||||
pm.setComponentEnabledSetting(componentToEnable,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setBranding failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,6 @@ public class LinkSanitizer {
|
||||
|
||||
public LinkSanitizer(String ... parametersToRemove) {
|
||||
final int parameterCount = parametersToRemove.length;
|
||||
if (parameterCount == 0) {
|
||||
throw new IllegalArgumentException("No parameters specified");
|
||||
}
|
||||
|
||||
// List is faster if only checking a few parameters.
|
||||
this.parametersToRemove = parameterCount > 4
|
||||
@@ -40,10 +37,12 @@ public class LinkSanitizer {
|
||||
try {
|
||||
Uri.Builder builder = uri.buildUpon().clearQuery();
|
||||
|
||||
for (String paramName : uri.getQueryParameterNames()) {
|
||||
if (!parametersToRemove.contains(paramName)) {
|
||||
for (String value : uri.getQueryParameters(paramName)) {
|
||||
builder.appendQueryParameter(paramName, value);
|
||||
if (!parametersToRemove.isEmpty()) {
|
||||
for (String paramName : uri.getQueryParameterNames()) {
|
||||
if (!parametersToRemove.contains(paramName)) {
|
||||
for (String value : uri.getQueryParameters(paramName)) {
|
||||
builder.appendQueryParameter(paramName, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package app.revanced.extension.shared.settings;
|
||||
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static app.revanced.extension.shared.patches.CustomBrandingPatch.BrandingTheme;
|
||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability;
|
||||
|
||||
@@ -40,4 +41,7 @@ public class BaseSettings {
|
||||
public static final BooleanSetting REPLACE_MUSIC_LINKS_WITH_YOUTUBE = new BooleanSetting("revanced_replace_music_with_youtube", FALSE);
|
||||
|
||||
public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);
|
||||
|
||||
public static final EnumSetting<BrandingTheme> CUSTOM_BRANDING_ICON = new EnumSetting<>("revanced_custom_branding_icon", BrandingTheme.ORIGINAL, true);
|
||||
public static final IntegerSetting CUSTOM_BRANDING_NAME = new IntegerSetting("revanced_custom_branding_name", 1, true);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import static app.revanced.extension.shared.Utils.dipToPixels;
|
||||
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
@@ -125,6 +126,8 @@ public class ReVancedAboutPreference extends Preference {
|
||||
|
||||
{
|
||||
setOnPreferenceClickListener(pref -> {
|
||||
Context context = pref.getContext();
|
||||
|
||||
// Show a progress spinner if the social links are not fetched yet.
|
||||
if (!AboutLinksRoutes.hasFetchedLinks() && Utils.isNetworkConnected()) {
|
||||
// Show a progress spinner, but only if the api fetch takes more than a half a second.
|
||||
@@ -137,17 +140,18 @@ public class ReVancedAboutPreference extends Preference {
|
||||
handler.postDelayed(showDialogRunnable, delayToShowProgressSpinner);
|
||||
|
||||
Utils.runOnBackgroundThread(() ->
|
||||
fetchLinksAndShowDialog(handler, showDialogRunnable, progress));
|
||||
fetchLinksAndShowDialog(context, handler, showDialogRunnable, progress));
|
||||
} else {
|
||||
// No network call required and can run now.
|
||||
fetchLinksAndShowDialog(null, null, null);
|
||||
fetchLinksAndShowDialog(context, null, null, null);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private void fetchLinksAndShowDialog(@Nullable Handler handler,
|
||||
private void fetchLinksAndShowDialog(Context context,
|
||||
@Nullable Handler handler,
|
||||
Runnable showDialogRunnable,
|
||||
@Nullable ProgressDialog progress) {
|
||||
WebLink[] links = AboutLinksRoutes.fetchAboutLinks();
|
||||
@@ -164,7 +168,17 @@ public class ReVancedAboutPreference extends Preference {
|
||||
if (handler != null) {
|
||||
handler.removeCallbacks(showDialogRunnable);
|
||||
}
|
||||
if (progress != null) {
|
||||
|
||||
// Don't continue if the activity is done. To test this tap the
|
||||
// about dialog and immediately press back before the dialog can show.
|
||||
if (context instanceof Activity activity) {
|
||||
if (activity.isFinishing() || activity.isDestroyed()) {
|
||||
Logger.printDebug(() -> "Not showing about dialog, activity is closed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (progress != null && progress.isShowing()) {
|
||||
progress.dismiss();
|
||||
}
|
||||
new WebViewDialog(getContext(), htmlDialog).show();
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.graphics.Insets;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceGroup;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.util.TypedValue;
|
||||
import android.view.ViewGroup;
|
||||
@@ -22,6 +23,24 @@ import app.revanced.extension.shared.settings.BaseActivityHook;
|
||||
|
||||
@SuppressWarnings({"deprecation", "NewApi"})
|
||||
public class ToolbarPreferenceFragment extends AbstractPreferenceFragment {
|
||||
|
||||
/**
|
||||
* Removes the list of preferences from this fragment, if they exist.
|
||||
* @param keys Preference keys.
|
||||
*/
|
||||
protected void removePreferences(String ... keys) {
|
||||
for (String key : keys) {
|
||||
Preference pref = findPreference(key);
|
||||
if (pref != null) {
|
||||
PreferenceGroup parent = pref.getParent();
|
||||
if (parent != null) {
|
||||
Logger.printDebug(() -> "Removing preference: " + key);
|
||||
parent.removePreference(pref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets toolbar for all nested preference screens.
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,12 @@ public class ExtensionPreferenceCategory extends ConditionalPreferenceCategory {
|
||||
public void addPreferences(Context context) {
|
||||
addPreference(new ReVancedTikTokAboutPreference(context));
|
||||
|
||||
addPreference(new TogglePreference(context,
|
||||
"Sanitize sharing links",
|
||||
"Remove tracking parameters from shared links.",
|
||||
BaseSettings.SANITIZE_SHARED_LINKS
|
||||
));
|
||||
|
||||
addPreference(new TogglePreference(context,
|
||||
"Enable debug log",
|
||||
"Show extension debug log.",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package app.revanced.extension.tiktok.share;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.privacy.LinkSanitizer;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class ShareUrlSanitizer {
|
||||
|
||||
private static final LinkSanitizer sanitizer = new LinkSanitizer();
|
||||
|
||||
/**
|
||||
* Injection point for setting check.
|
||||
*/
|
||||
public static boolean shouldSanitize() {
|
||||
return BaseSettings.SANITIZE_SHARED_LINKS.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point for URL sanitization.
|
||||
*/
|
||||
public static String sanitizeShareUrl(final String url) {
|
||||
if (url == null || url.isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
return sanitizer.sanitizeUrlString(url);
|
||||
}
|
||||
}
|
||||
@@ -2,22 +2,29 @@ package app.revanced.twitter.patches.links;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class ChangeLinkSharingDomainPatch {
|
||||
private static final String DOMAIN_NAME = "https://fxtwitter.com";
|
||||
private static final String LINK_FORMAT = "%s/%s/status/%s";
|
||||
private static final String LINK_FORMAT = "https://%s/%s/status/%s";
|
||||
|
||||
/**
|
||||
* Method is modified during patching. Do not change.
|
||||
*/
|
||||
private static String getShareDomain() {
|
||||
return "";
|
||||
}
|
||||
|
||||
// TODO remove this once changeLinkSharingDomainResourcePatch is restored
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String formatResourceLink(Object... formatArgs) {
|
||||
String username = (String) formatArgs[0];
|
||||
String tweetId = (String) formatArgs[1];
|
||||
return String.format(LINK_FORMAT, DOMAIN_NAME, username, tweetId);
|
||||
return String.format(LINK_FORMAT, getShareDomain(), username, tweetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static String formatLink(long tweetId, String username) {
|
||||
return String.format(LINK_FORMAT, DOMAIN_NAME, username, tweetId);
|
||||
return String.format(LINK_FORMAT, getShareDomain(), username, tweetId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,25 @@ public class ChangeHeaderPatch {
|
||||
DEFAULT(null, null),
|
||||
REGULAR("ytWordmarkHeader", "yt_ringo2_wordmark_header"),
|
||||
PREMIUM("ytPremiumWordmarkHeader", "yt_ringo2_premium_wordmark_header"),
|
||||
REVANCED("revanced_header_logo", "revanced_header_logo"),
|
||||
REVANCED_MINIMAL("revanced_header_logo_minimal", "revanced_header_logo_minimal"),
|
||||
CUSTOM("custom_header", "custom_header");
|
||||
ROUNDED("revanced_header_rounded"),
|
||||
MINIMAL("revanced_header_minimal"),
|
||||
CUSTOM("revanced_header_custom"),
|
||||
|
||||
// Old enum names for data migration. TODO: Eventually delete these.
|
||||
@Deprecated
|
||||
REVANCED(ROUNDED.attributeName),
|
||||
@Deprecated
|
||||
REVANCED_MINIMAL(MINIMAL.attributeName);
|
||||
|
||||
@Nullable
|
||||
private final String attributeName;
|
||||
@Nullable
|
||||
private final String drawableName;
|
||||
|
||||
HeaderLogo(String attributeName) {
|
||||
this(Objects.requireNonNull(attributeName), Objects.requireNonNull(attributeName));
|
||||
}
|
||||
|
||||
HeaderLogo(@Nullable String attributeName, @Nullable String drawableName) {
|
||||
this.attributeName = attributeName;
|
||||
this.drawableName = drawableName;
|
||||
@@ -42,9 +52,8 @@ public class ChangeHeaderPatch {
|
||||
|
||||
final int identifier = Utils.getResourceIdentifier(attributeName, "attr");
|
||||
if (identifier == 0) {
|
||||
// Identifier is zero if custom header setting was included in imported settings
|
||||
// and a custom image was not included during patching.
|
||||
Logger.printDebug(() -> "Could not find attribute: " + drawableName);
|
||||
// Should never happen.
|
||||
Logger.printException(() -> "Could not find attribute: " + drawableName);
|
||||
Settings.HEADER_LOGO.resetToDefault();
|
||||
return null;
|
||||
}
|
||||
@@ -63,12 +72,14 @@ public class ChangeHeaderPatch {
|
||||
: "_light");
|
||||
|
||||
final int identifier = Utils.getResourceIdentifier(drawableFullName, "drawable");
|
||||
if (identifier == 0) {
|
||||
Logger.printDebug(() -> "Could not find drawable: " + drawableFullName);
|
||||
Settings.HEADER_LOGO.resetToDefault();
|
||||
return null;
|
||||
if (identifier != 0) {
|
||||
return Utils.getContext().getDrawable(identifier);
|
||||
}
|
||||
return Utils.getContext().getDrawable(identifier);
|
||||
|
||||
// Should never happen.
|
||||
Logger.printException(() -> "Could not find drawable: " + drawableFullName);
|
||||
Settings.HEADER_LOGO.resetToDefault();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,8 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"post_shelf_slim.e",
|
||||
"videos_post_responsive_root.e",
|
||||
"text_post_responsive_root.e",
|
||||
"poll_post_responsive_root.e"
|
||||
"poll_post_responsive_root.e",
|
||||
"shared_post_root.e"
|
||||
);
|
||||
|
||||
final var subscribersCommunityGuidelines = new StringFilterGroup(
|
||||
|
||||
@@ -29,14 +29,18 @@ public class SpoofVideoStreamsPatch {
|
||||
public static void setClientOrderToUse() {
|
||||
ClientType client = Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
|
||||
|
||||
if (client == ANDROID_VR_1_43_32 && Settings.SPOOF_VIDEO_STREAMS_AV1.get()) {
|
||||
// Use VR 1.61 client that has AV1 if user settings allow it.
|
||||
// AVC cannot be forced with VR 1.61 because it uses VP9 and AV1.
|
||||
// If both settings are on, then force AVC takes priority and VR 1.43 is used.
|
||||
if (client == ANDROID_VR_1_43_32 && Settings.SPOOF_VIDEO_STREAMS_AV1.get()
|
||||
&& !Settings.FORCE_AVC_CODEC.get()) {
|
||||
client = ANDROID_VR_1_61_48;
|
||||
}
|
||||
|
||||
List<ClientType> availableClients = List.of(
|
||||
VISIONOS,
|
||||
ANDROID_CREATOR,
|
||||
ANDROID_VR_1_43_32,
|
||||
VISIONOS,
|
||||
IPADOS);
|
||||
|
||||
app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse(
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package app.revanced.extension.youtube.patches.theme;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.youtube.patches.HideSeekbarPatch;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
/**
|
||||
* Used by {@link SeekbarColorPatch} change the color of the seekbar.
|
||||
* and {@link HideSeekbarPatch} to hide the seekbar of the feed and watch history.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ProgressBarDrawable extends Drawable {
|
||||
|
||||
private final Paint paint = new Paint();
|
||||
{
|
||||
paint.setColor(SeekbarColorPatch.getSeekbarColor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas.drawRect(getBounds(), paint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
paint.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||
paint.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,9 +4,7 @@ import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.shared.Utils.clamp;
|
||||
import static app.revanced.extension.youtube.patches.theme.ThemePatch.SplashScreenAnimationStyle;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
|
||||
import com.airbnb.lottie.LottieAnimationView;
|
||||
|
||||
@@ -15,7 +13,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Scanner;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
@@ -104,27 +101,6 @@ public final class SeekbarColorPatch {
|
||||
return customSeekbarColor;
|
||||
}
|
||||
|
||||
private static int colorChannelTo3Bits(int channel8Bits) {
|
||||
final float channel3Bits = channel8Bits * 7 / 255f;
|
||||
|
||||
// If a color channel is near zero, then allow rounding up so values between
|
||||
// 0x12 and 0x23 will show as 0x24. But always round down when the channel is
|
||||
// near full saturation, otherwise rounding to nearest will cause all values
|
||||
// between 0xEC and 0xFE to always show as full saturation (0xFF).
|
||||
return channel3Bits < 6
|
||||
? Math.round(channel3Bits)
|
||||
: (int) channel3Bits;
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static String get9BitStyleIdentifier(int color24Bit) {
|
||||
final int r3 = colorChannelTo3Bits(Color.red(color24Bit));
|
||||
final int g3 = colorChannelTo3Bits(Color.green(color24Bit));
|
||||
final int b3 = colorChannelTo3Bits(Color.blue(color24Bit));
|
||||
|
||||
return String.format(Locale.US, "splash_seekbar_color_style_%d_%d_%d", r3, g3, b3);
|
||||
}
|
||||
|
||||
/**
|
||||
* injection point.
|
||||
*/
|
||||
@@ -135,36 +111,6 @@ public final class SeekbarColorPatch {
|
||||
return original; // false = drawable style, true = lottie style.
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Old drawable style launch screen.
|
||||
*/
|
||||
public static void setSplashAnimationDrawableTheme(AnimatedVectorDrawable vectorDrawable) {
|
||||
// Alternatively a ColorMatrixColorFilter can be used to change the color of the drawable
|
||||
// without using any styles, but a color filter cannot selectively change the seekbar
|
||||
// while keeping the red YT logo untouched.
|
||||
// Even if the seekbar color xml value is changed to a completely different color (such as green),
|
||||
// a color filter still cannot be selectively applied when the drawable has more than 1 color.
|
||||
try {
|
||||
// Must set the color even if custom seekbar is off,
|
||||
// because the xml color was replaced with a themed value.
|
||||
String seekbarStyle = get9BitStyleIdentifier(customSeekbarColor);
|
||||
Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle);
|
||||
|
||||
final int styleIdentifierDefault = Utils.getResourceIdentifierOrThrow(
|
||||
seekbarStyle,
|
||||
"style"
|
||||
);
|
||||
|
||||
Resources.Theme theme = Utils.getContext().getResources().newTheme();
|
||||
theme.applyStyle(styleIdentifierDefault, true);
|
||||
|
||||
vectorDrawable.applyTheme(theme);
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "setSplashAnimationDrawableTheme failure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Modern Lottie style animation.
|
||||
|
||||
@@ -2,7 +2,6 @@ package app.revanced.extension.youtube.settings;
|
||||
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static app.revanced.extension.shared.settings.Setting.migrateOldSettingToNew;
|
||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||
import static app.revanced.extension.shared.settings.Setting.parentsAll;
|
||||
import static app.revanced.extension.shared.settings.Setting.parentsAny;
|
||||
@@ -17,7 +16,6 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerH
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideSubtextsAvailability;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MINIMAL;
|
||||
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.components.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability;
|
||||
@@ -41,7 +39,6 @@ import app.revanced.extension.shared.settings.IntegerSetting;
|
||||
import app.revanced.extension.shared.settings.LongSetting;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.shared.settings.StringSetting;
|
||||
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
|
||||
import app.revanced.extension.shared.spoof.ClientType;
|
||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.DeArrowAvailability;
|
||||
import app.revanced.extension.youtube.patches.AlternativeThumbnailsPatch.StillImagesAvailability;
|
||||
@@ -452,14 +449,7 @@ public class Settings extends BaseSettings {
|
||||
public static final StringSetting SB_CATEGORY_UNSUBMITTED_COLOR = new StringSetting("sb_unsubmitted_color", "#FFFFFFFF", false, false);
|
||||
|
||||
// Deprecated migrations
|
||||
private static final BooleanSetting DEPRECATED_AUTO_REPEAT = new BooleanSetting("revanced_auto_repeat", FALSE);
|
||||
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_BUTTONS = new BooleanSetting("revanced_hide_player_buttons", FALSE, true);
|
||||
private static final BooleanSetting DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER = new BooleanSetting("revanced_hide_video_quality_menu_footer", FALSE);
|
||||
private static final IntegerSetting DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA = new IntegerSetting("revanced_swipe_overlay_background_alpha", 127);
|
||||
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
|
||||
private static final BooleanSetting DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE);
|
||||
private static final BooleanSetting DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
|
||||
private static final BooleanSetting DEPRECATED_AUTO_CAPTIONS = new BooleanSetting("revanced_auto_captions", FALSE);
|
||||
|
||||
private static final FloatSetting DEPRECATED_SB_CATEGORY_SPONSOR_OPACITY = new FloatSetting("sb_sponsor_opacity", 0.8f, false, false);
|
||||
private static final FloatSetting DEPRECATED_SB_CATEGORY_SELF_PROMO_OPACITY = new FloatSetting("sb_selfpromo_opacity", 0.8f, false, false);
|
||||
@@ -475,17 +465,12 @@ public class Settings extends BaseSettings {
|
||||
static {
|
||||
// region Migration
|
||||
|
||||
migrateOldSettingToNew(DEPRECATED_AUTO_REPEAT, LOOP_VIDEO);
|
||||
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_BUTTONS, HIDE_PLAYER_PREVIOUS_NEXT_BUTTONS);
|
||||
migrateOldSettingToNew(DEPRECATED_HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER, HIDE_PLAYER_FLYOUT_VIDEO_QUALITY_FOOTER);
|
||||
migrateOldSettingToNew(DEPRECATED_DISABLE_SUGGESTED_VIDEO_END_SCREEN, HIDE_END_SCREEN_SUGGESTED_VIDEO);
|
||||
migrateOldSettingToNew(DEPRECATED_RESTORE_OLD_VIDEO_QUALITY_MENU, ADVANCED_VIDEO_QUALITY_MENU);
|
||||
migrateOldSettingToNew(DEPRECATED_AUTO_CAPTIONS, DISABLE_AUTO_CAPTIONS);
|
||||
|
||||
// Migrate renamed enum.
|
||||
//noinspection deprecation
|
||||
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {
|
||||
MINIPLAYER_TYPE.save(MINIMAL);
|
||||
// Migrate renamed change header enums.
|
||||
if (HEADER_LOGO.get() == HeaderLogo.REVANCED) {
|
||||
HEADER_LOGO.save(HeaderLogo.ROUNDED);
|
||||
}
|
||||
if (HEADER_LOGO.get() == HeaderLogo.REVANCED_MINIMAL) {
|
||||
HEADER_LOGO.save(HeaderLogo.MINIMAL);
|
||||
}
|
||||
|
||||
// Migrate old single color seekbar with a slightly brighter accent color based on the primary.
|
||||
@@ -512,11 +497,6 @@ public class Settings extends BaseSettings {
|
||||
DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.resetToDefault();
|
||||
}
|
||||
|
||||
if (!DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.isSetToDefault()) {
|
||||
SWIPE_OVERLAY_OPACITY.save(DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.get() / 255);
|
||||
DEPRECATED_SWIPE_OVERLAY_BACKGROUND_ALPHA.resetToDefault();
|
||||
}
|
||||
|
||||
// Old spoof versions that no longer work,
|
||||
// or is spoofing to a version the same or newer than this app.
|
||||
if (!SPOOF_APP_VERSION_TARGET.isSetToDefault() &&
|
||||
@@ -534,13 +514,7 @@ public class Settings extends BaseSettings {
|
||||
|
||||
// RYD requires manually migrating old settings since the lack of
|
||||
// a "revanced_" on the old setting causes duplicate key exceptions during export.
|
||||
SharedPrefCategory revancedPrefs = Setting.preferences;
|
||||
Setting.migrateFromOldPreferences(revancedPrefs, RYD_USER_ID, "ryd_user_id");
|
||||
Setting.migrateFromOldPreferences(revancedPrefs, RYD_ENABLED, "ryd_enabled");
|
||||
Setting.migrateFromOldPreferences(revancedPrefs, RYD_DISLIKE_PERCENTAGE, "ryd_dislike_percentage");
|
||||
Setting.migrateFromOldPreferences(revancedPrefs, RYD_COMPACT_LAYOUT, "ryd_compact_layout");
|
||||
Setting.migrateFromOldPreferences(revancedPrefs, RYD_ESTIMATED_LIKE, "ryd_estimated_like");
|
||||
Setting.migrateFromOldPreferences(revancedPrefs, RYD_TOAST_ON_CONNECTION_ERROR, "ryd_toast_on_connection_error");
|
||||
Setting.migrateFromOldPreferences(Setting.preferences, RYD_USER_ID, "ryd_user_id");
|
||||
|
||||
// Migrate old saved data. Must be done here before the settings can be used by any other code.
|
||||
applyOldSbOpacityToColor(SB_CATEGORY_SPONSOR_COLOR, DEPRECATED_SB_CATEGORY_SPONSOR_OPACITY);
|
||||
|
||||
@@ -4,8 +4,10 @@ import android.app.Dialog;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import app.revanced.extension.shared.GmsCoreSupport;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment;
|
||||
import app.revanced.extension.youtube.settings.YouTubeActivityHook;
|
||||
|
||||
@@ -30,6 +32,17 @@ public class YouTubePreferenceFragment extends ToolbarPreferenceFragment {
|
||||
preferenceScreen = getPreferenceScreen();
|
||||
Utils.sortPreferenceGroups(preferenceScreen);
|
||||
setPreferenceScreenToolbar(preferenceScreen);
|
||||
|
||||
// Clunky work around until preferences are custom classes that manage themselves.
|
||||
// Custom branding only works with non-root install. But the preferences must be
|
||||
// added during patched because of difficulties detecting during patching if it's
|
||||
// a root install. So instead the non-functional preferences are removed during
|
||||
// runtime if the app is mount (root) installation.
|
||||
if (GmsCoreSupport.isPackageNameOriginal()) {
|
||||
removePreferences(
|
||||
BaseSettings.CUSTOM_BRANDING_ICON.key,
|
||||
BaseSettings.CUSTOM_BRANDING_NAME.key);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "initialize failure", ex);
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.42.0-dev.17
|
||||
version = 5.45.0-dev.4
|
||||
|
||||
@@ -60,6 +60,10 @@ public final class app/revanced/patches/all/misc/connectivity/wifi/spoof/SpoofWi
|
||||
public static final fun getSpoofWifiPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/customcertificates/CustomCertificatesPatchKt {
|
||||
public static final fun getCustomNetworkSecurityPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/debugging/EnableAndroidDebuggingPatchKt {
|
||||
public static final fun getEnableAndroidDebuggingPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
@@ -184,6 +188,10 @@ public final class app/revanced/patches/duolingo/debug/EnableDebugMenuPatchKt {
|
||||
public static final fun getEnableDebugMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/duolingo/energy/SkipEnergyRechargeAdsPatchKt {
|
||||
public static final fun getSkipEnergyRechargeAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/facebook/ads/mainfeed/HideSponsoredStoriesPatchKt {
|
||||
public static final fun getHideSponsoredStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -269,7 +277,7 @@ public final class app/revanced/patches/instagram/feed/LimitFeedToFollowedProfil
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/hide/explore/HideExploreFeedKt {
|
||||
public static final fun getHideExportFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public static final fun getHideExploreFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/hide/navigation/HideNavigationButtonsKt {
|
||||
@@ -280,6 +288,10 @@ public final class app/revanced/patches/instagram/hide/stories/HideStoriesKt {
|
||||
public static final fun getHideStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/hide/suggestions/HideSuggestedContentKt {
|
||||
public static final fun getHideSuggestedContent ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/misc/devmenu/EnableDeveloperMenuPatchKt {
|
||||
public static final fun getEnableDeveloperMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -477,7 +489,9 @@ public final class app/revanced/patches/music/misc/tracks/ForceOriginalAudioPatc
|
||||
|
||||
public final class app/revanced/patches/music/playservice/VersionCheckPatchKt {
|
||||
public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
public static final fun is_7_16_or_greater ()Z
|
||||
public static final fun is_7_33_or_greater ()Z
|
||||
public static final fun is_8_05_or_greater ()Z
|
||||
public static final fun is_8_10_or_greater ()Z
|
||||
public static final fun is_8_11_or_greater ()Z
|
||||
public static final fun is_8_15_or_greater ()Z
|
||||
@@ -758,6 +772,14 @@ public final class app/revanced/patches/reddit/misc/tracking/url/SanitizeUrlQuer
|
||||
public static final fun getSanitizeUrlQueryPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/samsung/radio/misc/fix/crash/FixCrashPatchKt {
|
||||
public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/samsung/radio/restrictions/device/BypassDeviceChecksPatchKt {
|
||||
public static final fun getBypassDeviceChecksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/serviceportalbund/detection/root/RootDetectionPatchKt {
|
||||
public static final fun getRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1166,6 +1188,10 @@ public final class app/revanced/patches/tiktok/misc/settings/SettingsPatchKt {
|
||||
public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatchKt {
|
||||
public static final fun getSanitizeShareUrlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/tiktok/misc/spoof/sim/SpoofSimPatchKt {
|
||||
public static final fun getSpoofSimPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1919,6 +1945,7 @@ public final class app/revanced/util/ResourceUtilsKt {
|
||||
public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
||||
public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V
|
||||
public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
|
||||
public static final fun removeFromParent (Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
|
||||
}
|
||||
|
||||
public final class app/revanced/util/resource/ArrayResource : app/revanced/util/resource/BaseResource {
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
package app.revanced.patches.all.misc.customcertificates
|
||||
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.booleanOption
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringsOption
|
||||
import app.revanced.util.Utils.trimIndentMultiline
|
||||
import app.revanced.util.getNode
|
||||
import org.w3c.dom.Element
|
||||
import java.io.File
|
||||
|
||||
|
||||
|
||||
|
||||
val customNetworkSecurityPatch = resourcePatch(
|
||||
name = "Custom network security",
|
||||
description = "Allows trusting custom certificate authorities for a specific domain.",
|
||||
use = false
|
||||
) {
|
||||
|
||||
val targetDomains by stringsOption(
|
||||
key = "targetDomains",
|
||||
title = "Target domains",
|
||||
description = "List of domains to which the custom trust configuration will be applied (one domain per entry).",
|
||||
default = listOf("example.com"),
|
||||
required = true
|
||||
)
|
||||
|
||||
val includeSubdomains by booleanOption(
|
||||
key = "includeSubdomains",
|
||||
title = "Include subdomains",
|
||||
description = "Applies the configuration to all subdomains of the target domains.",
|
||||
default = false,
|
||||
required = true
|
||||
)
|
||||
|
||||
val customCAFilePaths by stringsOption(
|
||||
key = "customCAFilePaths",
|
||||
title = "Custom CA file paths",
|
||||
description = """
|
||||
List of paths to files in PEM or DER format (one file path per entry).
|
||||
|
||||
Makes an app trust the provided custom certificate authorities (CAs),
|
||||
for the specified domains, and if the option "Include Subdomains" is enabled then also the subdomains.
|
||||
|
||||
|
||||
CA files will be bundled in res/raw/ of resulting APK
|
||||
""".trimIndentMultiline(),
|
||||
default = null,
|
||||
required = false
|
||||
)
|
||||
|
||||
val allowUserCerts by booleanOption(
|
||||
key = "allowUserCerts",
|
||||
title = "Trust user added CAs",
|
||||
description = "Makes an app trust certificates from the Android user store for the specified domains, and if the option \"Include Subdomains\" is enabled then also the subdomains.",
|
||||
|
||||
default = false,
|
||||
required = true
|
||||
)
|
||||
|
||||
val allowSystemCerts by booleanOption(
|
||||
key = "allowSystemCerts",
|
||||
title = "Trust system CAs",
|
||||
description = "Makes an app trust certificates from the Android system store for the specified domains, and and if the option \"Include Subdomains\" is enabled then also the subdomains.",
|
||||
|
||||
default = true,
|
||||
required = true
|
||||
)
|
||||
|
||||
val allowCleartextTraffic by booleanOption(
|
||||
key = "allowCleartextTraffic",
|
||||
title = "Allow cleartext traffic (HTTP)",
|
||||
description = "Allows unencrypted HTTP traffic for the specified domains, and if \"Include Subdomains\" is enabled then also the subdomains.",
|
||||
|
||||
default = false,
|
||||
required = true
|
||||
)
|
||||
|
||||
val overridePins by booleanOption(
|
||||
key = "overridePins",
|
||||
title = "Override certificate pinning",
|
||||
description = "Overrides certificate pinning for the specified domains and their subdomains if the option \"Include Subdomains\" is enabled to allow inspecting app traffic via a proxy.",
|
||||
|
||||
default = false,
|
||||
required = true
|
||||
)
|
||||
|
||||
fun generateNetworkSecurityConfig(): String {
|
||||
val targetDomains = targetDomains ?: emptyList()
|
||||
val includeSubdomains = includeSubdomains ?: false
|
||||
val customCAFilePaths = customCAFilePaths ?: emptyList()
|
||||
val allowUserCerts = allowUserCerts ?: false
|
||||
val allowSystemCerts = allowSystemCerts ?: true
|
||||
val allowCleartextTraffic = allowCleartextTraffic ?: false
|
||||
val overridePins = overridePins ?: false
|
||||
|
||||
val domainsXML = buildString {
|
||||
targetDomains.forEach {
|
||||
appendLine(""" <domain includeSubdomains="$includeSubdomains">$it</domain>""")
|
||||
}
|
||||
}.trimEnd()
|
||||
|
||||
val trustAnchorsXML = buildString {
|
||||
if (allowSystemCerts) {
|
||||
appendLine(""" <certificates src="system" overridePins="$overridePins" />""")
|
||||
}
|
||||
if (allowUserCerts) {
|
||||
appendLine(""" <certificates src="user" overridePins="$overridePins" />""")
|
||||
}
|
||||
customCAFilePaths.forEach { path ->
|
||||
val fileName = path.substringAfterLast('/').substringBeforeLast('.')
|
||||
appendLine(""" <certificates src="@raw/$fileName" overridePins="$overridePins" />""")
|
||||
}
|
||||
}
|
||||
|
||||
if (trustAnchorsXML.isBlank()) {
|
||||
throw PatchException("At least one trust anchor (System, User, or Custom CA) must be enabled.")
|
||||
}
|
||||
|
||||
return """
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="$allowCleartextTraffic">
|
||||
$domainsXML
|
||||
<trust-anchors>
|
||||
${trustAnchorsXML.trimEnd()}
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
|
||||
execute {
|
||||
val nscFileNameBare = "network_security_config"
|
||||
val resXmlDir = "res/xml"
|
||||
val resRawDir = "res/raw"
|
||||
val nscFileNameWithSuffix = "$nscFileNameBare.xml"
|
||||
|
||||
|
||||
document("AndroidManifest.xml").use { document ->
|
||||
val applicationNode = document.getNode("application") as Element
|
||||
applicationNode.setAttribute("android:networkSecurityConfig", "@xml/$nscFileNameBare")
|
||||
}
|
||||
|
||||
|
||||
File(get(resXmlDir), nscFileNameWithSuffix).apply {
|
||||
writeText(generateNetworkSecurityConfig())
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (customCAFilePath in customCAFilePaths ?: emptyList()) {
|
||||
val file = File(customCAFilePath)
|
||||
if (!file.exists()) {
|
||||
throw PatchException(
|
||||
"The custom CA file path cannot be found: " +
|
||||
file.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
if (!file.isFile) {
|
||||
throw PatchException(
|
||||
"The custom CA file path must be a file: "
|
||||
+ file.absolutePath
|
||||
)
|
||||
}
|
||||
val caFileNameWithoutSuffix = customCAFilePath.substringAfterLast('/').substringBefore('.')
|
||||
val caFile = File(customCAFilePath)
|
||||
File(
|
||||
get(resRawDir),
|
||||
caFileNameWithoutSuffix
|
||||
).writeText(
|
||||
caFile.readText()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,35 @@
|
||||
package app.revanced.patches.duolingo.debug
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import app.revanced.util.returnEarly
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
val enableDebugMenuPatch = bytecodePatch(
|
||||
name = "Enable debug menu",
|
||||
use = false,
|
||||
use = false
|
||||
) {
|
||||
compatibleWith("com.duolingo"("5.158.4"))
|
||||
compatibleWith("com.duolingo")
|
||||
|
||||
execute {
|
||||
initializeBuildConfigProviderFingerprint.method.apply {
|
||||
val insertIndex = initializeBuildConfigProviderFingerprint.patternMatch!!.startIndex
|
||||
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
|
||||
// It seems all categories are allowed on release. Force this on anyway.
|
||||
debugCategoryAllowOnReleaseBuildsFingerprint.method.returnEarly(true)
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"const/4 v$register, 0x1",
|
||||
)
|
||||
// Change build config debug build flag.
|
||||
buildConfigProviderConstructorFingerprint.match(
|
||||
buildConfigProviderToStringFingerprint.classDef
|
||||
).let {
|
||||
val index = it.patternMatch!!.startIndex
|
||||
|
||||
it.method.apply {
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
addInstruction(
|
||||
index + 1,
|
||||
"const/4 v$register, 0x1"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,25 @@ import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* The `BuildConfigProvider` class has two booleans:
|
||||
*
|
||||
* - `isChina`: (usually) compares "play" with "china"...except for builds in China
|
||||
* - `isDebug`: compares "release" with "debug" <-- we want to force this to `true`
|
||||
*/
|
||||
|
||||
internal val initializeBuildConfigProviderFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
returns("V")
|
||||
opcodes(Opcode.IPUT_BOOLEAN)
|
||||
strings("debug", "release", "china")
|
||||
internal val debugCategoryAllowOnReleaseBuildsFingerprint = fingerprint {
|
||||
returns("Z")
|
||||
parameters()
|
||||
custom { method, classDef ->
|
||||
method.name == "getAllowOnReleaseBuilds" && classDef.type == "Lcom/duolingo/debug/DebugCategory;"
|
||||
}
|
||||
}
|
||||
|
||||
internal val buildConfigProviderConstructorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameters()
|
||||
opcodes(Opcode.CONST_4)
|
||||
}
|
||||
|
||||
internal val buildConfigProviderToStringFingerprint = fingerprint {
|
||||
parameters()
|
||||
returns("Ljava/lang/String;")
|
||||
strings("BuildConfigProvider(") // Partial string match.
|
||||
custom { method, _ ->
|
||||
method.name == "toString"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package app.revanced.patches.duolingo.energy
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
/**
|
||||
* Matches the class found in [energyConfigToStringFingerprint].
|
||||
*/
|
||||
internal val initializeEnergyConfigFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
opcodes(Opcode.RETURN_VOID)
|
||||
}
|
||||
|
||||
// Class name currently is not obfuscated but it may be in the future.
|
||||
internal val energyConfigToStringFingerprint = fingerprint {
|
||||
parameters()
|
||||
returns("Ljava/lang/String;")
|
||||
strings("EnergyConfig(", "maxEnergy=") // Partial string matches.
|
||||
custom { method, _ -> method.name == "toString" }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package app.revanced.patches.duolingo.energy
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.findFieldFromToString
|
||||
|
||||
@Suppress("unused")
|
||||
val skipEnergyRechargeAdsPatch = bytecodePatch(
|
||||
name = "Skip energy recharge ads",
|
||||
description = "Skips watching ads to recharge energy."
|
||||
) {
|
||||
compatibleWith("com.duolingo")
|
||||
|
||||
execute {
|
||||
initializeEnergyConfigFingerprint
|
||||
.match(energyConfigToStringFingerprint.classDef)
|
||||
.method.apply {
|
||||
val energyField = energyConfigToStringFingerprint.method
|
||||
.findFieldFromToString("energy=")
|
||||
val insertIndex = initializeEnergyConfigFingerprint.patternMatch!!.startIndex
|
||||
|
||||
addInstructions(
|
||||
insertIndex,
|
||||
"""
|
||||
const/16 v0, 99
|
||||
iput v0, p0, $energyField
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ val spoofFeaturesPatch = bytecodePatch(
|
||||
"com.google.android.feature.PIXEL_2024_MIDYEAR_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2024_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2025_MIDYEAR_EXPERIENCE",
|
||||
"com.google.android.feature.PIXEL_2025_EXPERIENCE",
|
||||
),
|
||||
title = "Features to disable",
|
||||
description = "Google Pixel exclusive features to disable." +
|
||||
|
||||
@@ -3,7 +3,9 @@ package app.revanced.patches.instagram.hide.explore
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal const val EXPLORE_KEY_TO_BE_HIDDEN = "sectional_items"
|
||||
|
||||
internal val exploreResponseJsonParserFingerprint = fingerprint {
|
||||
strings("sectional_items", "ExploreTopicalFeedResponse")
|
||||
strings(EXPLORE_KEY_TO_BE_HIDDEN, "ExploreTopicalFeedResponse")
|
||||
custom { method, _ -> method.name == "parseFromJson" }
|
||||
}
|
||||
|
||||
@@ -1,33 +1,39 @@
|
||||
package app.revanced.patches.instagram.hide.explore
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
context(BytecodePatchContext)
|
||||
internal fun Fingerprint.replaceJsonFieldWithBogus(
|
||||
key: String,
|
||||
) {
|
||||
val targetStringIndex = stringMatches!!.first { match -> match.string == key }.index
|
||||
val targetStringRegister = method.getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
|
||||
|
||||
/**
|
||||
* Replacing the JSON key we want to skip with a random string that is not a valid JSON key.
|
||||
* This way the feeds array will never be populated.
|
||||
* Received JSON keys that are not handled are simply ignored, so there are no side effects.
|
||||
*/
|
||||
method.replaceInstruction(
|
||||
targetStringIndex,
|
||||
"const-string v$targetStringRegister, \"BOGUS\"",
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
val hideExportFeedPatch = bytecodePatch(
|
||||
val hideExploreFeedPatch = bytecodePatch(
|
||||
name = "Hide explore feed",
|
||||
description = "Hides posts and reels from the explore/search page.",
|
||||
use = false
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.instagram.android")
|
||||
|
||||
execute {
|
||||
exploreResponseJsonParserFingerprint.method.apply {
|
||||
val sectionalItemStringIndex = exploreResponseJsonParserFingerprint.stringMatches!!.first().index
|
||||
val sectionalItemStringRegister = getInstruction<OneRegisterInstruction>(sectionalItemStringIndex).registerA
|
||||
|
||||
/**
|
||||
* Replacing the JSON key we want to skip with a random string that is not a valid JSON key.
|
||||
* This way the feeds array will never be populated.
|
||||
* Received JSON keys that are not handled are simply ignored, so there are no side effects.
|
||||
*/
|
||||
replaceInstruction(
|
||||
sectionalItemStringIndex,
|
||||
"const-string v$sectionalItemStringRegister, \"BOGUS\""
|
||||
)
|
||||
}
|
||||
exploreResponseJsonParserFingerprint.replaceJsonFieldWithBogus(EXPLORE_KEY_TO_BE_HIDDEN)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ package app.revanced.patches.instagram.hide.navigation
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val initializeNavigationButtonsListFingerprint = fingerprint {
|
||||
strings("Nav3")
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameters("Lcom/instagram/common/session/UserSession;", "Z")
|
||||
returns("Ljava/util/List;")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.instagram.hide.suggestions
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal val FEED_ITEM_KEYS_TO_BE_HIDDEN = arrayOf(
|
||||
"clips_netego",
|
||||
"stories_netego",
|
||||
"in_feed_survey",
|
||||
"bloks_netego",
|
||||
"suggested_igd_channels",
|
||||
"suggested_top_accounts",
|
||||
"suggested_users",
|
||||
)
|
||||
|
||||
internal val feedItemParseFromJsonFingerprint = fingerprint {
|
||||
strings(*FEED_ITEM_KEYS_TO_BE_HIDDEN, "FeedItem")
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package app.revanced.patches.instagram.hide.suggestions
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.instagram.hide.explore.replaceJsonFieldWithBogus
|
||||
|
||||
@Suppress("unused")
|
||||
val hideSuggestedContent = bytecodePatch(
|
||||
name = "Hide suggested content",
|
||||
description = "Hides suggested stories, reels, threads and survey from feed (Suggested posts will still be shown).",
|
||||
use = false,
|
||||
) {
|
||||
compatibleWith("com.instagram.android")
|
||||
|
||||
execute {
|
||||
FEED_ITEM_KEYS_TO_BE_HIDDEN.forEach { key ->
|
||||
feedItemParseFromJsonFingerprint.replaceJsonFieldWithBogus(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ val enableDeveloperMenuPatch = bytecodePatch(
|
||||
with(clearNotificationReceiverFingerprint.method) {
|
||||
indexOfFirstInstructionReversedOrThrow(clearNotificationReceiverFingerprint.stringMatches!!.first().index) {
|
||||
val reference = getReference<MethodReference>()
|
||||
Opcode.INVOKE_STATIC == opcode &&
|
||||
opcode in listOf(Opcode.INVOKE_STATIC, Opcode.INVOKE_STATIC_RANGE) &&
|
||||
reference?.parameterTypes?.size == 1 &&
|
||||
reference.parameterTypes.first() == "Lcom/instagram/common/session/UserSession;" &&
|
||||
reference.returnType == "Z"
|
||||
|
||||
@@ -19,14 +19,14 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
||||
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
execute {
|
||||
val customDomainHost by stringOption(
|
||||
key = "domainName",
|
||||
default = "imginn.com",
|
||||
title = "Domain name",
|
||||
description = "The domain name to use when sharing links."
|
||||
)
|
||||
val customDomainHost by stringOption(
|
||||
key = "domainName",
|
||||
default = "imginn.com",
|
||||
title = "Domain name",
|
||||
description = "The domain name to use when sharing links."
|
||||
)
|
||||
|
||||
execute {
|
||||
getCustomShareDomainFingerprint.method.returnEarly(customDomainHost!!)
|
||||
|
||||
editShareLinksPatch { index, register ->
|
||||
|
||||
@@ -4,6 +4,11 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.misc.gms.Constants.MUSIC_MAIN_ACTIVITY_NAME
|
||||
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
|
||||
import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint
|
||||
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.shared.layout.branding.baseCustomBrandingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
@@ -50,27 +55,21 @@ private val disableSplashAnimationPatch = bytecodePatch {
|
||||
}
|
||||
}
|
||||
|
||||
private const val APP_NAME = "YT Music ReVanced"
|
||||
|
||||
@Suppress("unused")
|
||||
val customBrandingPatch = baseCustomBrandingPatch(
|
||||
defaultAppName = APP_NAME,
|
||||
appNameValues = mapOf(
|
||||
"YT Music ReVanced" to APP_NAME,
|
||||
"Music ReVanced" to "Music ReVanced",
|
||||
"Music" to "Music",
|
||||
"YT Music" to "YT Music",
|
||||
),
|
||||
resourceFolder = "custom-branding/music",
|
||||
iconResourceFileNames = arrayOf(
|
||||
"adaptiveproduct_youtube_music_2024_q4_background_color_108",
|
||||
"adaptiveproduct_youtube_music_2024_q4_foreground_color_108",
|
||||
"ic_launcher_release",
|
||||
),
|
||||
monochromeIconFileNames = arrayOf("ic_app_icons_themed_youtube_music.xml"),
|
||||
addResourcePatchName = "music",
|
||||
originalLauncherIconName = "ic_launcher_release",
|
||||
originalAppName = "@string/app_launcher_name",
|
||||
originalAppPackageName = MUSIC_PACKAGE_NAME,
|
||||
isYouTubeMusic = true,
|
||||
numberOfPresetAppNames = 5,
|
||||
mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint,
|
||||
mainActivityName = MUSIC_MAIN_ACTIVITY_NAME,
|
||||
activityAliasNameWithIntents = MUSIC_MAIN_ACTIVITY_NAME,
|
||||
preferenceScreen = PreferenceScreen.GENERAL,
|
||||
|
||||
block = {
|
||||
dependsOn(disableSplashAnimationPatch)
|
||||
dependsOn(sharedExtensionPatch, disableSplashAnimationPatch)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.apps.youtube.music"(
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
package app.revanced.patches.music.layout.compactheader
|
||||
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
|
||||
internal val constructCategoryBarFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
internal val chipCloudFingerprint = fingerprint {
|
||||
returns("V")
|
||||
parameters("Landroid/content/Context;", "L", "L", "L")
|
||||
opcodes(
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.CONST,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.IPUT_OBJECT,
|
||||
Opcode.CONST,
|
||||
Opcode.INVOKE_VIRTUAL
|
||||
Opcode.CONST_4,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
)
|
||||
literal { chipCloud }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package app.revanced.patches.music.layout.compactheader
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
@@ -8,10 +8,14 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.util.findFreeRegister
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
internal var chipCloud = -1L
|
||||
private set
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/HideCategoryBarPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
@@ -33,28 +37,21 @@ val hideCategoryBar = bytecodePatch(
|
||||
)
|
||||
|
||||
execute {
|
||||
chipCloud = resourceMappings["layout", "chip_cloud"]
|
||||
|
||||
addResources("music", "layout.compactheader.hideCategoryBar")
|
||||
|
||||
PreferenceScreen.GENERAL.addPreferences(
|
||||
SwitchPreference("revanced_music_hide_category_bar"),
|
||||
)
|
||||
|
||||
constructCategoryBarFingerprint.method.apply {
|
||||
val insertIndex = constructCategoryBarFingerprint.patternMatch!!.startIndex
|
||||
val register = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
|
||||
val freeRegister = findFreeRegister(insertIndex, register)
|
||||
chipCloudFingerprint.method.apply {
|
||||
val targetIndex = chipCloudFingerprint.patternMatch!!.endIndex
|
||||
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex,
|
||||
"""
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->hideCategoryBar()Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :show
|
||||
const/16 v$freeRegister, 0x8
|
||||
invoke-virtual { v$register, v$freeRegister }, Landroid/view/View;->setVisibility(I)V
|
||||
:show
|
||||
nop
|
||||
"""
|
||||
addInstruction(
|
||||
targetIndex + 1,
|
||||
"invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->hideCategoryBar(Landroid/view/View;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package app.revanced.patches.music.misc.gms
|
||||
|
||||
object Constants {
|
||||
internal const val MUSIC_MAIN_ACTIVITY_NAME = "com.google.android.apps.youtube.music.activities.MusicActivity"
|
||||
|
||||
internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music"
|
||||
internal const val MUSIC_PACKAGE_NAME = "com.google.android.apps.youtube.music"
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint
|
||||
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||
import app.revanced.patches.music.playservice.is_7_16_or_greater
|
||||
import app.revanced.patches.music.playservice.is_7_33_or_greater
|
||||
import app.revanced.patches.music.playservice.is_8_11_or_greater
|
||||
import app.revanced.patches.music.playservice.is_8_15_or_greater
|
||||
@@ -18,7 +19,7 @@ import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
|
||||
val spoofVideoStreamsPatch = spoofVideoStreamsPatch(
|
||||
extensionClassDescriptor = "Lapp/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch;",
|
||||
mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint,
|
||||
fixMediaFetchHotConfig = { true },
|
||||
fixMediaFetchHotConfig = { is_7_16_or_greater },
|
||||
fixMediaFetchHotConfigAlternative = { is_8_11_or_greater && !is_8_15_or_greater },
|
||||
fixParsePlaybackResponseFeatureFlag = { is_7_33_or_greater },
|
||||
|
||||
|
||||
@@ -3,14 +3,11 @@ package app.revanced.patches.music.misc.tracks
|
||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||
import app.revanced.patches.music.playservice.is_8_10_or_greater
|
||||
import app.revanced.patches.music.playservice.is_8_05_or_greater
|
||||
import app.revanced.patches.music.playservice.versionCheckPatch
|
||||
import app.revanced.patches.music.shared.mainActivityOnCreateFingerprint
|
||||
import app.revanced.patches.shared.misc.audio.forceOriginalAudioPatch
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/patches/ForceOriginalAudioPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val forceOriginalAudioPatch = forceOriginalAudioPatch(
|
||||
block = {
|
||||
@@ -27,8 +24,8 @@ val forceOriginalAudioPatch = forceOriginalAudioPatch(
|
||||
)
|
||||
)
|
||||
},
|
||||
fixUseLocalizedAudioTrackFlag = is_8_10_or_greater,
|
||||
fixUseLocalizedAudioTrackFlag = { is_8_05_or_greater },
|
||||
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
|
||||
subclassExtensionClassDescriptor = EXTENSION_CLASS_DESCRIPTOR,
|
||||
subclassExtensionClassDescriptor = "Lapp/revanced/extension/music/patches/ForceOriginalAudioPatch;",
|
||||
preferenceScreen = PreferenceScreen.MISC,
|
||||
)
|
||||
|
||||
@@ -4,14 +4,21 @@ package app.revanced.patches.music.playservice
|
||||
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.util.findPlayStoreServicesVersion
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
var is_7_33_or_greater = false
|
||||
// Use notNull delegate so an exception is thrown if these fields are accessed before they are set.
|
||||
|
||||
var is_7_16_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_8_10_or_greater = false
|
||||
var is_7_33_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_8_11_or_greater = false
|
||||
var is_8_05_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_8_15_or_greater = false
|
||||
var is_8_10_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_8_11_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_8_15_or_greater: Boolean by Delegates.notNull()
|
||||
private set
|
||||
|
||||
val versionCheckPatch = resourcePatch(
|
||||
@@ -23,8 +30,10 @@ val versionCheckPatch = resourcePatch(
|
||||
val playStoreServicesVersion = findPlayStoreServicesVersion()
|
||||
|
||||
// All bug fix releases always seem to use the same play store version as the minor version.
|
||||
is_7_16_or_greater = 243499000 <= playStoreServicesVersion
|
||||
is_7_33_or_greater = 245199000 <= playStoreServicesVersion
|
||||
is_8_10_or_greater = 244799000 <= playStoreServicesVersion
|
||||
is_8_05_or_greater = 250599000 <= playStoreServicesVersion
|
||||
is_8_10_or_greater = 251099000 <= playStoreServicesVersion
|
||||
is_8_11_or_greater = 251199000 <= playStoreServicesVersion
|
||||
is_8_15_or_greater = 251530000 <= playStoreServicesVersion
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package app.revanced.patches.samsung.radio.misc.fix.crash
|
||||
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.util.asSequence
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Suppress("unused")
|
||||
internal val addManifestPermissionsPatch = resourcePatch {
|
||||
|
||||
val requiredPermissions = listOf(
|
||||
"android.permission.READ_PHONE_STATE",
|
||||
"android.permission.FOREGROUND_SERVICE_MICROPHONE",
|
||||
"android.permission.RECORD_AUDIO",
|
||||
)
|
||||
|
||||
execute {
|
||||
document("AndroidManifest.xml").use { document ->
|
||||
document.getElementsByTagName("manifest").item(0).let { manifestEl ->
|
||||
|
||||
// Check which permissions are missing
|
||||
val existingPermissionNames = document.getElementsByTagName("uses-permission").asSequence()
|
||||
.mapNotNull { (it as? Element)?.getAttribute("android:name") }.toSet()
|
||||
val missingPermissions = requiredPermissions.filterNot { it in existingPermissionNames }
|
||||
|
||||
// Then add them
|
||||
for (permission in missingPermissions) {
|
||||
val element = document.createElement("uses-permission")
|
||||
element.setAttribute("android:name", permission)
|
||||
manifestEl.appendChild(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package app.revanced.patches.samsung.radio.misc.fix.crash
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patches.all.misc.transformation.IMethodCall
|
||||
import app.revanced.patches.all.misc.transformation.fromMethodReference
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val permissionRequestListFingerprint = fingerprint {
|
||||
strings(
|
||||
"android.permission.POST_NOTIFICATIONS",
|
||||
"android.permission.READ_MEDIA_AUDIO",
|
||||
"android.permission.RECORD_AUDIO"
|
||||
)
|
||||
custom { method, _ -> method.name == "<clinit>" }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package app.revanced.patches.samsung.radio.misc.fix.crash
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.samsung.radio.restrictions.device.bypassDeviceChecksPatch
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch;"
|
||||
|
||||
val fixCrashPatch = bytecodePatch(
|
||||
name = "Fix crashes", description = "Prevents the app from crashing because of missing system permissions."
|
||||
) {
|
||||
dependsOn(addManifestPermissionsPatch, bypassDeviceChecksPatch)
|
||||
extendWith("extensions/samsung/radio.rve")
|
||||
compatibleWith("com.sec.android.app.fm"("12.4.00.7", "12.3.00.13", "12.3.00.11"))
|
||||
|
||||
execute {
|
||||
permissionRequestListFingerprint.method.apply {
|
||||
findInstructionIndicesReversedOrThrow(Opcode.FILLED_NEW_ARRAY).forEach { filledNewArrayIndex ->
|
||||
val moveResultIndex = indexOfFirstInstruction(filledNewArrayIndex, Opcode.MOVE_RESULT_OBJECT)
|
||||
if (moveResultIndex < 0) return@forEach // No move-result-object found after the filled-new-array
|
||||
|
||||
// Get the register where the array is saved
|
||||
val arrayRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
|
||||
|
||||
// Invoke the method from the extension
|
||||
addInstructions(
|
||||
moveResultIndex + 1, """
|
||||
invoke-static { v$arrayRegister }, ${EXTENSION_CLASS_DESCRIPTOR}->fixPermissionRequestList([Ljava/lang/String;)[Ljava/lang/String;
|
||||
move-result-object v$arrayRegister
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package app.revanced.patches.samsung.radio.restrictions.device
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/samsung/radio/restrictions/device/BypassDeviceChecksPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val bypassDeviceChecksPatch = bytecodePatch(
|
||||
name = "Bypass device checks",
|
||||
description = "Removes firmware and region blacklisting. " +
|
||||
"This patch will still not allow the app to run on devices that do not have the required hardware.",
|
||||
) {
|
||||
extendWith("extensions/samsung/radio.rve")
|
||||
compatibleWith("com.sec.android.app.fm"("12.4.00.7", "12.3.00.13", "12.3.00.11"))
|
||||
|
||||
execute {
|
||||
// Return false = The device is not blacklisted
|
||||
checkDeviceFingerprint.method.apply {
|
||||
// Find the first string that start with "SM-", that's the list of incompatible devices
|
||||
val firstStringIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.CONST_STRING &&
|
||||
getReference<StringReference>()?.string?.startsWith("SM-") == true
|
||||
}
|
||||
|
||||
// Find the following filled-new-array (or filled-new-array/range) instruction
|
||||
val filledNewArrayIndex = indexOfFirstInstructionOrThrow(firstStringIndex + 1) {
|
||||
opcode == Opcode.FILLED_NEW_ARRAY || opcode == Opcode.FILLED_NEW_ARRAY_RANGE
|
||||
}
|
||||
|
||||
// Find an available register for our use
|
||||
val resultRegister = findFreeRegister(filledNewArrayIndex + 1)
|
||||
|
||||
// Store the array there and invoke the method that we added to the class earlier
|
||||
addInstructions(
|
||||
filledNewArrayIndex + 1, """
|
||||
move-result-object v$resultRegister
|
||||
invoke-static { v$resultRegister }, $EXTENSION_CLASS_DESCRIPTOR->checkIfDeviceIsIncompatible([Ljava/lang/String;)Z
|
||||
move-result v$resultRegister
|
||||
return v$resultRegister
|
||||
"""
|
||||
)
|
||||
|
||||
// Remove the instructions before our strings
|
||||
removeInstructions(0, firstStringIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package app.revanced.patches.samsung.radio.restrictions.device
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patches.all.misc.transformation.IMethodCall
|
||||
import app.revanced.patches.all.misc.transformation.fromMethodReference
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val checkDeviceFingerprint = fingerprint {
|
||||
returns("Z")
|
||||
custom { method, _ ->
|
||||
/* Check for methods call to:
|
||||
- Landroid/os/SemSystemProperties;->getSalesCode()Ljava/lang/String;
|
||||
- Landroid/os/SemSystemProperties;->getCountryIso()Ljava/lang/String;
|
||||
*/
|
||||
|
||||
val impl = method.implementation ?: return@custom false
|
||||
|
||||
// Track which target methods we've found
|
||||
val foundMethods = mutableSetOf<MethodCall>()
|
||||
|
||||
// Scan method instructions for calls to our target methods
|
||||
for (instr in impl.instructions) {
|
||||
val ref = instr.getReference<MethodReference>() ?: continue
|
||||
val mc = fromMethodReference<MethodCall>(ref) ?: continue
|
||||
|
||||
if (mc == MethodCall.GetSalesCode || mc == MethodCall.GetCountryIso) {
|
||||
foundMethods.add(mc)
|
||||
|
||||
// If we found both methods, return success
|
||||
if (foundMethods.size == 2) {
|
||||
return@custom true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only match if both methods are present
|
||||
return@custom false
|
||||
}
|
||||
}
|
||||
|
||||
// Information about method calls we want to replace
|
||||
private enum class MethodCall(
|
||||
override val definedClassName: String,
|
||||
override val methodName: String,
|
||||
override val methodParams: Array<String>,
|
||||
override val returnType: String,
|
||||
) : IMethodCall {
|
||||
GetSalesCode(
|
||||
"Landroid/os/SemSystemProperties;",
|
||||
"getSalesCode",
|
||||
arrayOf(),
|
||||
"Ljava/lang/String;",
|
||||
),
|
||||
GetCountryIso(
|
||||
"Landroid/os/SemSystemProperties;",
|
||||
"getCountryIso",
|
||||
arrayOf(),
|
||||
"Ljava/lang/String;",
|
||||
),
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package app.revanced.patches.shared.layout.branding
|
||||
|
||||
import app.revanced.patcher.patch.rawResourcePatch
|
||||
import app.revanced.util.inputStreamFromBundledResource
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Copies a branding license text file to the target apk.
|
||||
*
|
||||
* This patch must be a dependency for all patches that add ReVanced branding to the target app.
|
||||
*/
|
||||
internal val addBrandLicensePatch = rawResourcePatch {
|
||||
execute {
|
||||
val brandingLicenseFileName = "LICENSE_REVANCED.TXT"
|
||||
|
||||
val inputFileStream = inputStreamFromBundledResource(
|
||||
"branding-license",
|
||||
brandingLicenseFileName
|
||||
)!!
|
||||
|
||||
val targetFile = get(brandingLicenseFileName, false).toPath()
|
||||
|
||||
Files.copy(inputFileStream, targetFile)
|
||||
}
|
||||
}
|
||||
@@ -1,173 +1,467 @@
|
||||
package app.revanced.patches.shared.layout.branding
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.ResourcePatchBuilder
|
||||
import app.revanced.patcher.patch.ResourcePatchContext
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
|
||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.Utils.trimIndentMultiline
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import app.revanced.util.removeFromParent
|
||||
import app.revanced.util.returnEarly
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.NodeList
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.util.logging.Logger
|
||||
|
||||
private const val REVANCED_ICON = "ReVanced*Logo" // Can never be a valid path.
|
||||
|
||||
internal val mipmapDirectories = arrayOf(
|
||||
private val mipmapDirectories = mapOf(
|
||||
// Target app does not have ldpi icons.
|
||||
"mdpi",
|
||||
"hdpi",
|
||||
"xhdpi",
|
||||
"xxhdpi",
|
||||
"xxxhdpi",
|
||||
).map { "mipmap-$it" }.toTypedArray()
|
||||
"mipmap-mdpi" to "108x108 px",
|
||||
"mipmap-hdpi" to "162x162 px",
|
||||
"mipmap-xhdpi" to "216x216 px",
|
||||
"mipmap-xxhdpi" to "324x324 px",
|
||||
"mipmap-xxxhdpi" to "432x432 px"
|
||||
)
|
||||
|
||||
private fun formatResourceFileList(resourceNames: Array<String>) = resourceNames.joinToString("\n") { "- $it" }
|
||||
private val iconStyleNames = arrayOf(
|
||||
"rounded",
|
||||
"minimal",
|
||||
"scaled"
|
||||
)
|
||||
|
||||
/**
|
||||
* Attempts to fix unescaped and invalid characters not allowed for an Android app name.
|
||||
*/
|
||||
private fun escapeAppName(name: String): String? {
|
||||
// Remove ASCII control characters.
|
||||
val cleanedName = name.filter { it.code >= 32 }
|
||||
private const val ORIGINAL_USER_ICON_STYLE_NAME = "original"
|
||||
private const val CUSTOM_USER_ICON_STYLE_NAME = "custom"
|
||||
|
||||
// Replace invalid XML characters with escaped equivalents.
|
||||
val escapedName = cleanedName
|
||||
.replace("&", "&") // Must be first to avoid double-escaping.
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace(Regex("(?<!&)\""), """)
|
||||
private const val LAUNCHER_RESOURCE_NAME_PREFIX = "revanced_launcher_"
|
||||
private const val LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX = "revanced_adaptive_background_"
|
||||
private const val LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX = "revanced_adaptive_foreground_"
|
||||
private const val LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX = "revanced_adaptive_monochrome_"
|
||||
private const val NOTIFICATION_ICON_NAME = "revanced_notification_icon"
|
||||
|
||||
// Trim empty spacing.
|
||||
val trimmed = escapedName.trim()
|
||||
private val USER_CUSTOM_ADAPTIVE_FILE_NAMES = arrayOf(
|
||||
"$LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png",
|
||||
"$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png"
|
||||
)
|
||||
|
||||
return trimmed.ifBlank { null }
|
||||
}
|
||||
private const val USER_CUSTOM_MONOCHROME_FILE_NAME = "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml"
|
||||
private const val USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME = "${NOTIFICATION_ICON_NAME}_$CUSTOM_USER_ICON_STYLE_NAME.xml"
|
||||
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/CustomBrandingPatch;"
|
||||
|
||||
/**
|
||||
* Shared custom branding patch for YouTube and YT Music.
|
||||
*/
|
||||
internal fun baseCustomBrandingPatch(
|
||||
defaultAppName: String,
|
||||
appNameValues: Map<String, String>,
|
||||
resourceFolder: String,
|
||||
iconResourceFileNames: Array<String>,
|
||||
monochromeIconFileNames: Array<String>,
|
||||
block: ResourcePatchBuilder.() -> Unit = {},
|
||||
addResourcePatchName: String,
|
||||
originalLauncherIconName: String,
|
||||
originalAppName: String,
|
||||
originalAppPackageName: String,
|
||||
isYouTubeMusic: Boolean,
|
||||
numberOfPresetAppNames: Int,
|
||||
mainActivityOnCreateFingerprint: Fingerprint,
|
||||
mainActivityName: String,
|
||||
activityAliasNameWithIntents: String,
|
||||
preferenceScreen: BasePreferenceScreen.Screen,
|
||||
block: ResourcePatchBuilder.() -> Unit,
|
||||
executeBlock: ResourcePatchContext.() -> Unit = {}
|
||||
): ResourcePatch = resourcePatch(
|
||||
name = "Custom branding",
|
||||
description = "Applies a custom app name and icon. Defaults to \"$defaultAppName\" and the ReVanced logo.",
|
||||
use = false,
|
||||
description = "Adds options to change the app icon and app name. " +
|
||||
"Branding cannot be changed for mounted (root) installations."
|
||||
) {
|
||||
val iconResourceFileNamesPng = iconResourceFileNames.map { "$it.png" }.toTypedArray<String>()
|
||||
|
||||
val appName by stringOption(
|
||||
key = "appName",
|
||||
default = defaultAppName,
|
||||
values = appNameValues,
|
||||
val customName by stringOption(
|
||||
key = "customName",
|
||||
title = "App name",
|
||||
description = "The name of the app.",
|
||||
description = "Custom app name."
|
||||
)
|
||||
|
||||
val iconPath by stringOption(
|
||||
key = "iconPath",
|
||||
default = REVANCED_ICON,
|
||||
values = mapOf("ReVanced Logo" to REVANCED_ICON),
|
||||
title = "App icon",
|
||||
val customIcon by stringOption(
|
||||
key = "customIcon",
|
||||
title = "Custom icon",
|
||||
description = """
|
||||
The icon to apply to the app.
|
||||
Folder with images to use as a custom icon.
|
||||
|
||||
If a path to a folder is provided, the folder must contain the following folders:
|
||||
|
||||
${formatResourceFileList(mipmapDirectories)}
|
||||
|
||||
Each of these folders must contain the following files:
|
||||
|
||||
${formatResourceFileList(iconResourceFileNamesPng)}
|
||||
The folder must contain one or more of the following folders, depending on the DPI of the device:
|
||||
${mipmapDirectories.keys.joinToString("\n") { "- $it" }}
|
||||
|
||||
Optionally, a 'drawable' folder with the monochrome icon files:
|
||||
|
||||
${formatResourceFileList(monochromeIconFileNames)}
|
||||
""".trimIndentMultiline(),
|
||||
Each of the folders must contain all of the following files:
|
||||
${USER_CUSTOM_ADAPTIVE_FILE_NAMES.joinToString("\n")}
|
||||
|
||||
The image dimensions must be as follows:
|
||||
${mipmapDirectories.map { (dpi, dim) -> "- $dpi: $dim" }.joinToString("\n")}
|
||||
|
||||
Optionally, the path contains a 'drawable' folder with any of the monochrome icon files:
|
||||
$USER_CUSTOM_MONOCHROME_FILE_NAME
|
||||
$USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME
|
||||
""".trimIndentMultiline()
|
||||
)
|
||||
|
||||
block()
|
||||
|
||||
dependsOn(
|
||||
addResourcesPatch,
|
||||
resourceMappingPatch,
|
||||
addBrandLicensePatch,
|
||||
bytecodePatch {
|
||||
execute {
|
||||
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||
0,
|
||||
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setBranding()V"
|
||||
)
|
||||
|
||||
numberOfPresetAppNamesExtensionFingerprint.method.returnEarly(numberOfPresetAppNames)
|
||||
|
||||
notificationFingerprint.method.apply {
|
||||
val getBuilderIndex = if (isYouTubeMusic) {
|
||||
// YT Music the field is not a plain object type.
|
||||
indexOfFirstInstructionOrThrow {
|
||||
getReference<FieldReference>()?.type == "Landroid/app/Notification\$Builder;"
|
||||
}
|
||||
} else {
|
||||
// Find the field name of the notification builder. Field is an Object type.
|
||||
val builderCastIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<TypeReference>()
|
||||
opcode == Opcode.CHECK_CAST &&
|
||||
reference?.type == "Landroid/app/Notification\$Builder;"
|
||||
}
|
||||
indexOfFirstInstructionReversedOrThrow(builderCastIndex) {
|
||||
getReference<FieldReference>()?.type == "Ljava/lang/Object;"
|
||||
}
|
||||
}
|
||||
|
||||
val builderFieldName = getInstruction<ReferenceInstruction>(getBuilderIndex)
|
||||
.getReference<FieldReference>()
|
||||
|
||||
findInstructionIndicesReversedOrThrow(
|
||||
Opcode.RETURN_VOID
|
||||
).forEach { index ->
|
||||
addInstructionsAtControlFlowLabel(
|
||||
index,
|
||||
"""
|
||||
move-object/from16 v0, p0
|
||||
iget-object v0, v0, $builderFieldName
|
||||
check-cast v0, Landroid/app/Notification${'$'}Builder;
|
||||
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification${'$'}Builder;)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
finalize {
|
||||
// Can only check if app is root installation by checking if change package name patch is in use.
|
||||
// and can only do that in the finalize block here.
|
||||
// The UI preferences cannot be selectively added here, because the settings finalize block
|
||||
// may have already run and the settings are already wrote to file.
|
||||
// Instead, show a warning if any patch option was used (A rooted device launcher ignores the manifest changes),
|
||||
// and the non-functional in-app settings are removed on app startup by extension code.
|
||||
if (customName != null || customIcon != null) {
|
||||
if (setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName) {
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"Custom branding does not work with root installation. No changes applied."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
execute {
|
||||
val mipmapIconResourceGroups = mipmapDirectories.map { directory ->
|
||||
ResourceGroup(
|
||||
directory,
|
||||
*iconResourceFileNamesPng,
|
||||
addResources("shared", "layout.branding.baseCustomBrandingPatch")
|
||||
addResources(addResourcePatchName, "layout.branding.customBrandingPatch")
|
||||
|
||||
preferenceScreen.addPreferences(
|
||||
if (customName != null ) {
|
||||
ListPreference(
|
||||
key = "revanced_custom_branding_name",
|
||||
entriesKey = "revanced_custom_branding_name_custom_entries",
|
||||
entryValuesKey = "revanced_custom_branding_name_custom_entry_values"
|
||||
)
|
||||
} else {
|
||||
ListPreference("revanced_custom_branding_name")
|
||||
},
|
||||
if (customIcon != null) {
|
||||
ListPreference(
|
||||
key = "revanced_custom_branding_icon",
|
||||
entriesKey = "revanced_custom_branding_icon_custom_entries",
|
||||
entryValuesKey = "revanced_custom_branding_icon_custom_entry_values"
|
||||
)
|
||||
} else {
|
||||
ListPreference("revanced_custom_branding_icon")
|
||||
}
|
||||
)
|
||||
|
||||
val useCustomName = customName != null
|
||||
val useCustomIcon = customIcon != null
|
||||
|
||||
iconStyleNames.forEach { style ->
|
||||
copyResources(
|
||||
"custom-branding",
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
"$LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX$style.xml",
|
||||
"$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$style.xml",
|
||||
"$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$style.xml",
|
||||
),
|
||||
ResourceGroup(
|
||||
"mipmap-anydpi",
|
||||
"$LAUNCHER_RESOURCE_NAME_PREFIX$style.xml"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val iconPathTrimmed = iconPath!!.trim()
|
||||
if (iconPathTrimmed == REVANCED_ICON) {
|
||||
// Replace mipmap icons with preset patch icons.
|
||||
mipmapIconResourceGroups.forEach { groupResources ->
|
||||
copyResources(resourceFolder, groupResources)
|
||||
copyResources(
|
||||
"custom-branding",
|
||||
// Push notification 'small' icon.
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
"$NOTIFICATION_ICON_NAME.xml"
|
||||
),
|
||||
|
||||
// Copy template user icon, because the aliases must be added even if no user icon is provided.
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
USER_CUSTOM_MONOCHROME_FILE_NAME,
|
||||
USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME
|
||||
),
|
||||
ResourceGroup(
|
||||
"mipmap-anydpi",
|
||||
"$LAUNCHER_RESOURCE_NAME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml"
|
||||
)
|
||||
)
|
||||
|
||||
// Copy template icon files.
|
||||
mipmapDirectories.keys.forEach { dpi ->
|
||||
copyResources(
|
||||
"custom-branding",
|
||||
ResourceGroup(
|
||||
dpi,
|
||||
"$LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png",
|
||||
"$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
document("AndroidManifest.xml").use { document ->
|
||||
// Create launch aliases that can be programmatically selected in app.
|
||||
fun createAlias(
|
||||
aliasName: String,
|
||||
iconMipmapName: String,
|
||||
appNameIndex: Int,
|
||||
useCustomName: Boolean,
|
||||
enabled: Boolean,
|
||||
intents: NodeList
|
||||
): Element {
|
||||
val label = if (useCustomName) {
|
||||
if (customName == null) {
|
||||
"Custom" // Dummy text, and normally cannot be seen.
|
||||
} else {
|
||||
customName!!
|
||||
}
|
||||
} else if (appNameIndex == 1) {
|
||||
// Indexing starts at 1.
|
||||
originalAppName
|
||||
} else {
|
||||
"@string/revanced_custom_branding_name_entry_$appNameIndex"
|
||||
}
|
||||
val alias = document.createElement("activity-alias")
|
||||
alias.setAttribute("android:name", aliasName)
|
||||
alias.setAttribute("android:enabled", enabled.toString())
|
||||
alias.setAttribute("android:exported", "true")
|
||||
alias.setAttribute("android:icon", "@mipmap/$iconMipmapName")
|
||||
alias.setAttribute("android:label", label)
|
||||
alias.setAttribute("android:targetActivity", mainActivityName)
|
||||
|
||||
// Copy all intents from the original alias so long press actions still work.
|
||||
if (isYouTubeMusic) {
|
||||
val intentFilter = document.createElement("intent-filter").apply {
|
||||
val action = document.createElement("action")
|
||||
action.setAttribute("android:name", "android.intent.action.MAIN")
|
||||
appendChild(action)
|
||||
|
||||
val category = document.createElement("category")
|
||||
category.setAttribute("android:name", "android.intent.category.LAUNCHER")
|
||||
appendChild(category)
|
||||
}
|
||||
alias.appendChild(intentFilter)
|
||||
} else {
|
||||
for (i in 0 until intents.length) {
|
||||
alias.appendChild(
|
||||
intents.item(i).cloneNode(true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return alias
|
||||
}
|
||||
|
||||
// Replace monochrome icons.
|
||||
monochromeIconFileNames.forEach { fileName ->
|
||||
copyResources(
|
||||
resourceFolder,
|
||||
ResourceGroup("drawable", fileName)
|
||||
val application = document.getElementsByTagName("application").item(0) as Element
|
||||
val intentFilters = document.childNodes.findElementByAttributeValueOrThrow(
|
||||
"android:name",
|
||||
activityAliasNameWithIntents
|
||||
).childNodes
|
||||
|
||||
// The YT application name can appear in some places along side the system
|
||||
// YouTube app, such as the settings app list and in the "open with" file picker.
|
||||
// Because the YouTube app cannot be completely uninstalled and only disabled,
|
||||
// use a custom name for this situation to disambiguate which app is which.
|
||||
application.setAttribute(
|
||||
"android:label",
|
||||
"@string/revanced_custom_branding_name_entry_2"
|
||||
)
|
||||
|
||||
for (appNameIndex in 1 .. numberOfPresetAppNames) {
|
||||
fun aliasName(name: String): String = ".revanced_" + name + '_' + appNameIndex
|
||||
|
||||
val useCustomNameLabel = (useCustomName && appNameIndex == numberOfPresetAppNames)
|
||||
|
||||
// Original icon.
|
||||
application.appendChild(
|
||||
createAlias(
|
||||
aliasName = aliasName(ORIGINAL_USER_ICON_STYLE_NAME),
|
||||
iconMipmapName = originalLauncherIconName,
|
||||
appNameIndex = appNameIndex,
|
||||
useCustomName = useCustomNameLabel,
|
||||
enabled = (appNameIndex == 1),
|
||||
intentFilters
|
||||
)
|
||||
)
|
||||
|
||||
// Bundled icons.
|
||||
iconStyleNames.forEachIndexed { index, style ->
|
||||
application.appendChild(
|
||||
createAlias(
|
||||
aliasName = aliasName(style),
|
||||
iconMipmapName = LAUNCHER_RESOURCE_NAME_PREFIX + style,
|
||||
appNameIndex = appNameIndex,
|
||||
useCustomName = useCustomNameLabel,
|
||||
enabled = false,
|
||||
intentFilters
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// User provided custom icon.
|
||||
//
|
||||
// Must add all aliases even if the user did not provide a custom icon of their own.
|
||||
// This is because if the user installs with an option, then repatches without the option,
|
||||
// the alias must still exist because if it was previously enabled and then it's removed
|
||||
// the app will become broken and cannot launch. Even if the app data is cleared
|
||||
// it still cannot be launched and the only fix is to uninstall the app.
|
||||
// To prevent this, always include all aliases and use dummy data if needed.
|
||||
application.appendChild(
|
||||
createAlias(
|
||||
aliasName = aliasName(CUSTOM_USER_ICON_STYLE_NAME),
|
||||
iconMipmapName = LAUNCHER_RESOURCE_NAME_PREFIX + CUSTOM_USER_ICON_STYLE_NAME,
|
||||
appNameIndex = appNameIndex,
|
||||
useCustomName = useCustomNameLabel,
|
||||
enabled = false,
|
||||
intentFilters
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val filePath = File(iconPathTrimmed)
|
||||
|
||||
// Remove the main action from the original alias, otherwise two apps icons
|
||||
// can be shown in the launcher. Can only be done after adding the new aliases.
|
||||
intentFilters.findElementByAttributeValueOrThrow(
|
||||
"android:name",
|
||||
"android.intent.action.MAIN"
|
||||
).removeFromParent()
|
||||
}
|
||||
|
||||
// Copy custom icons last, so if the user enters an invalid icon path
|
||||
// and an exception is thrown then the critical manifest changes are still made.
|
||||
if (useCustomIcon) {
|
||||
// Copy user provided files
|
||||
val iconPathFile = File(customIcon!!.trim())
|
||||
|
||||
if (!iconPathFile.exists()) {
|
||||
throw PatchException(
|
||||
"The custom icon path cannot be found: " + iconPathFile.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
if (!iconPathFile.isDirectory) {
|
||||
throw PatchException(
|
||||
"The custom icon path must be a folder: " + iconPathFile.absolutePath
|
||||
)
|
||||
}
|
||||
|
||||
val resourceDirectory = get("res")
|
||||
var copiedFiles = false
|
||||
|
||||
// Replace
|
||||
mipmapIconResourceGroups.forEach { groupResources ->
|
||||
val groupResourceDirectoryName = groupResources.resourceDirectoryName
|
||||
val fromDirectory = filePath.resolve(groupResourceDirectoryName)
|
||||
val toDirectory = resourceDirectory.resolve(groupResourceDirectoryName)
|
||||
// For each source folder, copy the files to the target resource directories.
|
||||
iconPathFile.listFiles {
|
||||
file -> file.isDirectory && file.name in mipmapDirectories
|
||||
}!!.forEach { dpiSourceFolder ->
|
||||
val targetDpiFolder = resourceDirectory.resolve(dpiSourceFolder.name)
|
||||
if (!targetDpiFolder.exists()) {
|
||||
// Should never happen.
|
||||
throw IllegalStateException("Resource not found: $dpiSourceFolder")
|
||||
}
|
||||
|
||||
groupResources.resources.forEach { iconFileName ->
|
||||
Files.write(
|
||||
toDirectory.resolve(iconFileName).toPath(),
|
||||
fromDirectory.resolve(iconFileName).readBytes(),
|
||||
)
|
||||
val customFiles = dpiSourceFolder.listFiles { file ->
|
||||
file.isFile && file.name in USER_CUSTOM_ADAPTIVE_FILE_NAMES
|
||||
}!!
|
||||
|
||||
if (customFiles.isNotEmpty() && customFiles.size != USER_CUSTOM_ADAPTIVE_FILE_NAMES.size) {
|
||||
throw PatchException("Must include all required icon files " +
|
||||
"but only found " + customFiles.map { it.name })
|
||||
}
|
||||
|
||||
customFiles.forEach { imgSourceFile ->
|
||||
val imgTargetFile = targetDpiFolder.resolve(imgSourceFile.name)
|
||||
imgSourceFile.copyTo(target = imgTargetFile, overwrite = true)
|
||||
|
||||
copiedFiles = true
|
||||
}
|
||||
}
|
||||
|
||||
// Copy all monochrome icons if provided.
|
||||
monochromeIconFileNames.forEach { fileName ->
|
||||
val replacementMonochrome = filePath.resolve("drawable").resolve(fileName)
|
||||
if (replacementMonochrome.exists()) {
|
||||
Files.write(
|
||||
resourceDirectory.resolve("drawable").resolve(fileName).toPath(),
|
||||
replacementMonochrome.readBytes(),
|
||||
// Copy monochrome and small notification icon if it provided.
|
||||
arrayOf(
|
||||
USER_CUSTOM_MONOCHROME_FILE_NAME,
|
||||
USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME
|
||||
).forEach { fileName ->
|
||||
val relativePath = "drawable/$fileName"
|
||||
val file = iconPathFile.resolve(relativePath)
|
||||
if (file.exists()) {
|
||||
file.copyTo(
|
||||
target = resourceDirectory.resolve(relativePath),
|
||||
overwrite = true
|
||||
)
|
||||
copiedFiles = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change the app name.
|
||||
escapeAppName(appName!!)?.let { escapedAppName ->
|
||||
val newValue = "android:label=\"$escapedAppName\""
|
||||
|
||||
val manifest = get("AndroidManifest.xml")
|
||||
val original = manifest.readText()
|
||||
val replacement = original
|
||||
// YouTube
|
||||
.replace("android:label=\"@string/application_name\"", newValue)
|
||||
// YT Music
|
||||
.replace("android:label=\"@string/app_launcher_name\"", newValue)
|
||||
|
||||
if (original == replacement) {
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"Could not replace manifest app name"
|
||||
)
|
||||
if (!copiedFiles) {
|
||||
throw PatchException("Expected to find directories and files: "
|
||||
+ USER_CUSTOM_ADAPTIVE_FILE_NAMES.contentToString()
|
||||
+ "\nBut none were found in the provided option file path: " + iconPathFile.absolutePath)
|
||||
}
|
||||
|
||||
manifest.writeText(replacement)
|
||||
}
|
||||
|
||||
executeBlock() // Must be after the main code to rename the new icons for YouTube 19.34+.
|
||||
executeBlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package app.revanced.patches.shared.layout.branding
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val numberOfPresetAppNamesExtensionFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returns("I")
|
||||
parameters()
|
||||
custom { method, classDef ->
|
||||
method.name == "numberOfPresetAppNames" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
// A much simpler fingerprint exists that can set the small icon (contains string "414843287017"),
|
||||
// but that has limited usage and this fingerprint allows changing any part of the notification.
|
||||
internal val notificationFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
parameters("L")
|
||||
strings("key_action_priority")
|
||||
}
|
||||
@@ -79,7 +79,7 @@ internal val darkThemeBackgroundColorOption = stringOption(
|
||||
*/
|
||||
internal fun baseThemePatch(
|
||||
extensionClassDescriptor: String,
|
||||
block: BytecodePatchBuilder.() -> Unit = {},
|
||||
block: BytecodePatchBuilder.() -> Unit,
|
||||
executeBlock: BytecodePatchContext.() -> Unit = {}
|
||||
) = bytecodePatch(
|
||||
name = "Theme",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package app.revanced.patches.shared.misc.audio
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val formatStreamModelToStringFingerprint = fingerprint {
|
||||
@@ -20,11 +20,7 @@ internal val formatStreamModelToStringFingerprint = fingerprint {
|
||||
internal const val AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG = 45666189L
|
||||
|
||||
internal val selectAudioStreamFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returns("L")
|
||||
custom { method, _ ->
|
||||
method.parameters.size > 2 // Method has a large number of parameters and may change.
|
||||
&& method.containsLiteralInstruction(AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG)
|
||||
literal {
|
||||
AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
internal fun forceOriginalAudioPatch(
|
||||
block: BytecodePatchBuilder.() -> Unit = {},
|
||||
executeBlock: BytecodePatchContext.() -> Unit = {},
|
||||
fixUseLocalizedAudioTrackFlag: Boolean,
|
||||
fixUseLocalizedAudioTrackFlag: BytecodePatchContext.() -> Boolean,
|
||||
mainActivityOnCreateFingerprint: Fingerprint,
|
||||
subclassExtensionClassDescriptor: String,
|
||||
preferenceScreen: BasePreferenceScreen.Screen
|
||||
@@ -64,7 +64,7 @@ internal fun forceOriginalAudioPatch(
|
||||
|
||||
// Disable feature flag that ignores the default track flag
|
||||
// and instead overrides to the user region language.
|
||||
if (fixUseLocalizedAudioTrackFlag) {
|
||||
if (fixUseLocalizedAudioTrackFlag()) {
|
||||
selectAudioStreamFingerprint.method.insertLiteralOverride(
|
||||
AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->ignoreDefaultAudioStream(Z)Z"
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
package app.revanced.patches.shared.misc.gms
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patches.shared.misc.gms.EXTENSION_CLASS_DESCRIPTOR
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
const val GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME = "getGmsCoreVendorGroupId"
|
||||
|
||||
internal val gmsCoreSupportFingerprint = fingerprint {
|
||||
custom { _, classDef ->
|
||||
classDef.endsWith("GmsCoreSupport;")
|
||||
}
|
||||
}
|
||||
|
||||
internal val googlePlayUtilityFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||
returns("I")
|
||||
@@ -28,3 +23,21 @@ internal val serviceCheckFingerprint = fingerprint {
|
||||
parameters("L", "I")
|
||||
strings("Google Play Services not available")
|
||||
}
|
||||
|
||||
internal val gmsCoreSupportFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returns("Ljava/lang/String;")
|
||||
parameters()
|
||||
custom { method, classDef ->
|
||||
method.name == "getGmsCoreVendorGroupId" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
internal val originalPackageNameExtensionFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returns("Ljava/lang/String;")
|
||||
parameters()
|
||||
custom { methodDef, classDef ->
|
||||
methodDef.name == "getOriginalPackageName" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
package app.revanced.patches.shared.misc.gms
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.*
|
||||
import app.revanced.patcher.patch.BytecodePatchBuilder
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.Option
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.ResourcePatchBuilder
|
||||
import app.revanced.patcher.patch.ResourcePatchContext
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.all.misc.packagename.changePackageNamePatch
|
||||
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
@@ -12,7 +20,8 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.gms.Constants.ACTIONS
|
||||
import app.revanced.patches.shared.misc.gms.Constants.AUTHORITIES
|
||||
import app.revanced.patches.shared.misc.gms.Constants.PERMISSIONS
|
||||
import app.revanced.util.*
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.returnEarly
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
@@ -23,6 +32,8 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/GmsCoreSupport;"
|
||||
|
||||
private const val PACKAGE_NAME_REGEX_PATTERN = "^[a-z]\\w*(\\.[a-z]\\w*)+\$"
|
||||
|
||||
/**
|
||||
@@ -201,19 +212,18 @@ fun gmsCoreSupportPatch(
|
||||
googlePlayUtilityFingerprint.method.returnEarly(0)
|
||||
}
|
||||
|
||||
// Set original and patched package names for extension to use.
|
||||
originalPackageNameExtensionFingerprint.method.returnEarly(fromPackageName)
|
||||
|
||||
// Verify GmsCore is installed and whitelisted for power optimizations and background usage.
|
||||
mainActivityOnCreateFingerprint.method.apply {
|
||||
addInstructions(
|
||||
0,
|
||||
"invoke-static/range { p0 .. p0 }, Lapp/revanced/extension/shared/GmsCoreSupport;->" +
|
||||
"checkGmsCore(Landroid/app/Activity;)V",
|
||||
)
|
||||
}
|
||||
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||
0,
|
||||
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"checkGmsCore(Landroid/app/Activity;)V"
|
||||
)
|
||||
|
||||
// Change the vendor of GmsCore in the extension.
|
||||
gmsCoreSupportFingerprint.classDef.methods
|
||||
.single { it.name == GET_GMS_CORE_VENDOR_GROUP_ID_METHOD_NAME }
|
||||
.replaceInstruction(0, "const-string v0, \"$gmsCoreVendorGroupId\"")
|
||||
gmsCoreSupportFingerprint.method.returnEarly(gmsCoreVendorGroupId!!)
|
||||
|
||||
executeBlock()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.all.misc.resources.addResource
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.layout.branding.addBrandLicensePatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.BasePreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
|
||||
@@ -61,7 +62,11 @@ fun settingsPatch (
|
||||
rootPreferences: List<Pair<BasePreference, String>>? = null,
|
||||
preferences: Set<BasePreference>,
|
||||
) = resourcePatch {
|
||||
dependsOn(addResourcesPatch, settingsColorPatch)
|
||||
dependsOn(
|
||||
addResourcesPatch,
|
||||
settingsColorPatch,
|
||||
addBrandLicensePatch
|
||||
)
|
||||
|
||||
execute {
|
||||
copyResources(
|
||||
|
||||
@@ -43,7 +43,7 @@ internal fun spoofVideoStreamsPatch(
|
||||
fixMediaFetchHotConfig: BytecodePatchBuilder.() -> Boolean = { false },
|
||||
fixMediaFetchHotConfigAlternative: BytecodePatchBuilder.() -> Boolean = { false },
|
||||
fixParsePlaybackResponseFeatureFlag: BytecodePatchBuilder.() -> Boolean = { false },
|
||||
block: BytecodePatchBuilder.() -> Unit = {},
|
||||
block: BytecodePatchBuilder.() -> Unit,
|
||||
executeBlock: BytecodePatchContext.() -> Unit = {},
|
||||
) = bytecodePatch(
|
||||
name = "Spoof video streams",
|
||||
|
||||
@@ -47,11 +47,15 @@ val changeLyricsProviderPatch = bytecodePatch(
|
||||
// may not allow network connections or the network may be down.
|
||||
try {
|
||||
InetAddress.getByName(host)
|
||||
} catch (e: UnknownHostException) {
|
||||
} catch (_: UnknownHostException) {
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"Host \"$host\" did not resolve to any domain."
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
// Must ignore any kind of exception. Trying to resolve network
|
||||
// on Manager throws android.os.NetworkOnMainThreadException
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.shared.layout.branding.addBrandLicensePatch
|
||||
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
|
||||
@@ -18,7 +19,7 @@ val settingsPatch = bytecodePatch(
|
||||
name = "Settings",
|
||||
description = "Adds ReVanced settings to TikTok.",
|
||||
) {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
dependsOn(sharedExtensionPatch, addBrandLicensePatch)
|
||||
|
||||
compatibleWith(
|
||||
"com.ss.android.ugc.trill"("36.5.4"),
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package app.revanced.patches.tiktok.misc.share
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val urlShorteningFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL)
|
||||
returns("LX/")
|
||||
parameters(
|
||||
"I",
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;",
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
opcodes(Opcode.RETURN_OBJECT)
|
||||
|
||||
// Same Kotlin intrinsics literal on both variants.
|
||||
strings("getShortShareUrlObservab\u2026ongUrl, subBizSceneValue)")
|
||||
|
||||
custom { method, _ ->
|
||||
// LIZLLL is obfuscated by ProGuard/R8, but stable across both TikTok and Musically.
|
||||
method.name == "LIZLLL"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package app.revanced.patches.tiktok.misc.share
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS
|
||||
import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS
|
||||
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.findFreeRegister
|
||||
import app.revanced.util.getReference
|
||||
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.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/tiktok/share/ShareUrlSanitizer;"
|
||||
|
||||
@Suppress("unused")
|
||||
val sanitizeShareUrlsPatch = bytecodePatch(
|
||||
name = PATCH_NAME_SANITIZE_SHARING_LINKS,
|
||||
description = PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS,
|
||||
) {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
compatibleWith(
|
||||
"com.ss.android.ugc.trill"("36.5.4"),
|
||||
"com.zhiliaoapp.musically"("36.5.4"),
|
||||
)
|
||||
|
||||
execute {
|
||||
urlShorteningFingerprint.method.apply {
|
||||
val invokeIndex = indexOfFirstInstructionOrThrow {
|
||||
val ref = getReference<MethodReference>()
|
||||
ref?.name == "LIZ" && ref.definingClass.startsWith("LX/")
|
||||
}
|
||||
|
||||
val moveResultIndex = indexOfFirstInstructionOrThrow(invokeIndex, Opcode.MOVE_RESULT_OBJECT)
|
||||
val urlRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
|
||||
|
||||
// Resolve Observable wrapper classes at runtime
|
||||
val observableWrapperIndex = indexOfFirstInstructionOrThrow(Opcode.NEW_INSTANCE)
|
||||
val observableWrapperClass = getInstruction<ReferenceInstruction>(observableWrapperIndex)
|
||||
.reference.toString()
|
||||
|
||||
val observableFactoryIndex = indexOfFirstInstructionOrThrow {
|
||||
val ref = getReference<MethodReference>()
|
||||
ref?.name == "LJ" && ref.definingClass.startsWith("LX/")
|
||||
}
|
||||
val observableFactoryRef = getInstruction<ReferenceInstruction>(observableFactoryIndex)
|
||||
.reference as MethodReference
|
||||
|
||||
val observableFactoryClass = observableFactoryRef.definingClass
|
||||
val observableInterfaceType = observableFactoryRef.parameterTypes.first()
|
||||
val observableReturnType = observableFactoryRef.returnType
|
||||
|
||||
val wrapperRegister = findFreeRegister(moveResultIndex + 1, urlRegister)
|
||||
|
||||
// Check setting and conditionally sanitize share URL.
|
||||
addInstructionsWithLabels(
|
||||
moveResultIndex + 1,
|
||||
"""
|
||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldSanitize()Z
|
||||
move-result v$wrapperRegister
|
||||
if-eqz v$wrapperRegister, :skip_sanitization
|
||||
|
||||
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->sanitizeShareUrl(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$urlRegister
|
||||
|
||||
# Wrap sanitized URL and return early to bypass ShareExtService
|
||||
new-instance v$wrapperRegister, $observableWrapperClass
|
||||
invoke-direct { v$wrapperRegister, v$urlRegister }, $observableWrapperClass-><init>(Ljava/lang/String;)V
|
||||
invoke-static { v$wrapperRegister }, $observableFactoryClass->LJ($observableInterfaceType)$observableReturnType
|
||||
move-result-object v$urlRegister
|
||||
return-object v$urlRegister
|
||||
|
||||
:skip_sanitization
|
||||
nop
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,38 +4,70 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.shared.PATCH_DESCRIPTION_CHANGE_LINK_SHARING_DOMAIN
|
||||
import app.revanced.patches.shared.PATCH_NAME_CHANGE_LINK_SHARING_DOMAIN
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.patches.twitter.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.returnEarly
|
||||
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 java.net.InetAddress
|
||||
import java.net.UnknownHostException
|
||||
import java.util.logging.Logger
|
||||
|
||||
internal var tweetShareLinkTemplateId = -1L
|
||||
private set
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/twitter/patches/links/ChangeLinkSharingDomainPatch;"
|
||||
|
||||
internal val changeLinkSharingDomainResourcePatch = resourcePatch {
|
||||
dependsOn(resourceMappingPatch)
|
||||
|
||||
execute {
|
||||
tweetShareLinkTemplateId = resourceMappings["string", "tweet_share_link"]
|
||||
internal val domainNameOption = stringOption(
|
||||
key = "domainName",
|
||||
default = "fxtwitter.com",
|
||||
title = "Domain name",
|
||||
description = "The domain name to use when sharing links.",
|
||||
required = true,
|
||||
) {
|
||||
// Do a courtesy check if the host can be resolved.
|
||||
// If it does not resolve, then print a warning but use the host anyway.
|
||||
// Unresolvable hosts should not be rejected, since the patching environment
|
||||
// may not allow network connections or the network may be down.
|
||||
try {
|
||||
InetAddress.getByName(it)
|
||||
} catch (_: UnknownHostException) {
|
||||
Logger.getLogger(this::class.java.name).warning(
|
||||
"Host \"$it\" did not resolve to any domain."
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
// Must ignore any kind of exception. Trying to resolve network
|
||||
// on Manager throws android.os.NetworkOnMainThreadException
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/twitter/patches/links/ChangeLinkSharingDomainPatch;"
|
||||
// TODO restore this once Manager uses a fixed version of Patcher
|
||||
/*
|
||||
internal val changeLinkSharingDomainResourcePatch = resourcePatch {
|
||||
execute {
|
||||
val domainName = domainNameOption.value!!
|
||||
|
||||
val shareLinkTemplate = "https://$domainName/%1\$s/status/%2\$s"
|
||||
|
||||
document("res/values/strings.xml").use { document ->
|
||||
document.documentElement.childNodes.findElementByAttributeValueOrThrow(
|
||||
"name",
|
||||
"tweet_share_link"
|
||||
).textContent = shareLinkTemplate
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@Suppress("unused")
|
||||
val changeLinkSharingDomainPatch = bytecodePatch(
|
||||
name = PATCH_NAME_CHANGE_LINK_SHARING_DOMAIN,
|
||||
description = PATCH_DESCRIPTION_CHANGE_LINK_SHARING_DOMAIN
|
||||
description = "$PATCH_DESCRIPTION_CHANGE_LINK_SHARING_DOMAIN Including this patch can prevent making posts that quote other posts.",
|
||||
use = false
|
||||
) {
|
||||
dependsOn(
|
||||
changeLinkSharingDomainResourcePatch,
|
||||
sharedExtensionPatch,
|
||||
)
|
||||
|
||||
@@ -46,26 +78,11 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
||||
)
|
||||
)
|
||||
|
||||
val domainName by stringOption(
|
||||
key = "domainName",
|
||||
default = "fxtwitter.com",
|
||||
title = "Domain name",
|
||||
description = "The domain name to use when sharing links.",
|
||||
required = true,
|
||||
)
|
||||
val domainName by domainNameOption()
|
||||
|
||||
execute {
|
||||
linkSharingDomainFingerprint.let {
|
||||
val replacementIndex = it.stringMatches!!.first().index
|
||||
val domainRegister = it.method.getInstruction<OneRegisterInstruction>(
|
||||
replacementIndex
|
||||
).registerA
|
||||
|
||||
it.method.replaceInstruction(
|
||||
replacementIndex,
|
||||
"const-string v$domainRegister, \"https://$domainName\"",
|
||||
)
|
||||
}
|
||||
// Replace the domain name in the link sharing extension methods.
|
||||
linkSharingDomainHelperFingerprint.method.returnEarly(domainName!!)
|
||||
|
||||
// Replace the domain name when copying a link with "Copy link" button.
|
||||
linkBuilderFingerprint.method.addInstructions(
|
||||
@@ -77,9 +94,10 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
||||
"""
|
||||
)
|
||||
|
||||
// Used in the Share via... dialog.
|
||||
// TODO remove this once changeLinkSharingDomainResourcePatch is restored
|
||||
// Replace the domain name in the "Share via..." dialog.
|
||||
linkResourceGetterFingerprint.method.apply {
|
||||
val templateIdConstIndex = indexOfFirstLiteralInstructionOrThrow(tweetShareLinkTemplateId)
|
||||
val templateIdConstIndex = indexOfFirstInstructionOrThrow(Opcode.CONST)
|
||||
|
||||
// Format the link with the new domain name register (1 instruction below the const).
|
||||
val formatLinkCallIndex = templateIdConstIndex + 1
|
||||
@@ -88,7 +106,8 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
||||
// Replace the original method call with the new method call.
|
||||
replaceInstruction(
|
||||
formatLinkCallIndex,
|
||||
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;",
|
||||
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package app.revanced.patches.twitter.misc.links
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal val openLinkFingerprint = fingerprint {
|
||||
@@ -19,13 +18,20 @@ internal val linkBuilderFingerprint = fingerprint {
|
||||
strings("/%1\$s/status/%2\$d")
|
||||
}
|
||||
|
||||
// Gets Resource string for share link view available by pressing "Share via" button.
|
||||
// TODO remove this once changeLinkSharingDomainResourcePatch is restored
|
||||
// Returns a shareable link for the "Share via..." dialog.
|
||||
internal val linkResourceGetterFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
parameters("Landroid/content/res/Resources;")
|
||||
literal { tweetShareLinkTemplateId }
|
||||
custom { _, classDef ->
|
||||
classDef.fields.any { field ->
|
||||
field.type.startsWith("Lcom/twitter/model/core/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val linkSharingDomainFingerprint = fingerprint {
|
||||
strings("https://fxtwitter.com")
|
||||
internal val linkSharingDomainHelperFingerprint = fingerprint {
|
||||
custom { method, classDef ->
|
||||
method.name == "getShareDomain" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,28 @@
|
||||
package app.revanced.patches.youtube.layout.branding
|
||||
|
||||
import app.revanced.patches.shared.layout.branding.baseCustomBrandingPatch
|
||||
import app.revanced.patches.shared.layout.branding.mipmapDirectories
|
||||
import java.nio.file.Files
|
||||
|
||||
private const val APP_NAME = "YouTube ReVanced"
|
||||
|
||||
private val youtubeIconResourceFileNames_19_34 = mapOf(
|
||||
"adaptiveproduct_youtube_foreground_color_108" to "adaptiveproduct_youtube_2024_q4_foreground_color_108",
|
||||
"adaptiveproduct_youtube_background_color_108" to "adaptiveproduct_youtube_2024_q4_background_color_108",
|
||||
)
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_MAIN_ACTIVITY_NAME
|
||||
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||
|
||||
@Suppress("unused")
|
||||
val customBrandingPatch = baseCustomBrandingPatch(
|
||||
defaultAppName = APP_NAME,
|
||||
appNameValues = mapOf(
|
||||
"YouTube ReVanced" to APP_NAME,
|
||||
"YT ReVanced" to "YT ReVanced",
|
||||
"YT" to "YT",
|
||||
"YouTube" to "YouTube",
|
||||
),
|
||||
resourceFolder = "custom-branding/youtube",
|
||||
iconResourceFileNames = arrayOf(
|
||||
"adaptiveproduct_youtube_background_color_108",
|
||||
"adaptiveproduct_youtube_foreground_color_108",
|
||||
"ic_launcher",
|
||||
"ic_launcher_round",
|
||||
),
|
||||
monochromeIconFileNames = arrayOf(
|
||||
"adaptive_monochrome_ic_youtube_launcher.xml",
|
||||
"ringo2_adaptive_monochrome_ic_youtube_launcher.xml"
|
||||
),
|
||||
addResourcePatchName = "youtube",
|
||||
originalLauncherIconName = "ic_launcher",
|
||||
originalAppName = "@string/application_name",
|
||||
originalAppPackageName = YOUTUBE_PACKAGE_NAME,
|
||||
isYouTubeMusic = false,
|
||||
numberOfPresetAppNames = 5,
|
||||
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
|
||||
mainActivityName = YOUTUBE_MAIN_ACTIVITY_NAME,
|
||||
activityAliasNameWithIntents = "com.google.android.youtube.app.honeycomb.Shell\$HomeActivity",
|
||||
preferenceScreen = PreferenceScreen.GENERAL_LAYOUT,
|
||||
|
||||
block = {
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.34.42",
|
||||
@@ -41,20 +31,5 @@ val customBrandingPatch = baseCustomBrandingPatch(
|
||||
"20.14.43",
|
||||
)
|
||||
)
|
||||
},
|
||||
|
||||
executeBlock = {
|
||||
val resourceDirectory = get("res")
|
||||
|
||||
mipmapDirectories.forEach { directory ->
|
||||
val targetDirectory = resourceDirectory.resolve(directory)
|
||||
|
||||
youtubeIconResourceFileNames_19_34.forEach { (old, new) ->
|
||||
val oldFile = targetDirectory.resolve("$old.png")
|
||||
val newFile = targetDirectory.resolve("$new.png")
|
||||
|
||||
Files.write(newFile.toPath(), oldFile.readBytes())
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patcher.util.Document
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.layout.branding.addBrandLicensePatch
|
||||
import app.revanced.patches.shared.misc.mapping.get
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
@@ -24,15 +25,43 @@ import java.io.File
|
||||
|
||||
private val variants = arrayOf("light", "dark")
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/ChangeHeaderPatch;"
|
||||
private val targetResourceDirectoryNames = mapOf(
|
||||
"drawable-hdpi" to "194x72 px",
|
||||
"drawable-xhdpi" to "258x96 px",
|
||||
"drawable-xxhdpi" to "387x144 px",
|
||||
"drawable-xxxhdpi" to "512x192 px"
|
||||
)
|
||||
|
||||
/**
|
||||
* Header logos built into this patch.
|
||||
*/
|
||||
private val logoResourceNames = arrayOf(
|
||||
"revanced_header_minimal",
|
||||
"revanced_header_rounded",
|
||||
)
|
||||
|
||||
/**
|
||||
* Custom header resource/file name.
|
||||
*/
|
||||
private const val CUSTOM_HEADER_RESOURCE_NAME = "revanced_header_custom"
|
||||
|
||||
/**
|
||||
* Custom header resource/file names.
|
||||
*/
|
||||
private val customHeaderResourceFileNames = variants.map { variant ->
|
||||
"${CUSTOM_HEADER_RESOURCE_NAME}_$variant.png"
|
||||
}.toTypedArray()
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeHeaderPatch;"
|
||||
|
||||
private val changeHeaderBytecodePatch = bytecodePatch {
|
||||
dependsOn(resourceMappingPatch)
|
||||
dependsOn(
|
||||
resourceMappingPatch,
|
||||
addBrandLicensePatch
|
||||
)
|
||||
|
||||
execute {
|
||||
// Resources are not used during patching, but extension code uses these
|
||||
// images so verify they exist.
|
||||
// Verify images exist. Resources are not used during patching but extension code does.
|
||||
arrayOf(
|
||||
"yt_ringo2_wordmark_header",
|
||||
"yt_ringo2_premium_wordmark_header"
|
||||
@@ -62,28 +91,6 @@ private val changeHeaderBytecodePatch = bytecodePatch {
|
||||
}
|
||||
}
|
||||
|
||||
private val targetResourceDirectoryNames = mapOf(
|
||||
"xxxhdpi" to "512px x 192px",
|
||||
"xxhdpi" to "387px x 144px",
|
||||
"xhdpi" to "258px x 96px",
|
||||
"hdpi" to "194px x 72px",
|
||||
"mdpi" to "129px x 48px"
|
||||
).mapKeys { (dpi, _) -> "drawable-$dpi" }
|
||||
|
||||
|
||||
/**
|
||||
* Header logos built into this patch.
|
||||
*/
|
||||
private val logoResourceNames = arrayOf(
|
||||
"revanced_header_logo_minimal",
|
||||
"revanced_header_logo",
|
||||
)
|
||||
|
||||
/**
|
||||
* Custom header resource/file name.
|
||||
*/
|
||||
private const val CUSTOM_HEADER_RESOURCE_NAME = "custom_header"
|
||||
|
||||
@Suppress("unused")
|
||||
val changeHeaderPatch = resourcePatch(
|
||||
name = "Change header",
|
||||
@@ -110,7 +117,7 @@ val changeHeaderPatch = resourcePatch(
|
||||
${targetResourceDirectoryNames.keys.joinToString("\n") { "- $it" }}
|
||||
|
||||
Each of the folders must contain all of the following files:
|
||||
${variants.joinToString("\n") { variant -> "- ${CUSTOM_HEADER_RESOURCE_NAME}_$variant.png" }}
|
||||
${customHeaderResourceFileNames.joinToString("\n")}
|
||||
|
||||
The image dimensions must be as follows:
|
||||
${targetResourceDirectoryNames.map { (dpi, dim) -> "- $dpi: $dim" }.joinToString("\n")}
|
||||
@@ -120,53 +127,41 @@ val changeHeaderPatch = resourcePatch(
|
||||
execute {
|
||||
addResources("youtube", "layout.branding.changeHeaderPatch")
|
||||
|
||||
fun getLightDarkFileNames(vararg resourceNames: String): Array<String> =
|
||||
variants.flatMap { variant ->
|
||||
resourceNames.map { resource -> "${resource}_$variant.png" }
|
||||
}.toTypedArray()
|
||||
|
||||
val logoResourceFileNames = getLightDarkFileNames(*logoResourceNames)
|
||||
copyResources(
|
||||
"change-header",
|
||||
ResourceGroup("drawable-hdpi", *logoResourceFileNames),
|
||||
ResourceGroup("drawable-mdpi", *logoResourceFileNames),
|
||||
ResourceGroup("drawable-xhdpi", *logoResourceFileNames),
|
||||
ResourceGroup("drawable-xxhdpi", *logoResourceFileNames),
|
||||
ResourceGroup("drawable-xxxhdpi", *logoResourceFileNames),
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
if (custom == null) {
|
||||
ListPreference("revanced_header_logo")
|
||||
} else {
|
||||
ListPreference(
|
||||
key = "revanced_header_logo",
|
||||
entriesKey = "revanced_header_logo_custom_entries",
|
||||
entryValuesKey = "revanced_header_logo_custom_entry_values"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (custom != null) {
|
||||
val sourceFolders = File(custom!!).listFiles { file -> file.isDirectory }
|
||||
?: throw PatchException("The provided path is not a directory: $custom")
|
||||
|
||||
val customResourceFileNames = getLightDarkFileNames(CUSTOM_HEADER_RESOURCE_NAME)
|
||||
|
||||
var copiedFiles = false
|
||||
|
||||
// For each source folder, copy the files to the target resource directories.
|
||||
sourceFolders.forEach { dpiSourceFolder ->
|
||||
val targetDpiFolder = get("res").resolve(dpiSourceFolder.name)
|
||||
if (!targetDpiFolder.exists()) return@forEach
|
||||
|
||||
val customFiles = dpiSourceFolder.listFiles { file ->
|
||||
file.isFile && file.name in customResourceFileNames
|
||||
}!!
|
||||
|
||||
if (customFiles.size > 0 && customFiles.size != variants.size) {
|
||||
throw PatchException("Both light/dark mode images " +
|
||||
"must be specified but only found: " + customFiles.map { it.name })
|
||||
}
|
||||
|
||||
customFiles.forEach { imgSourceFile ->
|
||||
val imgTargetFile = targetDpiFolder.resolve(imgSourceFile.name)
|
||||
imgSourceFile.copyTo(imgTargetFile)
|
||||
|
||||
copiedFiles = true
|
||||
}
|
||||
logoResourceNames.forEach { logo ->
|
||||
variants.forEach { variant ->
|
||||
copyResources(
|
||||
"change-header",
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
logo + "_" + variant + ".xml"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!copiedFiles) {
|
||||
throw PatchException("No custom header images found in the provided path: $custom")
|
||||
// Copy custom template. Images are only used if settings
|
||||
// are imported and a custom header is enabled.
|
||||
targetResourceDirectoryNames.keys.forEach { dpi ->
|
||||
variants.forEach { variant ->
|
||||
copyResources(
|
||||
"change-header",
|
||||
ResourceGroup(
|
||||
dpi,
|
||||
*customHeaderResourceFileNames
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +180,7 @@ val changeHeaderPatch = resourcePatch(
|
||||
addAttributeReference(logoName)
|
||||
}
|
||||
|
||||
if (custom != null) {
|
||||
addAttributeReference(CUSTOM_HEADER_RESOURCE_NAME)
|
||||
}
|
||||
addAttributeReference(CUSTOM_HEADER_RESOURCE_NAME)
|
||||
}
|
||||
|
||||
// Add custom drawables to all styles that use the regular and premium logo.
|
||||
@@ -213,22 +206,58 @@ val changeHeaderPatch = resourcePatch(
|
||||
addDrawableElement(document, logoName, mode)
|
||||
}
|
||||
|
||||
if (custom != null) {
|
||||
addDrawableElement(document, CUSTOM_HEADER_RESOURCE_NAME, mode)
|
||||
}
|
||||
addDrawableElement(document, CUSTOM_HEADER_RESOURCE_NAME, mode)
|
||||
}
|
||||
}
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
if (custom == null) {
|
||||
ListPreference("revanced_header_logo")
|
||||
} else {
|
||||
ListPreference(
|
||||
key = "revanced_header_logo",
|
||||
entriesKey = "revanced_header_logo_custom_entries",
|
||||
entryValuesKey = "revanced_header_logo_custom_entry_values"
|
||||
// Copy user provided images last, so if an exception is thrown due to bad input.
|
||||
if (custom != null) {
|
||||
val customFile = File(custom!!.trim())
|
||||
if (!customFile.exists()) {
|
||||
throw PatchException("The custom header path cannot be found: " +
|
||||
customFile.absolutePath
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
if (!customFile.isDirectory) {
|
||||
throw PatchException("The custom header path must be a folder: "
|
||||
+ customFile.absolutePath)
|
||||
}
|
||||
|
||||
var copiedFiles = false
|
||||
|
||||
// For each source folder, copy the files to the target resource directories.
|
||||
customFile.listFiles {
|
||||
file -> file.isDirectory && file.name in targetResourceDirectoryNames
|
||||
}!!.forEach { dpiSourceFolder ->
|
||||
val targetDpiFolder = get("res").resolve(dpiSourceFolder.name)
|
||||
if (!targetDpiFolder.exists()) {
|
||||
// Should never happen.
|
||||
throw IllegalStateException("Resource not found: $dpiSourceFolder")
|
||||
}
|
||||
|
||||
val customFiles = dpiSourceFolder.listFiles { file ->
|
||||
file.isFile && file.name in customHeaderResourceFileNames
|
||||
}!!
|
||||
|
||||
if (customFiles.isNotEmpty() && customFiles.size != variants.size) {
|
||||
throw PatchException("Both light/dark mode images " +
|
||||
"must be specified but only found: " + customFiles.map { it.name })
|
||||
}
|
||||
|
||||
customFiles.forEach { imgSourceFile ->
|
||||
val imgTargetFile = targetDpiFolder.resolve(imgSourceFile.name)
|
||||
imgSourceFile.copyTo(target = imgTargetFile, overwrite = true)
|
||||
|
||||
copiedFiles = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!copiedFiles) {
|
||||
throw PatchException("Expected to find directories and files: "
|
||||
+ customHeaderResourceFileNames.contentToString()
|
||||
+ "\nBut none were found in the provided option file path: " + customFile.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import app.revanced.util.forEachLiteralValueInstruction
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstLiteralInstruction
|
||||
import app.revanced.util.removeFromParent
|
||||
import app.revanced.util.returnLate
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
@@ -127,7 +128,7 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
|
||||
)
|
||||
|
||||
if (hideShortsAppShortcut == true) {
|
||||
shortsItem.parentNode.removeChild(shortsItem)
|
||||
shortsItem.removeFromParent()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +139,7 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
|
||||
)
|
||||
|
||||
if (hideShortsWidget == true) {
|
||||
shortsItem.parentNode.removeChild(shortsItem)
|
||||
shortsItem.removeFromParent()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package app.revanced.patches.youtube.layout.seekbar
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
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.replaceInstruction
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
@@ -17,17 +15,13 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_49_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.inputStreamFromBundledResource
|
||||
import app.revanced.util.insertLiteralOverride
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
@@ -38,9 +32,6 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
import org.w3c.dom.Element
|
||||
import java.io.ByteArrayInputStream
|
||||
import kotlin.use
|
||||
|
||||
internal var reelTimeBarPlayedColorId = -1L
|
||||
private set
|
||||
@@ -57,8 +48,6 @@ internal var ytTextSecondaryId = -1L
|
||||
internal var inlineTimeBarLiveSeekableRangeId = -1L
|
||||
private set
|
||||
|
||||
internal const val splashSeekbarColorAttributeName = "splash_custom_seekbar_color"
|
||||
|
||||
private val seekbarColorResourcePatch = resourcePatch {
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
@@ -92,21 +81,6 @@ private val seekbarColorResourcePatch = resourcePatch {
|
||||
"inline_time_bar_live_seekable_range"
|
||||
]
|
||||
|
||||
// Modify the resume playback drawable and replace the progress bar with a custom drawable.
|
||||
document("res/drawable/resume_playback_progressbar_drawable.xml").use { document ->
|
||||
val layerList = document.getElementsByTagName("layer-list").item(0) as Element
|
||||
val progressNode = layerList.getElementsByTagName("item").item(1) as Element
|
||||
if (!progressNode.getAttributeNode("android:id").value.endsWith("progress")) {
|
||||
throw PatchException("Could not find progress bar")
|
||||
}
|
||||
val scaleNode = progressNode.getElementsByTagName("scale").item(0) as Element
|
||||
val shapeNode = scaleNode.getElementsByTagName("shape").item(0) as Element
|
||||
val replacementNode = document.createElement(
|
||||
"app.revanced.extension.youtube.patches.theme.ProgressBarDrawable",
|
||||
)
|
||||
scaleNode.replaceChild(replacementNode, shapeNode)
|
||||
}
|
||||
|
||||
ytYoutubeMagentaColorId = resourceMappings[
|
||||
"color",
|
||||
"yt_youtube_magenta",
|
||||
@@ -115,99 +89,9 @@ private val seekbarColorResourcePatch = resourcePatch {
|
||||
"attr",
|
||||
"ytStaticBrandRed",
|
||||
]
|
||||
|
||||
// Add attribute and styles for splash screen custom color.
|
||||
// Using a style is the only way to selectively change just the seekbar fill color.
|
||||
//
|
||||
// Because the style colors must be hard coded for all color possibilities,
|
||||
// instead of allowing 24 bit color the style is restricted to 9-bit (3 bits per color channel)
|
||||
// and the style color closest to the users custom color is used for the splash screen.
|
||||
arrayOf(
|
||||
inputStreamFromBundledResource("seekbar/values", "attrs.xml")!! to "res/values/attrs.xml",
|
||||
ByteArrayInputStream(create9BitSeekbarColorStyles().toByteArray()) to "res/values/styles.xml"
|
||||
).forEach { (source, destination) ->
|
||||
"resources".copyXmlNode(
|
||||
document(source),
|
||||
document(destination),
|
||||
).close()
|
||||
}
|
||||
|
||||
fun setSplashDrawablePathFillColor(xmlFileNames: Iterable<String>, vararg resourceNames: String) {
|
||||
xmlFileNames.forEach { xmlFileName ->
|
||||
document(xmlFileName).use { document ->
|
||||
val childNodes = document.childNodes
|
||||
|
||||
resourceNames.forEach { elementId ->
|
||||
val element = childNodes.findElementByAttributeValueOrThrow(
|
||||
"android:name",
|
||||
elementId
|
||||
)
|
||||
|
||||
val attribute = "android:fillColor"
|
||||
if (!element.hasAttribute(attribute)) {
|
||||
throw PatchException("Could not find $attribute for $elementId")
|
||||
}
|
||||
|
||||
element.setAttribute(attribute, "?attr/$splashSeekbarColorAttributeName")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setSplashDrawablePathFillColor(
|
||||
listOf(
|
||||
"res/drawable/\$startup_animation_light__0.xml",
|
||||
"res/drawable/\$startup_animation_dark__0.xml"
|
||||
),
|
||||
"_R_G_L_10_G_D_0_P_0"
|
||||
)
|
||||
|
||||
if (!is_19_46_or_greater) {
|
||||
// Resources removed in 19.46+
|
||||
setSplashDrawablePathFillColor(
|
||||
listOf(
|
||||
"res/drawable/\$buenos_aires_animation_light__0.xml",
|
||||
"res/drawable/\$buenos_aires_animation_dark__0.xml"
|
||||
),
|
||||
"_R_G_L_8_G_D_0_P_0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a style xml with all combinations of 9-bit colors.
|
||||
*/
|
||||
private fun create9BitSeekbarColorStyles(): String = StringBuilder().apply {
|
||||
append("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
|
||||
append("<resources>\n")
|
||||
|
||||
for (red in 0..7) {
|
||||
for (green in 0..7) {
|
||||
for (blue in 0..7) {
|
||||
val name = "${red}_${green}_${blue}"
|
||||
|
||||
fun roundTo3BitHex(channel8Bits: Int) =
|
||||
(channel8Bits * 255 / 7).toString(16).padStart(2, '0')
|
||||
val r = roundTo3BitHex(red)
|
||||
val g = roundTo3BitHex(green)
|
||||
val b = roundTo3BitHex(blue)
|
||||
val color = "#ff$r$g$b"
|
||||
|
||||
append(
|
||||
"""
|
||||
<style name="splash_seekbar_color_style_$name">
|
||||
<item name="$splashSeekbarColorAttributeName">$color</item>
|
||||
</style>
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
append("</resources>")
|
||||
}.toString()
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/theme/SeekbarColorPatch;"
|
||||
|
||||
val seekbarColorPatch = bytecodePatch(
|
||||
@@ -344,21 +228,6 @@ val seekbarColorPatch = bytecodePatch(
|
||||
|
||||
// Hook the splash animation to set the a seekbar color.
|
||||
mainActivityOnCreateFingerprint.method.apply {
|
||||
val drawableIndex = indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Landroid/widget/ImageView;"
|
||||
&& reference.name == "getDrawable"
|
||||
}
|
||||
val checkCastIndex = indexOfFirstInstructionOrThrow(drawableIndex, Opcode.CHECK_CAST)
|
||||
val drawableRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
|
||||
|
||||
addInstruction(
|
||||
checkCastIndex + 1,
|
||||
"invoke-static { v$drawableRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"setSplashAnimationDrawableTheme(Landroid/graphics/drawable/AnimatedVectorDrawable;)V"
|
||||
)
|
||||
|
||||
// Replace the Lottie animation view setAnimation(int) call.
|
||||
val setAnimationIntMethodName = lottieAnimationViewSetAnimationIntFingerprint.originalMethod.name
|
||||
|
||||
findInstructionIndicesReversedOrThrow {
|
||||
@@ -371,13 +240,11 @@ val seekbarColorPatch = bytecodePatch(
|
||||
replaceInstruction(
|
||||
index,
|
||||
"invoke-static { v${instruction.registerC}, v${instruction.registerD} }, " +
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->setSplashAnimationLottie" +
|
||||
"(Lcom/airbnb/lottie/LottieAnimationView;I)V"
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->setSplashAnimationLottie(Lcom/airbnb/lottie/LottieAnimationView;I)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add non obfuscated method aliases for `setAnimation(int)`
|
||||
// and `setAnimation(InputStream, String)` so extension code can call them.
|
||||
lottieAnimationViewSetAnimationIntFingerprint.classDef.methods.apply {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package app.revanced.patches.youtube.misc.gms
|
||||
|
||||
internal object Constants {
|
||||
internal const val YOUTUBE_MAIN_ACTIVITY_NAME = "com.google.android.apps.youtube.app.watchwhile.MainActivity"
|
||||
|
||||
const val YOUTUBE_PACKAGE_NAME = "com.google.android.youtube"
|
||||
const val REVANCED_YOUTUBE_PACKAGE_NAME = "app.revanced.android.youtube"
|
||||
}
|
||||
@@ -4,72 +4,75 @@ package app.revanced.patches.youtube.misc.playservice
|
||||
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.util.findPlayStoreServicesVersion
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
// Use notNull delegate so an exception is thrown if these fields are accessed before they are set.
|
||||
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_03_or_greater = false
|
||||
var is_19_03_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_04_or_greater = false
|
||||
var is_19_04_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_16_or_greater = false
|
||||
var is_19_16_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_17_or_greater = false
|
||||
var is_19_17_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_18_or_greater = false
|
||||
var is_19_18_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_23_or_greater = false
|
||||
var is_19_23_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_25_or_greater = false
|
||||
var is_19_25_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_26_or_greater = false
|
||||
var is_19_26_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_29_or_greater = false
|
||||
var is_19_29_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_32_or_greater = false
|
||||
var is_19_32_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_33_or_greater = false
|
||||
var is_19_33_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
@Deprecated("19.34.42 is the lowest supported version")
|
||||
var is_19_34_or_greater = false
|
||||
var is_19_34_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_19_35_or_greater = false
|
||||
var is_19_35_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_19_36_or_greater = false
|
||||
var is_19_36_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_19_41_or_greater = false
|
||||
var is_19_41_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_19_43_or_greater = false
|
||||
var is_19_43_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_19_46_or_greater = false
|
||||
var is_19_46_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_19_47_or_greater = false
|
||||
var is_19_47_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_19_49_or_greater = false
|
||||
var is_19_49_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_20_02_or_greater = false
|
||||
var is_20_02_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_20_03_or_greater = false
|
||||
var is_20_03_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_20_05_or_greater = false
|
||||
var is_20_05_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_20_07_or_greater = false
|
||||
var is_20_07_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_20_09_or_greater = false
|
||||
var is_20_09_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_20_10_or_greater = false
|
||||
var is_20_10_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_20_14_or_greater = false
|
||||
var is_20_14_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
var is_20_15_or_greater = false
|
||||
var is_20_15_or_greater : Boolean by Delegates.notNull()
|
||||
private set
|
||||
|
||||
val versionCheckPatch = resourcePatch(
|
||||
|
||||
@@ -8,9 +8,6 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/ForceOriginalAudioPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val forceOriginalAudioPatch = forceOriginalAudioPatch(
|
||||
block = {
|
||||
@@ -29,8 +26,8 @@ val forceOriginalAudioPatch = forceOriginalAudioPatch(
|
||||
)
|
||||
)
|
||||
},
|
||||
fixUseLocalizedAudioTrackFlag = is_20_07_or_greater,
|
||||
fixUseLocalizedAudioTrackFlag = { is_20_07_or_greater },
|
||||
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
|
||||
subclassExtensionClassDescriptor = EXTENSION_CLASS_DESCRIPTOR,
|
||||
subclassExtensionClassDescriptor = "Lapp/revanced/extension/youtube/patches/ForceOriginalAudioPatch;",
|
||||
preferenceScreen = PreferenceScreen.VIDEO,
|
||||
)
|
||||
|
||||
@@ -14,6 +14,13 @@ import java.nio.file.StandardCopyOption
|
||||
|
||||
private val classLoader = object {}.javaClass.classLoader
|
||||
|
||||
/**
|
||||
* Removes a node from its parent.
|
||||
*
|
||||
* @return The node that was removed (object this method was called on).
|
||||
*/
|
||||
fun Node.removeFromParent() : Node = parentNode.removeChild(this)
|
||||
|
||||
/**
|
||||
* Returns a sequence for all child nodes.
|
||||
*/
|
||||
@@ -70,8 +77,13 @@ fun ResourcePatchContext.copyResources(
|
||||
for (resourceGroup in resources) {
|
||||
resourceGroup.resources.forEach { resource ->
|
||||
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
|
||||
val stream = inputStreamFromBundledResource(sourceResourceDirectory, resourceFile)
|
||||
if (stream == null) {
|
||||
throw IllegalArgumentException("Could not find resource: $resourceFile " +
|
||||
"in directory: $sourceResourceDirectory")
|
||||
}
|
||||
Files.copy(
|
||||
inputStreamFromBundledResource(sourceResourceDirectory, resourceFile)!!,
|
||||
stream,
|
||||
targetResourceDirectory.resolve(resourceFile).toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING,
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,11 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||
@@ -203,8 +209,10 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.theme.themePatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
|
||||
</patch>
|
||||
@@ -253,6 +261,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
</patch>
|
||||
<patch id="ad.video.hideVideoAdsPatch">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,11 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||
@@ -203,8 +209,10 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.theme.themePatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
|
||||
</patch>
|
||||
@@ -253,6 +261,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
</patch>
|
||||
<patch id="ad.video.hideVideoAdsPatch">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">اسم التطبيق</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">مخصص</string>
|
||||
<string name="revanced_custom_branding_icon_title">أيقونة التطبيق</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">الأصلية</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimal</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced scaled</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">مخصص</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">فشلت عمليات التحقق</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">فتح الموقع الرسمي</string>
|
||||
@@ -1410,11 +1423,13 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">لون تمييز شريط التقدم</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">لون شريط التقدم غير صالح</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">علامة الشعار</string>
|
||||
<string name="revanced_header_logo_entry_1">الافتراضي</string>
|
||||
<string name="revanced_header_logo_entry_2">القياسي</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced minimal</string>
|
||||
<string name="revanced_header_logo_entry_6">مخصص</string>
|
||||
</patch>
|
||||
@@ -1604,14 +1619,14 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_slide_to_seek_summary_off">تم تعطيل التمرير للتقديم أو الترجيع</string>
|
||||
</patch>
|
||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||
<string name="revanced_spoof_video_streams_av1_title">السماح لـ Android VR AV1</string>
|
||||
<string name="revanced_spoof_video_streams_av1_title">السماح بـ Android VR AV1</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_on">"برنامج ترميز الفيديو هو AVC (H.264) أو VP9 أو AV1
|
||||
|
||||
قد يحدث تقطيع أو فقدان للإطارات أثناء التشغيل"</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_off">برنامج ترميز الفيديو هو AVC (H.264) أو VP9</string>
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"قد يؤدي تمكين هذا الإعداد إلى استخدام فك ترميز AV1 برمجيًا.
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"قد يؤدي تمكين هذا الإعداد إلى استخدام فك تشفير AV1 البرمجي.
|
||||
|
||||
قد يتلعثم تشغيل الفيديو بتقنية AV1 أو يفقد بعض الإطارات."</string>
|
||||
قد يتسبب تشغيل الفيديو باستخدام AV1 في التقطيع أو إسقاط الإطارات."</string>
|
||||
<string name="revanced_spoof_video_streams_about_title">التأثيرات الجانبية للتزييف</string>
|
||||
<string name="revanced_spoof_video_streams_about_experimental">• عميل تجريبي وقد يتوقف عن العمل في أي وقت</string>
|
||||
<string name="revanced_spoof_video_streams_about_playback_failure">• قد يتوقف الفيديو عند 1:00، أو قد لا يكون متاحًا في بعض المناطق</string>
|
||||
@@ -1631,6 +1646,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">لمحة</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">الإعلانات</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,11 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||
@@ -204,8 +210,10 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.theme.themePatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
|
||||
</patch>
|
||||
@@ -255,6 +263,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
</patch>
|
||||
<patch id="ad.video.hideVideoAdsPatch">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Tətbiq adı</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Fərdi</string>
|
||||
<string name="revanced_custom_branding_icon_title">Tətbiq simvolu</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Orijinal</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced ən kiçik</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced ölçüləndirilmiş</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Fərdi</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Yoxlamalar uğursuz oldu</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Xidməti veb saytı aç</string>
|
||||
@@ -205,9 +218,9 @@ Hər halda, bunu aktivləşdirmə IP ünvanınız kimi bəzi istifadəçi məlum
|
||||
<string name="revanced_hide_community_posts_title">İcma elanların gizlət</string>
|
||||
<string name="revanced_hide_community_posts_summary_on">İcma elanları gizlədilib</string>
|
||||
<string name="revanced_hide_community_posts_summary_off">İcma elanları göstərilir</string>
|
||||
<string name="revanced_hide_compact_banner_title">Yığcam etiketləri gizlət</string>
|
||||
<string name="revanced_hide_compact_banner_summary_on">Yığcam etiketlər gizlidir</string>
|
||||
<string name="revanced_hide_compact_banner_summary_off">Yığcam etiketlər göstərilir</string>
|
||||
<string name="revanced_hide_compact_banner_title">Yığcam afişaları gizlət</string>
|
||||
<string name="revanced_hide_compact_banner_summary_on">Yığcam afişalar gizlidir</string>
|
||||
<string name="revanced_hide_compact_banner_summary_off">Yığcam afişalar görünür</string>
|
||||
<string name="revanced_hide_crowdfunding_box_title">İanə qutusunu gizlət</string>
|
||||
<string name="revanced_hide_crowdfunding_box_summary_on">İanə qutusu gizlidir</string>
|
||||
<string name="revanced_hide_crowdfunding_box_summary_off">İanə qutusu göstərilir</string>
|
||||
@@ -458,9 +471,9 @@ Məhdudiyyətlər
|
||||
<string name="revanced_hide_creator_store_shelf_title">Yaradıcı mağaza bölümün gizlət</string>
|
||||
<string name="revanced_hide_creator_store_shelf_summary_on">Yaradıcı alış-veriş cərgəsi video oynadıcı altında gizlidir</string>
|
||||
<string name="revanced_hide_creator_store_shelf_summary_off">Yaradıcı alış-veriş cərgəsi video oynadıcı altında görünür</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_title">Son ekran mağaza etiketini gizlət</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_summary_on">Son ekran alış-veriş etiketi gizlədilib</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_summary_off">Son ekran alış-veriş etiketi görünür</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_title">Son ekran mağaza afişasın gizlət</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_summary_on">Son ekran mağaza afişası gizlidir</string>
|
||||
<string name="revanced_hide_end_screen_store_banner_summary_off">Son ekran mağaza afişası görünür</string>
|
||||
<string name="revanced_hide_fullscreen_ads_title">Tam ekran reklamlarını gizlət</string>
|
||||
<string name="revanced_hide_fullscreen_ads_summary_on">"Tam ekran reklamları gizlidir
|
||||
|
||||
@@ -471,9 +484,9 @@ Bu xüsusiyyət yalnız köhnə cihazlar üçün mövcuddur"</string>
|
||||
<string name="revanced_hide_general_ads_title">Ümumi reklamları gizlət</string>
|
||||
<string name="revanced_hide_general_ads_summary_on">Ümumi reklamlar gizlidir</string>
|
||||
<string name="revanced_hide_general_ads_summary_off">Ümumi reklamlar göstərilir</string>
|
||||
<string name="revanced_hide_merchandise_banners_title">Məhsul etiketlərini gizlət</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">Məhsul etiketləri gizlədilir</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Məhsul etiketləri göstərilir</string>
|
||||
<string name="revanced_hide_merchandise_banners_title">Məhsul afişaların gizlət</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">Məhsul afişaları gizlədilir</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Məhsul afişaları görünür</string>
|
||||
<string name="revanced_hide_paid_promotion_label_title">Ödənişli tanıtım etiketini gizlət</string>
|
||||
<string name="revanced_hide_paid_promotion_label_summary_on">Ödənişli reklam etiketi gizlədilib</string>
|
||||
<string name="revanced_hide_paid_promotion_label_summary_off">Ödənişli reklam etiketi göstərilir</string>
|
||||
@@ -483,7 +496,7 @@ Bu xüsusiyyət yalnız köhnə cihazlar üçün mövcuddur"</string>
|
||||
<string name="revanced_hide_shopping_links_title">Alış-veriş linklərini gizlət</string>
|
||||
<string name="revanced_hide_shopping_links_summary_on">Alış-veriş linkləri video təsvirdə gizlidir</string>
|
||||
<string name="revanced_hide_shopping_links_summary_off">Alış-veriş linkləri video təsvirdə görünür</string>
|
||||
<string name="revanced_hide_view_products_banner_title">“Məhsullara baxın” panelin gizlət</string>
|
||||
<string name="revanced_hide_view_products_banner_title">“Məhsullara baxın” afişasın gizlət</string>
|
||||
<string name="revanced_hide_view_products_banner_summary_on">Məhsullara baxış etiketi video örtüyündə gizlidir</string>
|
||||
<string name="revanced_hide_view_products_banner_summary_off">Məhsullara baxış etiketi video örtüyündə görünür</string>
|
||||
<string name="revanced_hide_web_search_results_title">Veb axtarış nəticələrini gizlət</string>
|
||||
@@ -1409,11 +1422,13 @@ Kiçik oynadıcı ekrandan sola və ya sağa sürüklənə bilər"</string>
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">İrəliləyiş cizgisi vurğu rəngi</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Etibarsız irəliləmə cizgisi rəng dəyəri</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Başlıq simvolu</string>
|
||||
<string name="revanced_header_logo_entry_1">İlkin</string>
|
||||
<string name="revanced_header_logo_entry_2">Müntəzəm</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">Ən kiçik ReVanced</string>
|
||||
<string name="revanced_header_logo_entry_6">Fərdi</string>
|
||||
</patch>
|
||||
@@ -1604,13 +1619,13 @@ Məhdudiyyətlər:
|
||||
</patch>
|
||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||
<string name="revanced_spoof_video_streams_av1_title">Android VR AV1-ə icazə ver</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_on">"Video kodek AVC (H.264), VP9 və ya AV1-dir
|
||||
<string name="revanced_spoof_video_streams_av1_summary_on">"Video kodlayıcı AVC (H.264), VP9 və ya AV1-dir
|
||||
|
||||
Oxutma kəsilə bilər və ya kadrlar atıla bilər"</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_off">Video kodek AVC (H.264) və ya VP9-dur</string>
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"Bu ayarı aktivləşdirmək proqram təminatı AV1 dekodlaşdırmasından istifadə edə bilər.
|
||||
Oynatma ilişə bilər və ya kadrlar buraxıla bilər"</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_off">Video kodlayıcı AVC (H.264) və ya VP9-dur</string>
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"Bu tənzimləməni aktivləşdirmə proqram təminatlı AV1 kodlayıcı istifadə edə bilər.
|
||||
|
||||
AV1 ilə video oxutma kəsilə bilər və ya kadrlar atıla bilər."</string>
|
||||
Video oynatma AV1 ilə ilişə bilər və ya kadrlar buraxıla bilər."</string>
|
||||
<string name="revanced_spoof_video_streams_about_title">Saxtakarlıq yan təsirləri</string>
|
||||
<string name="revanced_spoof_video_streams_about_experimental">• Təcrübi qəbuledici və hər vaxt işləməyi dayandıra bilər</string>
|
||||
<string name="revanced_spoof_video_streams_about_playback_failure">• Video 01:00-da dayana bilər və ya bəzi bölgələrdə mövcud olmaya bilər</string>
|
||||
@@ -1630,6 +1645,8 @@ AV1 ilə video oxutma kəsilə bilər və ya kadrlar atıla bilər."</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Haqqında</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Reklamlar</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Назва праграмы</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Карыстальніцкая</string>
|
||||
<string name="revanced_custom_branding_icon_title">Значок праграмы</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Арыгінал</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced мінімальны</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced маштабаваны</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Карыстальніцкі</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Праверкі не пройдзены</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Адкрыць афіцыйны вэб-сайт</string>
|
||||
@@ -1411,11 +1424,13 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Акцэнтны колер пошуку</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Неверное значение цвета ползунка прогресса</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Лагатып загалоўка</string>
|
||||
<string name="revanced_header_logo_entry_1">Па змаўчанні</string>
|
||||
<string name="revanced_header_logo_entry_2">Звычайны</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced мінімальны</string>
|
||||
<string name="revanced_header_logo_entry_6">Карыстальніцкі</string>
|
||||
</patch>
|
||||
@@ -1632,6 +1647,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Пра</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Рэклама</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Име на приложението</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Персонализиран</string>
|
||||
<string name="revanced_custom_branding_icon_title">Икона на приложението</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Оригинален</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced минимален</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced мащабиран</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Персонализиран</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Проверката е неуспешна</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Отворете официалния уебсайт</string>
|
||||
@@ -1410,11 +1423,13 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Цветът на лентата за търсене</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Невалидна стойност за цвят на лентата на прогреса</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Лого в заглавката</string>
|
||||
<string name="revanced_header_logo_entry_1">По подразбиране</string>
|
||||
<string name="revanced_header_logo_entry_2">Обикновено</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced минимално</string>
|
||||
<string name="revanced_header_logo_entry_6">По избор</string>
|
||||
</patch>
|
||||
@@ -1631,6 +1646,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Относно</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Реклами</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">অ্যাপের নাম</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">কাস্টম</string>
|
||||
<string name="revanced_custom_branding_icon_title">অ্যাপ আইকন</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">মূল</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">রিভ্যান্সড মিনিমাল</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">রিভ্যান্সড স্কেলড</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">কাস্টম</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">চেক ফেইল করেছে</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">অফিশ্যাল ওয়েবসাইট খুলুন</string>
|
||||
@@ -1406,11 +1419,13 @@ Miniplayer স্ক্রিন থেকে বামে বা ডানে
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">সিকবারের এক্সেন্ট রঙ</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">অবৈধ সিকবার রঙ মান</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">হেডার লোগো</string>
|
||||
<string name="revanced_header_logo_entry_1">ডিফল্ট</string>
|
||||
<string name="revanced_header_logo_entry_2">সাধারণ</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">রিভ্যান্সড মিনিমাল</string>
|
||||
<string name="revanced_header_logo_entry_6">কাস্টম</string>
|
||||
</patch>
|
||||
@@ -1627,6 +1642,8 @@ AV1 সহ ভিডিও প্লেব্যাক আটকে যেতে
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">সম্পর্কে</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">বিজ্ঞাপন</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,11 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||
@@ -203,8 +209,10 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.theme.themePatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
|
||||
</patch>
|
||||
@@ -253,6 +261,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
</patch>
|
||||
<patch id="ad.video.hideVideoAdsPatch">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,11 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||
@@ -203,8 +209,10 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.theme.themePatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
|
||||
</patch>
|
||||
@@ -253,6 +261,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
</patch>
|
||||
<patch id="ad.video.hideVideoAdsPatch">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Název aplikace</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Vlastní</string>
|
||||
<string name="revanced_custom_branding_icon_title">Ikona aplikace</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Původní</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimální</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced škálované</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Vlastní</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Kontrola proběhla neúspěšně</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Otevřít oficiální webovou stránku</string>
|
||||
@@ -1410,11 +1423,13 @@ Omezení: Použití tlačítka zpět na panelu nástrojů nemusí fungovat"</str
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Zvýrazněná barva posuvníku</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Neplatná hodnota barvy posuvníku</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Logo záhlaví</string>
|
||||
<string name="revanced_header_logo_entry_1">Výchozí</string>
|
||||
<string name="revanced_header_logo_entry_2">Běžné</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced minimální</string>
|
||||
<string name="revanced_header_logo_entry_6">Vlastní</string>
|
||||
</patch>
|
||||
@@ -1631,6 +1646,8 @@ Přehrávání videa s AV1 se může sekat nebo vypadávat snímky."</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">O aplikaci</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Reklamy</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Appnavn</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Tilpasset</string>
|
||||
<string name="revanced_custom_branding_icon_title">Appikon</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Original</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimal</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced skaleret</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Tilpasset</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Kontrol mislykkedes</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Åbn officiel hjemmeside</string>
|
||||
@@ -1412,11 +1425,13 @@ Miniafspiller kan trækkes ud over skærmen til venstre eller højre"</string>
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Søgelinjens accentfarve</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Ugyldig søgelinje farveværdi</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Sidehovedlogo</string>
|
||||
<string name="revanced_header_logo_entry_1">Standard</string>
|
||||
<string name="revanced_header_logo_entry_2">Normal</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced minimal</string>
|
||||
<string name="revanced_header_logo_entry_6">Brugerdefineret</string>
|
||||
</patch>
|
||||
@@ -1633,6 +1648,8 @@ Videoafspilning med AV1 kan hakke eller tabe billeder."</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Om</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Annoncer</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">App-Name</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Benutzerdefiniert</string>
|
||||
<string name="revanced_custom_branding_icon_title">App-Symbol</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Original</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimal</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced skaliert</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Benutzerdefiniert</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Prüfungen fehlgeschlagen</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Offizielle Website öffnen</string>
|
||||
@@ -1407,11 +1420,13 @@ Der Mini-Player kann mit einer Wischgeste vom Bildschirm nach links oder rechts
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Die Akzentfarbe der Fortschrittsanzeige</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Ungültiger Suchleisten-Farbwert</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Header-Logo</string>
|
||||
<string name="revanced_header_logo_entry_1">Standard</string>
|
||||
<string name="revanced_header_logo_entry_2">Regulär</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced Minimal</string>
|
||||
<string name="revanced_header_logo_entry_6">Benutzerdefiniert</string>
|
||||
</patch>
|
||||
@@ -1628,6 +1643,8 @@ Die Videowiedergabe mit AV1 kann stottern oder Bilder überspringen."</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Über</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Werbung</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Όνομα εφαρμογής</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Προσαρμοσμένο</string>
|
||||
<string name="revanced_custom_branding_icon_title">Εικονίδιο εφαρμογής</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Αρχικό</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced απλό</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced κλιμακωμένο</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Προσαρμοσμένο</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Αποτυχία ελέγχων</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Άνοιγμα επίσημης ιστοσελίδας</string>
|
||||
@@ -959,8 +972,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.relatedvideooverlay.hideRelatedVideoOverlayPatch">
|
||||
<string name="revanced_hide_related_videos_overlay_title">Σχετικά βίντεο στην πλήρη οθόνη</string>
|
||||
<string name="revanced_hide_related_videos_overlay_summary_on">Κρυμμένα\n\nΑφορά τα σχετικά βίντεο της τελικής οθόνης στη λειτουργία πλήρους οθόνης</string>
|
||||
<string name="revanced_hide_related_videos_overlay_summary_off">Εμφανίζονται\n\nΑφορά τα σχετικά βίντεο της τελικής οθόνης στη λειτουργία πλήρους οθόνης</string>
|
||||
<string name="revanced_hide_related_videos_overlay_summary_on">Κρυμμένα</string>
|
||||
<string name="revanced_hide_related_videos_overlay_summary_off">Εμφανίζονται</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.time.hideTimestampPatch">
|
||||
<string name="revanced_hide_timestamp_title">Χρονική πρόοδος βίντεο</string>
|
||||
@@ -1411,11 +1424,13 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Το χρώμα έμφασης της γραμμής προόδου</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Μη έγκυρη τιμή χρώματος γραμμής προόδου</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Λογότυπο επικεφαλίδας</string>
|
||||
<string name="revanced_header_logo_entry_1">Προεπιλογή</string>
|
||||
<string name="revanced_header_logo_entry_2">Κανονικό</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced απλό</string>
|
||||
<string name="revanced_header_logo_entry_6">Προσαρμοσμένο</string>
|
||||
</patch>
|
||||
@@ -1603,14 +1618,14 @@ Second \"item\" text"</string>
|
||||
<string name="revanced_slide_to_seek_summary_off">Η χειρονομία συρσίματος για αναζήτηση στη γραμμή προόδου είναι απενεργοποιημένη</string>
|
||||
</patch>
|
||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||
<string name="revanced_spoof_video_streams_av1_title">Να επιτρέπεται το Android VR AV1</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_on">"Ο κωδικοποιητής βίντεο είναι AVC (H.264), VP9 ή AV1
|
||||
<string name="revanced_spoof_video_streams_av1_title">Να επιτρέπεται ο κωδικοποιητής Android VR AV1</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_on">"Ο κωδικοποιητής βίντεο είναι ο AVC (H.264), ο VP9 ή ο AV1
|
||||
|
||||
Η αναπαραγωγή ενδέχεται να κολλήσει ή να χάσει καρέ"</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_off">Ο κωδικοποιητής βίντεο είναι AVC (H.264) ή VP9</string>
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"Η ενεργοποίηση αυτής της ρύθμισης ενδέχεται να χρησιμοποιήσει αποκωδικοποίηση AV1 λογισμικού.
|
||||
Η αναπαραγωγή μπορεί να κομπιάζει ή να χάνει καρέ"</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_off">Ο κωδικοποιητής βίντεο είναι ο AVC (H.264) ή ο VP9</string>
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"Η ενεργοποίηση αυτής της ρύθμισης ενδέχεται να χρησιμοποιήσει αποκωδικοποίηση λογισμικού AV1.
|
||||
|
||||
Η αναπαραγωγή βίντεο με AV1 ενδέχεται να έχει διακοπές ή να χάνει καρέ."</string>
|
||||
Η αναπαραγωγή βίντεο μπορεί να κομπιάζει ή να χάνει καρέ."</string>
|
||||
<string name="revanced_spoof_video_streams_about_title">Παρενέργειες παραποίησης</string>
|
||||
<string name="revanced_spoof_video_streams_about_experimental">• Πειραματικός πελάτης και μπορεί να σταματήσει να λειτουργεί ανά πάσα στιγμή</string>
|
||||
<string name="revanced_spoof_video_streams_about_playback_failure">• Το βίντεο μπορεί να σταματήσει στο 1:00 ή να μην είναι διαθέσιμο σε ορισμένες περιοχές</string>
|
||||
@@ -1630,6 +1645,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Σχετικά με</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Διαφημίσεις</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Nombre de la aplicación</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Personalizado</string>
|
||||
<string name="revanced_custom_branding_icon_title">Icono de la aplicación</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Original</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced mínimo</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced escalado</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Personalizado</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Comprobaciones fallidas</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Ir al sitio web oficial</string>
|
||||
@@ -1401,11 +1414,13 @@ El minireproductor se puede arrastrar fuera de la pantalla hacia la izquierda o
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">El color de acento de la barra de búsqueda</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Valor de color de la barra de búsqueda inválido</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Logotipo del encabezado</string>
|
||||
<string name="revanced_header_logo_entry_1">Predeterminado</string>
|
||||
<string name="revanced_header_logo_entry_2">Normal</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced mínimo</string>
|
||||
<string name="revanced_header_logo_entry_6">Personalizado</string>
|
||||
</patch>
|
||||
@@ -1620,6 +1635,8 @@ La reproducción puede tartamudear o perder fotogramas"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Acerca de</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Anuncios</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Rakenduse nimi</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Kohandatud</string>
|
||||
<string name="revanced_custom_branding_icon_title">Rakenduse ikoon</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Algne</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimaalne</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced skaleeritud</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Kohandatud</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Kontrollid ebaõnnestusid</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Ava oficial veebileht</string>
|
||||
@@ -1410,11 +1423,13 @@ Minimaalset mängijat saab lohistada ekraanilt vasakule või paremale"</string>
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Otsinguriba esiletõiduv värv</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Vigane edastusriba värvi väärtus</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Päise logo</string>
|
||||
<string name="revanced_header_logo_entry_1">Vaikimisi</string>
|
||||
<string name="revanced_header_logo_entry_2">Tavaline</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced minimaalne</string>
|
||||
<string name="revanced_header_logo_entry_6">Kohandatud</string>
|
||||
</patch>
|
||||
@@ -1631,6 +1646,8 @@ AV1-ga videotaasesitus võib hakitud olla või kaadreid vahele jätta."</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Teave</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Reklaamid</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,11 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
</patch>
|
||||
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
|
||||
@@ -203,8 +209,10 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.theme.themePatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
|
||||
</patch>
|
||||
@@ -253,6 +261,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
</patch>
|
||||
<patch id="ad.video.hideVideoAdsPatch">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,11 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">بررسی ناموفق بود</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">رفتن به وبسایت رسمی</string>
|
||||
@@ -253,8 +259,10 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
<patch id="layout.theme.themePatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
|
||||
</patch>
|
||||
@@ -303,6 +311,8 @@ Second \"item\" text"</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
</patch>
|
||||
<patch id="ad.video.hideVideoAdsPatch">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Sovelluksen nimi</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Mukautettu</string>
|
||||
<string name="revanced_custom_branding_icon_title">Sovelluskuvake</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Alkuperäinen</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced, minimaalinen</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced, skaalattu</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Mukautettu</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Tarkastuksia epäonnistui</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Avaa virallinen sivusto</string>
|
||||
@@ -1145,13 +1158,13 @@ UserID on kuin salasana, eikä sitä pidä jakaa kenellekään.
|
||||
<string name="revanced_sb_segments_highlight_sum">Se osa videota, jota useimmat ihmiset etsivät</string>
|
||||
<string name="revanced_sb_segments_intro">Tauko/Introanimaatio</string>
|
||||
<string name="revanced_sb_segments_intro_sum">Aikaväli ilman varsinaista sisältöä. Voi olla tauko, staattinen kehys tai toistuva animaatio. Ei sisällä siirtymiä, jotka sisältävät tietoa</string>
|
||||
<string name="revanced_sb_segments_outro">Loppukortit / Tekijätiedot</string>
|
||||
<string name="revanced_sb_segments_outro">Loppukortit / -tekstit</string>
|
||||
<string name="revanced_sb_segments_outro_sum">Lopputekstit tai kun YouTuben loppukortit tulevat näkyviin. Ei lopetuksille, joissa on tietoa</string>
|
||||
<string name="revanced_sb_segments_hook">Koukku / Tervehdykset</string>
|
||||
<string name="revanced_sb_segments_hook_sum">Kertovat trailerit tulevasta videosta, tervehdykset ja hyvästelyt. Ei sisällä osioita, jotka lisäävät lisäsisältöä</string>
|
||||
<string name="revanced_sb_segments_preview">Esikatselu / Yhteenveto</string>
|
||||
<string name="revanced_sb_segments_preview">Esikatselu / Kertaus</string>
|
||||
<string name="revanced_sb_segments_preview_sum">Kokoelma leikkeitä, jotka osoittavat, mitä on tulossa tai mitä tapahtui videossa tai muissa sarjan videoissa, joiden kaikki informaatio toistuu muualla</string>
|
||||
<string name="revanced_sb_segments_filler">Tangentti / Vitsit</string>
|
||||
<string name="revanced_sb_segments_filler">Toissijaiset kohtaukset / Vitsit</string>
|
||||
<string name="revanced_sb_segments_filler_sum">Epäolennaiset kohtaukset tai vitsit, joita ei tarvita videon pääsisällön ymmärtämiseen. Ei sisällä osioita, jotka tarjoavat kontekstia tai taustatietoja</string>
|
||||
<string name="revanced_sb_segments_nomusic">Musiikki: Musiikiton osa</string>
|
||||
<string name="revanced_sb_segments_nomusic_sum">Vain musiikkivideoille. Musiikkivideoiden osiot ilman musiikkia, jotka eivät jo kuulu toiseen kategoriaan</string>
|
||||
@@ -1169,7 +1182,7 @@ UserID on kuin salasana, eikä sitä pidä jakaa kenellekään.
|
||||
<string name="revanced_sb_skip_button_preview_beginning">Ohita esikatselu</string>
|
||||
<string name="revanced_sb_skip_button_preview_middle">Ohita esikatselu</string>
|
||||
<string name="revanced_sb_skip_button_preview_end">Ohita kertaus</string>
|
||||
<string name="revanced_sb_skip_button_filler">Ohita Tangentti</string>
|
||||
<string name="revanced_sb_skip_button_filler">Ohita toissijainen kohtaus</string>
|
||||
<string name="revanced_sb_skip_button_nomusic">Ohita musiikiton</string>
|
||||
<string name="revanced_sb_skip_button_unsubmitted">Ohita osio</string>
|
||||
<string name="revanced_sb_skipped_sponsor">Sponsori ohitettiin</string>
|
||||
@@ -1180,11 +1193,11 @@ UserID on kuin salasana, eikä sitä pidä jakaa kenellekään.
|
||||
<string name="revanced_sb_skipped_intro_middle">Tauko ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_intro_end">Tauko ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_outro">Outro ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_hook">Ohitettu koukku</string>
|
||||
<string name="revanced_sb_skipped_hook">Koukku ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_preview_beginning">Esikatselu ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_preview_middle">Esikatselu ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_preview_end">Kertaus ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_filler">Ohitettu tangentti</string>
|
||||
<string name="revanced_sb_skipped_filler">Toissijainen kohtaus ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_nomusic">Musiikiton osio ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_unsubmitted">Lähettämätön osio ohitettiin</string>
|
||||
<string name="revanced_sb_skipped_multiple_segments">Useita osioita ohitettiin</string>
|
||||
@@ -1241,7 +1254,7 @@ Oletko valmis lähettämään?"</string>
|
||||
<string name="revanced_sb_new_segment_edit_by_hand_parse_error">Annettu aika on virheellinen</string>
|
||||
<string name="revanced_sb_stats_title">Tilastot</string>
|
||||
<!-- Shown in the settings preferences, and translations can be any text length. -->
|
||||
<string name="revanced_sb_stats_connection_failure">Tilastot eivät tilapäisesti saatavilla (API ei ole käytettävissä)</string>
|
||||
<string name="revanced_sb_stats_connection_failure">Tilastot eivät ole tilapäisesti saatavilla (API ei ole käytettävissä)</string>
|
||||
<string name="revanced_sb_stats_loading">Ladataan...</string>
|
||||
<string name="revanced_sb_stats_sb_disabled">SponsorBlock ei ole käytössä</string>
|
||||
<string name="revanced_sb_stats_username">Käyttäjänimesi: <b>%s</b></string>
|
||||
@@ -1252,11 +1265,11 @@ Oletko valmis lähettämään?"</string>
|
||||
<string name="revanced_sb_stats_submissions">Olet luonut <b>%s</b> osiota</string>
|
||||
<string name="revanced_sb_stats_submissions_sum">Napauta tästä nähdäksesi osiosi</string>
|
||||
<string name="revanced_sb_stats_saved_zero">SponsorBlock-tulostaulu</string>
|
||||
<string name="revanced_sb_stats_saved">Olet pelastanut ihmisiä <b>%s</b> segmentiltä</string>
|
||||
<string name="revanced_sb_stats_saved">Olet pelastanut ihmisiä <b>%s</b> osiolta</string>
|
||||
<string name="revanced_sb_stats_saved_sum_zero">Napauta tästä nähdäksesi globaalit tilastot ja parhaat osallistujat</string>
|
||||
<string name="revanced_sb_stats_saved_sum">Se on <b>%s</b> heidän elämistään.<br>Napauta tästä nähdäksesi tulostaulun</string>
|
||||
<string name="revanced_sb_stats_self_saved">Olet ohittanut <b>%s</b> osiota</string>
|
||||
<string name="revanced_sb_stats_self_saved_sum">Tuo on <b>%s</b></string>
|
||||
<string name="revanced_sb_stats_self_saved_sum">Se on <b>%s</b></string>
|
||||
<string name="revanced_sb_stats_self_saved_reset_title">Nollataanko ohitettujen osioiden laskuri?</string>
|
||||
<string name="revanced_sb_stats_saved_hour_format">%1$s tuntia %2$s minuuttia</string>
|
||||
<string name="revanced_sb_stats_saved_minute_format">%1$s minuuttia %2$s sekuntia</string>
|
||||
@@ -1410,12 +1423,14 @@ Minisoitin voidaan vetää pois näytöltä vasemmalle tai oikealle"</string>
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Etenemispalkin korostusväri</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Virheellinen etenemispalkin väriarvo</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Ylätunnisteen logo</string>
|
||||
<string name="revanced_header_logo_entry_1">Oletus</string>
|
||||
<string name="revanced_header_logo_entry_2">Tavallinen</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced-minimaalinen</string>
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced, minimaalinen</string>
|
||||
<string name="revanced_header_logo_entry_6">Mukautettu</string>
|
||||
</patch>
|
||||
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
|
||||
@@ -1607,11 +1622,11 @@ Rajoitukset:
|
||||
<string name="revanced_spoof_video_streams_av1_title">Salli Android VR AV1</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_on">"Videokoodekki on AVC (H.264), VP9 tai AV1
|
||||
|
||||
Toisto voi nykiä tai pudottaa kehyksiä"</string>
|
||||
Toisto saattaa pätkiä"</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_off">Videokoodekki on AVC (H.264) tai VP9</string>
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"Tämän asetuksen ottaminen käyttöön saattaa käyttää ohjelmistopohjaista AV1-dekoodausta.
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"Tämän asetuksen käyttöönotto saattaa käyttää ohjelmistopohjaista AV1-dekoodausta.
|
||||
|
||||
AV1-videon toisto saattaa pätkiä tai pudottaa kuvia."</string>
|
||||
AV1-videon toisto saattaa pätkiä."</string>
|
||||
<string name="revanced_spoof_video_streams_about_title">Naamioimisen sivuvaikutukset</string>
|
||||
<string name="revanced_spoof_video_streams_about_experimental">• Kokeellinen asiakasohjelma, joka saattaa lakata toimimasta milloin tahansa</string>
|
||||
<string name="revanced_spoof_video_streams_about_playback_failure">• Video saattaa pysähtyä aikaan 1:00, tai ei välttämättä ole saatavilla joillakin alueilla</string>
|
||||
@@ -1631,6 +1646,8 @@ AV1-videon toisto saattaa pätkiä tai pudottaa kuvia."</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Tietoja</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Mainokset</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Pangalan ng app</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Pasadyang</string>
|
||||
<string name="revanced_custom_branding_icon_title">Icon ng app</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Orihinal</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimal</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced naka-scale</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Pasadyang</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Nabigo ang mga pagsusuri</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Buksan ang Opisyal na Website</string>
|
||||
@@ -1408,11 +1421,13 @@ Ang \"miniplayer\" ay maaaring i-drag palabas ng screen sa kaliwa o kanan"</stri
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">Ang accent na kulay ng seekbar</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Hindi wastong halaga ng kulay ng seekbar</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Logo ng Header</string>
|
||||
<string name="revanced_header_logo_entry_1">Default</string>
|
||||
<string name="revanced_header_logo_entry_2">Regular</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced minimal</string>
|
||||
<string name="revanced_header_logo_entry_6">Pasadyang</string>
|
||||
</patch>
|
||||
@@ -1629,6 +1644,8 @@ Maaaring mag-stutter o mag-drop ng frames ang pag-playback ng video na may AV1."
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">Tungkol</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Mga Ad</string>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
|
||||
<!--
|
||||
|
||||
All strings must have a unique path, even if the same string is declared in two different apps.
|
||||
@@ -20,6 +21,18 @@ Second \"item\" text"</string>
|
||||
-->
|
||||
<resources>
|
||||
<app id="shared">
|
||||
<patch id="layout.branding.baseCustomBrandingPatch">
|
||||
<string name="revanced_custom_branding_name_title">Nom de l\'application</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_icon_entry_5 -->
|
||||
<string name="revanced_custom_branding_name_entry_5">Personnalisé</string>
|
||||
<string name="revanced_custom_branding_icon_title">Icône de l\'application</string>
|
||||
<string name="revanced_custom_branding_icon_entry_1">Originale</string>
|
||||
<!-- Translation of this should be identical to revanced_header_logo_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_3">ReVanced minimaliste</string>
|
||||
<string name="revanced_custom_branding_icon_entry_4">ReVanced mise à l\'échelle</string>
|
||||
<!-- Translations of this should be identical to revanced_custom_branding_name_entry_5 -->
|
||||
<string name="revanced_custom_branding_icon_entry_5">Personnalisée</string>
|
||||
</patch>
|
||||
<patch id="misc.checks.checkEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">Les vérifications ont échoué</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">Ouvrir le site officiel</string>
|
||||
@@ -1350,7 +1363,7 @@ Limitation : Il se peut que le bouton Retour dans la barre d'outils ne fonction
|
||||
</patch>
|
||||
<patch id="layout.miniplayer.miniplayerPatch">
|
||||
<string name="revanced_miniplayer_screen_title">Lecteur réduit</string>
|
||||
<string name="revanced_miniplayer_screen_summary">Modifier le style du lecteur réduit à l\'intérieur de l\'application</string>
|
||||
<string name="revanced_miniplayer_screen_summary">Modifiez le style du lecteur réduit intégré à l\'application</string>
|
||||
<string name="revanced_miniplayer_type_title">Type de lecteur réduit</string>
|
||||
<string name="revanced_miniplayer_type_entry_0">Désactivé</string>
|
||||
<string name="revanced_miniplayer_type_entry_1">Par défaut</string>
|
||||
@@ -1411,11 +1424,13 @@ Le lecteur réduit peut être glissé hors de l'écran, à gauche comme à droit
|
||||
<string name="revanced_seekbar_custom_color_accent_summary">La couleur d\'accentuation de la barre de progression</string>
|
||||
<string name="revanced_seekbar_custom_color_invalid">Valeur invalide pour la couleur de la barre de progression</string>
|
||||
</patch>
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="layout.branding.changeHeaderPatch">
|
||||
<string name="revanced_header_logo_title">Logo d\'en-tête</string>
|
||||
<string name="revanced_header_logo_entry_1">Par défaut</string>
|
||||
<string name="revanced_header_logo_entry_2">Standard</string>
|
||||
<!-- For this situation "Minimal" means minimalistic. It does not mean small or tiny. -->
|
||||
<!-- Translation of this should be identical to revanced_custom_branding_icon_entry_3 -->
|
||||
<string name="revanced_header_logo_entry_5">ReVanced minimaliste</string>
|
||||
<string name="revanced_header_logo_entry_6">Personnalisé</string>
|
||||
</patch>
|
||||
@@ -1608,11 +1623,11 @@ Limitations :
|
||||
<string name="revanced_spoof_video_streams_av1_title">Autoriser Android VR AV1</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_on">"Le codec vidéo est AVC (H.264), VP9 ou AV1
|
||||
|
||||
La lecture peut saccader ou perdre des images"</string>
|
||||
La lecture peut être saccadée et des images peuvent être perdues"</string>
|
||||
<string name="revanced_spoof_video_streams_av1_summary_off">Le codec vidéo est AVC (H.264) ou VP9</string>
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"L'activation de ce paramètre peut utiliser le décodage AV1 logiciel.
|
||||
<string name="revanced_spoof_video_streams_av1_user_dialog_message">"L'activation de ce paramètre permet l'utilisation du décodage AV1 logiciel.
|
||||
|
||||
La lecture vidéo avec AV1 peut saccader ou perdre des images."</string>
|
||||
La lecture vidéo avec AV1 peut être saccadée et des images peuvent être perdues."</string>
|
||||
<string name="revanced_spoof_video_streams_about_title">Effets secondaires de la falsification</string>
|
||||
<string name="revanced_spoof_video_streams_about_experimental">• Client expérimental, peut cesser de fonctionner à tout moment</string>
|
||||
<string name="revanced_spoof_video_streams_about_playback_failure">• Les vidéos sont susceptibles de s\'arrêter à 1:00, ou de ne pas être disponibles dans certaines régions</string>
|
||||
@@ -1632,6 +1647,8 @@ La lecture vidéo avec AV1 peut saccader ou perdre des images."</string>
|
||||
</patch>
|
||||
</app>
|
||||
<app id="music">
|
||||
<patch id="layout.branding.customBrandingPatch">
|
||||
</patch>
|
||||
<patch id="misc.settings.settingsPatch">
|
||||
<string name="revanced_settings_music_screen_0_about_title">À propos</string>
|
||||
<string name="revanced_settings_music_screen_1_ads_title">Annonces</string>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user