Compare commits

...

46 Commits

Author SHA1 Message Date
semantic-release-bot
4dcd487275 chore(release): 2.76.2 [skip ci]
## [2.76.2](https://github.com/revanced/revanced-patches/compare/v2.76.1...v2.76.2) (2022-09-30)

### Bug Fixes

* **twitter/monochrome-icon:** add missing `File.write` call ([#682](https://github.com/revanced/revanced-patches/issues/682)) ([7f78d6f](68eff6fd76))
2022-09-30 18:02:24 +00:00
Ushie
68eff6fd76 fix(twitter/monochrome-icon): add missing File.write call (#682) 2022-09-30 20:00:29 +02:00
semantic-release-bot
9b58c642f3 chore(release): 2.76.1 [skip ci]
## [2.76.1](https://github.com/revanced/revanced-patches/compare/v2.76.0...v2.76.1) (2022-09-30)

### Bug Fixes

* **disable-startup-shorts-player:** remove redundant opcode pattern ([#679](https://github.com/revanced/revanced-patches/issues/679)) ([2774c0f](f9c16396d6))
2022-09-30 17:59:47 +00:00
OxrxL
f9c16396d6 fix(disable-startup-shorts-player): remove redundant opcode pattern (#679)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-09-30 19:58:09 +02:00
semantic-release-bot
7c528856e1 chore(release): 2.76.0 [skip ci]
# [2.76.0](https://github.com/revanced/revanced-patches/compare/v2.75.2...v2.76.0) (2022-09-30)

### Features

* bump YouTube Music patches compatibility to v5.26.52 ([#681](https://github.com/revanced/revanced-patches/issues/681)) ([24bfab8](29c18ad537))
2022-09-30 14:52:51 +00:00
Palm
29c18ad537 feat: bump YouTube Music patches compatibility to v5.26.52 (#681) 2022-09-30 16:50:36 +02:00
Sculas
1654d4121b refactor(twitter/dynamic-color): remove formatting in XML (#680) 2022-09-30 16:49:07 +02:00
semantic-release-bot
05c6c9e683 chore(release): 2.75.2 [skip ci]
## [2.75.2](https://github.com/revanced/revanced-patches/compare/v2.75.1...v2.75.2) (2022-09-30)

### Bug Fixes

* **twitter patches.:** use wider compatible Java API for writing to file ([#678](https://github.com/revanced/revanced-patches/issues/678)) ([bbf5ccf](facb808e37))
2022-09-30 14:39:21 +00:00
Aunali321
facb808e37 fix(twitter patches.): use wider compatible Java API for writing to file (#678)
Allows running the patch on Android
2022-09-30 16:37:40 +02:00
semantic-release-bot
fe1427b88a chore(release): 2.75.1 [skip ci]
## [2.75.1](https://github.com/revanced/revanced-patches/compare/v2.75.0...v2.75.1) (2022-09-29)

### Bug Fixes

* **disable-startup-shorts-player:** incorrect offsets, invert branch condition ([#672](https://github.com/revanced/revanced-patches/issues/672)) ([c5a772b](634c6c3344))
2022-09-29 22:27:30 +00:00
OxrxL
634c6c3344 fix(disable-startup-shorts-player): incorrect offsets, invert branch condition (#672) 2022-09-30 00:25:29 +02:00
semantic-release-bot
d438d9b866 chore(release): 2.75.0 [skip ci]
# [2.75.0](https://github.com/revanced/revanced-patches/compare/v2.74.0...v2.75.0) (2022-09-29)

### Features

* `disable-capture-restriction` patch ([#655](https://github.com/revanced/revanced-patches/issues/655)) ([87cf71a](e4139a112e))
2022-09-29 22:01:52 +00:00
Tim Schneeberger
e4139a112e feat: disable-capture-restriction patch (#655)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-09-30 00:00:16 +02:00
semantic-release-bot
559d7ad50c chore(release): 2.74.0 [skip ci]
# [2.74.0](https://github.com/revanced/revanced-patches/compare/v2.73.0...v2.74.0) (2022-09-29)

### Features

* `disable-startup-shorts-player` patch ([#670](https://github.com/revanced/revanced-patches/issues/670)) ([c4f4d4a](e1bbe59b40))
2022-09-29 21:56:14 +00:00
OxrxL
e1bbe59b40 feat: disable-startup-shorts-player patch (#670)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2022-09-29 23:53:58 +02:00
semantic-release-bot
2f932d697b chore(release): 2.73.0 [skip ci]
# [2.73.0](https://github.com/revanced/revanced-patches/compare/v2.72.1...v2.73.0) (2022-09-29)

### Features

* `dynamic-color` patch ([#652](https://github.com/revanced/revanced-patches/issues/652)) ([47d4b90](237129d327))
2022-09-29 20:09:46 +00:00
bennett-sh
237129d327 feat: dynamic-color patch (#652) 2022-09-29 22:08:00 +02:00
oSumAtrIX
e4d7dc0aa9 chore: bump patcher dependency version 2022-09-29 21:42:45 +02:00
semantic-release-bot
fed9e4aafa chore(release): 2.72.1 [skip ci]
## [2.72.1](https://github.com/revanced/revanced-patches/compare/v2.72.0...v2.72.1) (2022-09-29)

### Bug Fixes

* **hdr-brightness:** trim list of compatible versions ([#602](https://github.com/revanced/revanced-patches/issues/602)) ([eb900b1](85aa45d3f2))
2022-09-29 17:26:19 +00:00
OxrxL
85aa45d3f2 fix(hdr-brightness): trim list of compatible versions (#602) 2022-09-29 19:24:32 +02:00
semantic-release-bot
16df8d5ebc chore(release): 2.72.0 [skip ci]
# [2.72.0](https://github.com/revanced/revanced-patches/compare/v2.71.2...v2.72.0) (2022-09-29)

### Features

* `tiktok-speed` patch ([#668](https://github.com/revanced/revanced-patches/issues/668)) ([805d301](0f78f621c7))
* bump YouTube Music patches to v5.25.51 ([#669](https://github.com/revanced/revanced-patches/issues/669)) ([ca88b01](e89c3dad39))
2022-09-29 15:27:16 +00:00
sandip93291
e89c3dad39 feat: bump YouTube Music patches to v5.25.51 (#669) 2022-09-29 17:25:01 +02:00
d4rkk3y
0f78f621c7 feat: tiktok-speed patch (#668) 2022-09-29 17:23:58 +02:00
semantic-release-bot
fe30cea22d chore(release): 2.71.2 [skip ci]
## [2.71.2](https://github.com/revanced/revanced-patches/compare/v2.71.1...v2.71.2) (2022-09-28)

### Bug Fixes

* **hide-email-address:** invalid instruction offsets ([#654](https://github.com/revanced/revanced-patches/issues/654)) ([6c8f447](5a30d8564c))
2022-09-28 13:44:59 +00:00
OxrxL
5a30d8564c fix(hide-email-address): invalid instruction offsets (#654) 2022-09-28 15:42:35 +02:00
semantic-release-bot
34222d9f2c chore(release): 2.71.1 [skip ci]
## [2.71.1](https://github.com/revanced/revanced-patches/compare/v2.71.0...v2.71.1) (2022-09-28)

### Bug Fixes

* **disable-auto-player-popup-panels:** swap switch toggle state description ([#653](https://github.com/revanced/revanced-patches/issues/653)) ([632323a](f765af0929))
2022-09-28 13:24:22 +00:00
NRJ YDV
f765af0929 fix(disable-auto-player-popup-panels): swap switch toggle state description (#653) 2022-09-28 15:21:55 +02:00
semantic-release-bot
298c33deb6 chore(release): 2.71.0 [skip ci]
# [2.71.0](https://github.com/revanced/revanced-patches/compare/v2.70.0...v2.71.0) (2022-09-28)

### Features

* `hide-email-address` patch ([#578](https://github.com/revanced/revanced-patches/issues/578)) ([9ed8f94](111db2a499))
2022-09-28 11:29:51 +00:00
OxrxL
111db2a499 feat: hide-email-address patch (#578) 2022-09-28 13:28:07 +02:00
semantic-release-bot
fa536957cc chore(release): 2.70.0 [skip ci]
# [2.70.0](https://github.com/revanced/revanced-patches/compare/v2.69.4...v2.70.0) (2022-09-28)

### Features

* `monochrome-icon` patch ([#643](https://github.com/revanced/revanced-patches/issues/643)) ([b744c37](980deec6af))
2022-09-28 11:16:24 +00:00
bennett-sh
980deec6af feat: monochrome-icon patch (#643) 2022-09-28 13:14:39 +02:00
semantic-release-bot
658638bfad chore(release): 2.69.4 [skip ci]
## [2.69.4](https://github.com/revanced/revanced-patches/compare/v2.69.3...v2.69.4) (2022-09-27)

### Bug Fixes

* **sponsorblock:** dynamically insert `setSponsorBarRect` call ([#644](https://github.com/revanced/revanced-patches/issues/644)) ([88940dd](d5ee3b006d))
2022-09-27 20:02:25 +00:00
d4rkk3y
d5ee3b006d fix(sponsorblock): dynamically insert setSponsorBarRect call (#644) 2022-09-27 22:00:24 +02:00
semantic-release-bot
36ab007924 chore(release): 2.69.3 [skip ci]
## [2.69.3](https://github.com/revanced/revanced-patches/compare/v2.69.2...v2.69.3) (2022-09-27)

### Bug Fixes

* **sponsorblock:** correct class name for field reference ([#582](https://github.com/revanced/revanced-patches/issues/582)) ([c584092](31ae362d18))
2022-09-27 13:32:48 +00:00
semantic-release-bot
c46705f21a chore(release): 2.69.3 [skip ci]
## [2.69.3](https://github.com/revanced/revanced-patches/compare/v2.69.2...v2.69.3) (2022-09-27)

### Bug Fixes

* **sponsorblock:** correct class name for field reference ([#582](https://github.com/revanced/revanced-patches/issues/582)) ([c584092](31ae362d18))
2022-09-27 04:57:01 +00:00
inotia00
31ae362d18 fix(sponsorblock): correct class name for field reference (#582) 2022-09-27 06:55:15 +02:00
semantic-release-bot
b64f7bb400 chore(release): 2.69.2 [skip ci]
## [2.69.2](https://github.com/revanced/revanced-patches/compare/v2.69.1...v2.69.2) (2022-09-26)

### Performance Improvements

* **resource-mapping:** map resources with multiple threads ([81e86c4](64244ec476))
2022-09-26 02:33:00 +00:00
oSumAtrIX
64244ec476 perf(resource-mapping): map resources with multiple threads 2022-09-26 04:30:49 +02:00
oSumAtrIX
f005314358 chore: bump patcher dependency version 2022-09-26 04:30:49 +02:00
shadow578
ae1aeffc62 refactor(microg-support): share code between music-microg-support patch 2022-09-26 04:30:49 +02:00
semantic-release-bot
56768caa4c chore(release): 2.69.1 [skip ci]
## [2.69.1](https://github.com/revanced/revanced-patches/compare/v2.69.0...v2.69.1) (2022-09-24)

### Bug Fixes

* **sponsorblock:** reflect changes to strings ([#585](https://github.com/revanced/revanced-patches/issues/585)) ([ee3a9c5](19484ca2bc))
2022-09-24 21:46:59 +00:00
Michael M. Chang
19484ca2bc fix(sponsorblock): reflect changes to strings (#585) 2022-09-24 23:45:02 +02:00
semantic-release-bot
a8a98646e7 chore(release): 2.69.0 [skip ci]
# [2.69.0](https://github.com/revanced/revanced-patches/compare/v2.68.3...v2.69.0) (2022-09-24)

### Features

* `spotify-theme` patch ([#608](https://github.com/revanced/revanced-patches/issues/608)) ([468ec3e](4927bc7451))
2022-09-24 13:45:49 +00:00
bogadana
4927bc7451 feat: spotify-theme patch (#608) 2022-09-24 15:44:04 +02:00
semantic-release-bot
66a5ca3fa8 chore(release): 2.68.3 [skip ci]
## [2.68.3](https://github.com/revanced/revanced-patches/compare/v2.68.2...v2.68.3) (2022-09-23)

### Bug Fixes

* **seekbar-tapping:** do not disable seekbar when hiding it ([#600](https://github.com/revanced/revanced-patches/issues/600)) ([f251ae7](6c0b9213fe))
2022-09-23 19:11:44 +00:00
OxrxL
6c0b9213fe fix(seekbar-tapping): do not disable seekbar when hiding it (#600) 2022-09-23 21:09:47 +02:00
62 changed files with 1635 additions and 521 deletions

View File

@@ -1,3 +1,151 @@
## [2.76.2](https://github.com/revanced/revanced-patches/compare/v2.76.1...v2.76.2) (2022-09-30)
### Bug Fixes
* **twitter/monochrome-icon:** add missing `File.write` call ([#682](https://github.com/revanced/revanced-patches/issues/682)) ([de22e3f](https://github.com/revanced/revanced-patches/commit/de22e3f03ef1d6db08f9446f02e687721d9017d7))
## [2.76.1](https://github.com/revanced/revanced-patches/compare/v2.76.0...v2.76.1) (2022-09-30)
### Bug Fixes
* **disable-startup-shorts-player:** remove redundant opcode pattern ([#679](https://github.com/revanced/revanced-patches/issues/679)) ([5197a24](https://github.com/revanced/revanced-patches/commit/5197a24cc5ecc1b51cdc5c00c77c873b86394994))
# [2.76.0](https://github.com/revanced/revanced-patches/compare/v2.75.2...v2.76.0) (2022-09-30)
### Features
* bump YouTube Music patches compatibility to v5.26.52 ([#681](https://github.com/revanced/revanced-patches/issues/681)) ([f195734](https://github.com/revanced/revanced-patches/commit/f195734925dd41596d058f9cbf2b7e619a8d5833))
## [2.75.2](https://github.com/revanced/revanced-patches/compare/v2.75.1...v2.75.2) (2022-09-30)
### Bug Fixes
* **twitter patches.:** use wider compatible Java API for writing to file ([#678](https://github.com/revanced/revanced-patches/issues/678)) ([4448de8](https://github.com/revanced/revanced-patches/commit/4448de80a6277862d81c7a29b9489e4fbec48496))
## [2.75.1](https://github.com/revanced/revanced-patches/compare/v2.75.0...v2.75.1) (2022-09-29)
### Bug Fixes
* **disable-startup-shorts-player:** incorrect offsets, invert branch condition ([#672](https://github.com/revanced/revanced-patches/issues/672)) ([bfe1e38](https://github.com/revanced/revanced-patches/commit/bfe1e3808ece85fbb0785a4378ee95591115ac33))
# [2.75.0](https://github.com/revanced/revanced-patches/compare/v2.74.0...v2.75.0) (2022-09-29)
### Features
* `disable-capture-restriction` patch ([#655](https://github.com/revanced/revanced-patches/issues/655)) ([3cc26c3](https://github.com/revanced/revanced-patches/commit/3cc26c31d378c27ca7f768f777daa00e3f849dff))
# [2.74.0](https://github.com/revanced/revanced-patches/compare/v2.73.0...v2.74.0) (2022-09-29)
### Features
* `disable-startup-shorts-player` patch ([#670](https://github.com/revanced/revanced-patches/issues/670)) ([feb3bd0](https://github.com/revanced/revanced-patches/commit/feb3bd02aaf67379733d10988fd58b0c3924f88e))
# [2.73.0](https://github.com/revanced/revanced-patches/compare/v2.72.1...v2.73.0) (2022-09-29)
### Features
* `dynamic-color` patch ([#652](https://github.com/revanced/revanced-patches/issues/652)) ([a16575b](https://github.com/revanced/revanced-patches/commit/a16575b984354138b9ab175307be8d15de60b6a6))
## [2.72.1](https://github.com/revanced/revanced-patches/compare/v2.72.0...v2.72.1) (2022-09-29)
### Bug Fixes
* **hdr-brightness:** trim list of compatible versions ([#602](https://github.com/revanced/revanced-patches/issues/602)) ([3209d90](https://github.com/revanced/revanced-patches/commit/3209d902e4d1aa73b59ce059cf3f76ef2095af70))
# [2.72.0](https://github.com/revanced/revanced-patches/compare/v2.71.2...v2.72.0) (2022-09-29)
### Features
* `tiktok-speed` patch ([#668](https://github.com/revanced/revanced-patches/issues/668)) ([23fff16](https://github.com/revanced/revanced-patches/commit/23fff16e6ab02bf281d46d8b5f93788425d8b525))
* bump YouTube Music patches to v5.25.51 ([#669](https://github.com/revanced/revanced-patches/issues/669)) ([563c846](https://github.com/revanced/revanced-patches/commit/563c8466568d578de7c8a8e869fb1aa74370784a))
## [2.71.2](https://github.com/revanced/revanced-patches/compare/v2.71.1...v2.71.2) (2022-09-28)
### Bug Fixes
* **hide-email-address:** invalid instruction offsets ([#654](https://github.com/revanced/revanced-patches/issues/654)) ([1a3db44](https://github.com/revanced/revanced-patches/commit/1a3db44b5bd9628b7b25cc113a3a53bf8a85bd2b))
## [2.71.1](https://github.com/revanced/revanced-patches/compare/v2.71.0...v2.71.1) (2022-09-28)
### Bug Fixes
* **disable-auto-player-popup-panels:** swap switch toggle state description ([#653](https://github.com/revanced/revanced-patches/issues/653)) ([f881301](https://github.com/revanced/revanced-patches/commit/f88130143689c7a63fd67df3dff37caec5db9548))
# [2.71.0](https://github.com/revanced/revanced-patches/compare/v2.70.0...v2.71.0) (2022-09-28)
### Features
* `hide-email-address` patch ([#578](https://github.com/revanced/revanced-patches/issues/578)) ([82cb632](https://github.com/revanced/revanced-patches/commit/82cb6321beace1e5feed248d3f3d6ae56cf0d96b))
# [2.70.0](https://github.com/revanced/revanced-patches/compare/v2.69.4...v2.70.0) (2022-09-28)
### Features
* `monochrome-icon` patch ([#643](https://github.com/revanced/revanced-patches/issues/643)) ([127c8e5](https://github.com/revanced/revanced-patches/commit/127c8e54e54709c2716029044ae337ad53daafad))
## [2.69.4](https://github.com/revanced/revanced-patches/compare/v2.69.3...v2.69.4) (2022-09-27)
### Bug Fixes
* **sponsorblock:** dynamically insert `setSponsorBarRect` call ([#644](https://github.com/revanced/revanced-patches/issues/644)) ([998a249](https://github.com/revanced/revanced-patches/commit/998a249a23d09eb752b35c4da731f4223be40a3b))
## [2.69.3](https://github.com/revanced/revanced-patches/compare/v2.69.2...v2.69.3) (2022-09-27)
### Bug Fixes
* **sponsorblock:** correct class name for field reference ([#582](https://github.com/revanced/revanced-patches/issues/582)) ([7cd585c](https://github.com/revanced/revanced-patches/commit/7cd585cd78e284a82cee17d09f8049f0a5cdf2e8))
## [2.69.3](https://github.com/revanced/revanced-patches/compare/v2.69.2...v2.69.3) (2022-09-27)
### Bug Fixes
* **sponsorblock:** correct class name for field reference ([#582](https://github.com/revanced/revanced-patches/issues/582)) ([7cd585c](https://github.com/revanced/revanced-patches/commit/7cd585cd78e284a82cee17d09f8049f0a5cdf2e8))
## [2.69.2](https://github.com/revanced/revanced-patches/compare/v2.69.1...v2.69.2) (2022-09-26)
### Performance Improvements
* **resource-mapping:** map resources with multiple threads ([a7e4da0](https://github.com/revanced/revanced-patches/commit/a7e4da018bf939accdf6d406b471ac74f9078095))
## [2.69.1](https://github.com/revanced/revanced-patches/compare/v2.69.0...v2.69.1) (2022-09-24)
### Bug Fixes
* **sponsorblock:** reflect changes to strings ([#585](https://github.com/revanced/revanced-patches/issues/585)) ([d03568a](https://github.com/revanced/revanced-patches/commit/d03568aa39195a07bec62f43d929923825c67d3f))
# [2.69.0](https://github.com/revanced/revanced-patches/compare/v2.68.3...v2.69.0) (2022-09-24)
### Features
* `spotify-theme` patch ([#608](https://github.com/revanced/revanced-patches/issues/608)) ([7ee1b78](https://github.com/revanced/revanced-patches/commit/7ee1b78e8048698ef6490445dd012e2d88b4d332))
## [2.68.3](https://github.com/revanced/revanced-patches/compare/v2.68.2...v2.68.3) (2022-09-23)
### Bug Fixes
* **seekbar-tapping:** do not disable seekbar when hiding it ([#600](https://github.com/revanced/revanced-patches/issues/600)) ([68a9457](https://github.com/revanced/revanced-patches/commit/68a9457464c786a61b756eb18ca5f1ce05316636))
## [2.68.2](https://github.com/revanced/revanced-patches/compare/v2.68.1...v2.68.2) (2022-09-23) ## [2.68.2](https://github.com/revanced/revanced-patches/compare/v2.68.1...v2.68.2) (2022-09-23)

152
README.md
View File

@@ -4,52 +4,23 @@ The official Patch bundle provided by ReVanced and the community.
> Looking for the JSON variant of this? [Click here](patches.json). > Looking for the JSON variant of this? [Click here](patches.json).
### 📦 `com.google.android.youtube` ### 📦 `com.reddit.frontpage`
<details> <details>
| 💊 Patch | 📜 Description | 🏹 Target Version | | 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:| |:--------:|:--------------:|:-----------------:|
| `custom-branding` | Changes the YouTube launcher icon and name to your choice (defaults to ReVanced). | all | | `general-reddit-ads` | Removes general ads from the Reddit frontpage and subreddits. | all |
| `premium-heading` | Shows premium branding on the home screen. | all | | `premium-icon-reddit` | Unlocking Premium Icons in reddit app. | all |
| `theme` | Applies a custom theme. | all |
| `old-quality-layout` | Enables the original quality flyout menu. | 17.36.37 |
| `hide-autoplay-button` | Hides the autoplay button in the video player. | 17.36.37 |
| `sponsorblock` | Integrate SponsorBlock. | 17.36.37 |
| `hide-watermark` | Hides creator's watermarks on videos. | 17.36.37 |
| `disable-fullscreen-panels` | Disables video description and comments panel in fullscreen view. | 17.36.37 |
| `hide-create-button` | Hides the create button in the navigation bar. | 17.36.37 |
| `hide-shorts-button` | Hides the shorts button on the navigation bar. | 17.36.37 |
| `tablet-mini-player` | Enables the tablet mini player layout. | 17.36.37 |
| `hide-time-and-seekbar` | Hides progress bar and time counter on videos. | 17.36.37 |
| `return-youtube-dislike` | Shows the dislike count of videos using the Return YouTube Dislike API. | 17.36.37 |
| `disable-auto-captions` | Disable forced captions from being automatically enabled. | 17.36.37 |
| `disable-auto-player-popup-panels` | Disable automatic popup panels (playlist or live chat) on video player. | 17.36.37 |
| `enable-wide-searchbar` | Replaces the search icon with a wide search bar. This will hide the YouTube logo when active. | 17.36.37 |
| `hide-cast-button` | Hides the cast button in the video player. | all |
| `video-ads` | Removes ads in the video player. | 17.36.37 |
| `general-ads` | Removes general ads. | 17.36.37 |
| `hide-infocard-suggestions` | Hides infocards in videos. | 17.36.37 |
| `seekbar-tapping` | Enables tap-to-seek on the seekbar of the video player. | 17.36.37 |
| `downloads` | Enables downloading music and videos from YouTube. | 17.36.37 |
| `swipe-controls` | Adds volume and brightness swipe controls. | 17.36.37 |
| `microg-support` | Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG. | 17.36.37 |
| `settings` | Adds settings for ReVanced to YouTube. | all |
| `minimized-playback` | Enables minimized and background playback. | 17.36.37 |
| `custom-video-buffer` | Lets you change the buffers of videos. | 17.36.37 |
| `custom-playback-speed` | Adds more video playback speed options. | 17.36.37 |
| `remember-video-quality` | Adds the ability to remember the video quality you chose in the video quality flyout. | 17.36.37 |
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
| `hdr-auto-brightness` | Makes the brightness of HDR videos follow the system default. | 17.36.37 |
| `enable-debugging` | Enables app debugging by patching the manifest file. | all |
| `always-autorepeat` | Always repeats the playing video again. | 17.36.37 |
</details> </details>
### 📦 `com.vanced.android.youtube` ### 📦 `com.spotify.music`
<details> <details>
| 💊 Patch | 📜 Description | 🏹 Target Version | | 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:| |:--------:|:--------------:|:-----------------:|
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all | | `hide-premium-navbar` | Removes the premium tab from the navbar. | all |
| `disable-capture-restriction` | Allows capturing Spotify's audio output while screen sharing or screen recording. | all |
| `spotify-theme` | Applies a custom theme. | all |
</details> </details>
### 📦 `de.dwd.warnapp` ### 📦 `de.dwd.warnapp`
@@ -60,32 +31,6 @@ The official Patch bundle provided by ReVanced and the community.
| `promo-code-unlock` | Disables the validation of promo code. Any code will work to unlock all features. | all | | `promo-code-unlock` | Disables the validation of promo code. Any code will work to unlock all features. | all |
</details> </details>
### 📦 `com.reddit.frontpage`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `premium-icon-reddit` | Unlocking Premium Icons in reddit app. | all |
| `general-reddit-ads` | Removes general ads from the Reddit frontpage and subreddits. | all |
</details>
### 📦 `com.google.android.apps.youtube.music`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `background-play` | Enables playing music in the background. | 5.23.50 |
| `minimized-playback-music` | Enables minimized playback on Kids music. | 5.23.50 |
| `compact-header` | Hides the music category bar at the top of the homepage. | 5.23.50 |
| `tasteBuilder-remover` | Removes the "Tell us which artists you like" card from the home screen. | 5.23.50 |
| `hide-get-premium` | Removes all "Get Premium" evidences from the avatar menu. | 5.23.50 |
| `upgrade-button-remover` | Removes the upgrade tab from the pivot bar. | 5.23.50 |
| `music-video-ads` | Removes ads in the music player. | 5.23.50 |
| `music-microg-support` | Allows YouTube Music ReVanced to run without root and under a different package name. | 5.23.50 |
| `codecs-unlock` | Adds more audio codec options. The new audio codecs usually result in better audio quality. | 5.23.50 |
| `exclusive-audio-playback` | Enables the option to play music without video. | 5.23.50 |
</details>
### 📦 `com.garzotto.pflotsh.ecmwf_a` ### 📦 `com.garzotto.pflotsh.ecmwf_a`
<details> <details>
@@ -99,12 +44,13 @@ The official Patch bundle provided by ReVanced and the community.
| 💊 Patch | 📜 Description | 🏹 Target Version | | 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:| |:--------:|:--------------:|:-----------------:|
| `tiktok-feed-filter` | Filters tiktok videos: removing ads, removing livestreams. | all | | `tiktok-speed` | Enables the playback speed option for all videos. | all |
| `tiktok-ads` | Removes ads from TikTok. | all |
| `tiktok-seekbar` | Show progress bar for all video. | all |
| `tiktok-download` | Removes download restrictions and changes the default path to download to. | all | | `tiktok-download` | Removes download restrictions and changes the default path to download to. | all |
| `tiktok-settings` | Add settings menu to TikTok. | all | | `tiktok-seekbar` | Show progress bar for all video. | all |
| `tiktok-force-login` | Do not force login. | all | | `tiktok-force-login` | Do not force login. | all |
| `tiktok-settings` | Add settings menu to TikTok. | all |
| `tiktok-ads` | Removes ads from TikTok. | all |
| `tiktok-feed-filter` | Filters tiktok videos: removing ads, removing livestreams. | all |
</details> </details>
### 📦 `com.zhiliaoapp.musically` ### 📦 `com.zhiliaoapp.musically`
@@ -112,12 +58,13 @@ The official Patch bundle provided by ReVanced and the community.
| 💊 Patch | 📜 Description | 🏹 Target Version | | 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:| |:--------:|:--------------:|:-----------------:|
| `tiktok-feed-filter` | Filters tiktok videos: removing ads, removing livestreams. | all | | `tiktok-speed` | Enables the playback speed option for all videos. | all |
| `tiktok-ads` | Removes ads from TikTok. | all |
| `tiktok-seekbar` | Show progress bar for all video. | all |
| `tiktok-download` | Removes download restrictions and changes the default path to download to. | all | | `tiktok-download` | Removes download restrictions and changes the default path to download to. | all |
| `tiktok-settings` | Add settings menu to TikTok. | all | | `tiktok-seekbar` | Show progress bar for all video. | all |
| `tiktok-force-login` | Do not force login. | all | | `tiktok-force-login` | Do not force login. | all |
| `tiktok-settings` | Add settings menu to TikTok. | all |
| `tiktok-ads` | Removes ads from TikTok. | all |
| `tiktok-feed-filter` | Filters tiktok videos: removing ads, removing livestreams. | all |
</details> </details>
### 📦 `com.twitter.android` ### 📦 `com.twitter.android`
@@ -125,15 +72,76 @@ The official Patch bundle provided by ReVanced and the community.
| 💊 Patch | 📜 Description | 🏹 Target Version | | 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:| |:--------:|:--------------:|:-----------------:|
| `monochrome-icon` | Adds a monochrome icon. | all |
| `dynamic-color` | Replaces the default Twitter Blue with the users Material You palette. | all |
| `timeline-ads` | Removes ads from the Twitter timeline. | all | | `timeline-ads` | Removes ads from the Twitter timeline. | all |
</details> </details>
### 📦 `com.spotify.music` ### 📦 `com.google.android.apps.youtube.music`
<details> <details>
| 💊 Patch | 📜 Description | 🏹 Target Version | | 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:| |:--------:|:--------------:|:-----------------:|
| `hide-premium-navbar` | Removes the premium tab from the navbar. | all | | `exclusive-audio-playback` | Enables the option to play music without video. | 5.26.52 |
| `codecs-unlock` | Adds more audio codec options. The new audio codecs usually result in better audio quality. | 5.26.52 |
| `music-microg-support` | Allows YouTube Music ReVanced to run without root and under a different package name. | 5.26.52 |
| `music-video-ads` | Removes ads in the music player. | 5.26.52 |
| `tasteBuilder-remover` | Removes the "Tell us which artists you like" card from the home screen. | 5.26.52 |
| `minimized-playback-music` | Enables minimized playback on Kids music. | 5.26.52 |
| `compact-header` | Hides the music category bar at the top of the homepage. | 5.26.52 |
| `upgrade-button-remover` | Removes the upgrade tab from the pivot bar. | 5.26.52 |
| `hide-get-premium` | Removes all "Get Premium" evidences from the avatar menu. | 5.26.52 |
| `background-play` | Enables playing music in the background. | 5.26.52 |
</details>
### 📦 `com.google.android.youtube`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `swipe-controls` | Adds volume and brightness swipe controls. | 17.36.37 |
| `downloads` | Enables downloading music and videos from YouTube. | 17.36.37 |
| `seekbar-tapping` | Enables tap-to-seek on the seekbar of the video player. | 17.36.37 |
| `remember-video-quality` | Adds the ability to remember the video quality you chose in the video quality flyout. | 17.36.37 |
| `enable-debugging` | Enables app debugging by patching the manifest file. | all |
| `custom-playback-speed` | Adds more video playback speed options. | 17.36.37 |
| `minimized-playback` | Enables minimized and background playback. | 17.36.37 |
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
| `custom-video-buffer` | Lets you change the buffers of videos. | 17.36.37 |
| `settings` | Adds settings for ReVanced to YouTube. | all |
| `microg-support` | Allows YouTube ReVanced to run without root and under a different package name with Vanced MicroG. | 17.36.37 |
| `hdr-auto-brightness` | Makes the brightness of HDR videos follow the system default. | 17.36.37 |
| `always-autorepeat` | Always repeats the playing video again. | 17.36.37 |
| `general-ads` | Removes general ads. | 17.36.37 |
| `hide-infocard-suggestions` | Hides infocards in videos. | 17.36.37 |
| `video-ads` | Removes ads in the video player. | 17.36.37 |
| `hide-time-and-seekbar` | Hides progress bar and time counter on videos. | 17.36.37 |
| `old-quality-layout` | Enables the original quality flyout menu. | 17.36.37 |
| `enable-wide-searchbar` | Replaces the search icon with a wide search bar. This will hide the YouTube logo when active. | 17.36.37 |
| `disable-fullscreen-panels` | Disables video description and comments panel in fullscreen view. | 17.36.37 |
| `hide-autoplay-button` | Hides the autoplay button in the video player. | 17.36.37 |
| `disable-startup-shorts-player` | Disables playing YouTube Shorts when launching YouTube. | 17.36.37 |
| `premium-heading` | Shows premium branding on the home screen. | all |
| `custom-branding` | Changes the YouTube launcher icon and name to your choice (defaults to ReVanced). | all |
| `hide-create-button` | Hides the create button in the navigation bar. | 17.36.37 |
| `hide-shorts-button` | Hides the shorts button on the navigation bar. | 17.36.37 |
| `theme` | Applies a custom theme. | all |
| `hide-email-address` | Hides the email address in the account switcher. | 17.36.37 |
| `sponsorblock` | Integrate SponsorBlock. | 17.36.37 |
| `hide-cast-button` | Hides the cast button in the video player. | all |
| `tablet-mini-player` | Enables the tablet mini player layout. | 17.36.37 |
| `return-youtube-dislike` | Shows the dislike count of videos using the Return YouTube Dislike API. | 17.36.37 |
| `hide-watermark` | Hides creator's watermarks on videos. | 17.36.37 |
| `disable-auto-player-popup-panels` | Disable automatic popup panels (playlist or live chat) on video player. | 17.36.37 |
| `disable-auto-captions` | Disable forced captions from being automatically enabled. | 17.36.37 |
</details>
### 📦 `com.vanced.android.youtube`
<details>
| 💊 Patch | 📜 Description | 🏹 Target Version |
|:--------:|:--------------:|:-----------------:|
| `client-spoof` | Spoofs the YouTube or Vanced client to prevent playback issues. | all |
</details> </details>

View File

@@ -20,7 +20,7 @@ repositories {
} }
dependencies { dependencies {
implementation("app.revanced:revanced-patcher:5.0.1") implementation("app.revanced:revanced-patcher:5.1.2")
implementation("app.revanced:multidexlib2:2.5.2.r2") implementation("app.revanced:multidexlib2:2.5.2.r2")
// Required for meta // Required for meta
implementation("com.google.code.gson:gson:2.9.1") implementation("com.google.code.gson:gson:2.9.1")

View File

@@ -1,2 +1,2 @@
kotlin.code.style = official kotlin.code.style = official
version = 2.68.2 version = 2.76.2

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -10,7 +10,7 @@ import app.revanced.patcher.annotation.Package
*/ */
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -1,37 +1,27 @@
package app.revanced.patches.music.misc.microg.patch.bytecode package app.revanced.patches.music.misc.microg.patch.bytecode
import app.revanced.extensions.equalsAny
import app.revanced.patcher.annotation.Description import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.BytecodeData import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import app.revanced.patches.music.misc.microg.patch.resource.MusicMicroGResourcePatch
import app.revanced.patches.youtube.misc.microg.patch.resource.enum.StringReplaceMode
import app.revanced.patches.music.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.fingerprints.* import app.revanced.patches.music.misc.microg.fingerprints.*
import org.jf.dexlib2.Opcode import app.revanced.patches.music.misc.microg.patch.resource.MusicMicroGResourcePatch
import org.jf.dexlib2.builder.MutableMethodImplementation import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
import org.jf.dexlib2.iface.instruction.formats.Instruction21c import app.revanced.patches.youtube.misc.microg.shared.Constants
import org.jf.dexlib2.iface.reference.StringReference import app.revanced.util.microg.MicroGBytecodeHelper
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
@Patch @Patch
@DependsOn([MusicMicroGResourcePatch::class]) @DependsOn([MusicMicroGResourcePatch::class])
@Name("music-microg-support") @Name("music-microg-support")
@Description("Allows YouTube Music ReVanced to run without root and under a different package name.") @Description("Allows YouTube Music ReVanced to run without root and under a different package name.")
@MusicMicroGPatchCompatibility @MusicMicroGPatchCompatibility
@Version("0.0.1") @Version("0.0.2")
class MusicMicroGBytecodePatch : BytecodePatch( class MusicMicroGBytecodePatch : BytecodePatch(
listOf( listOf(
ServiceCheckFingerprint, ServiceCheckFingerprint,
@@ -42,130 +32,34 @@ class MusicMicroGBytecodePatch : BytecodePatch(
PrimeFingerprint, PrimeFingerprint,
) )
) { ) {
override fun execute(data: BytecodeData): PatchResult { // NOTE: the previous patch also replaced the following strings, but it seems like they are not needed:
disablePlayServiceChecks() // - "com.google.android.gms.chimera.GmsIntentOperationService",
data.classes.forEach { classDef -> // - "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
var proxiedClass: MutableClass? = null // - "com.google.android.gms.phenotype.internal.IPhenotypeService",
// - "com.google.android.gms.phenotype.PACKAGE_NAME",
classDef.methods.forEach methodLoop@{ method -> // - "com.google.android.gms.phenotype.UPDATE",
val implementation = method.implementation ?: return@methodLoop // - "com.google.android.gms.phenotype",
override fun execute(data: BytecodeData) =
var proxiedImplementation: MutableMethodImplementation? = null // apply common microG patch
MicroGBytecodeHelper.patchBytecode(
implementation.instructions.forEachIndexed { i, instruction -> data,
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed arrayOf(
MicroGBytecodeHelper.packageNameTransform(
val stringValue = ((instruction as Instruction21c).reference as StringReference).string Constants.PACKAGE_NAME,
Constants.REVANCED_PACKAGE_NAME
val replaceMode = if (stringValue.equalsAny( )
"com.google.android.gms", ),
"com.google.android.gms.chimera", MicroGBytecodeHelper.PrimeMethodTransformationData(
"com.google.android.c2dm.intent.REGISTER", PrimeFingerprint,
"com.google.android.c2dm.permission.SEND", MUSIC_PACKAGE_NAME,
"com.google.iid.TOKEN_REQUEST", REVANCED_MUSIC_PACKAGE_NAME
"com.google", ),
"com.google.android.gms.chimera.GmsIntentOperationService", listOf(
"com.google.android.gms.phenotype.internal.IPhenotypeCallbacks", ServiceCheckFingerprint,
"com.google.android.gms.phenotype.internal.IPhenotypeService", GooglePlayUtilityFingerprint,
"com.google.android.gms.phenotype.service.START", CastDynamiteModuleFingerprint,
"com.google.android.gms.phenotype.PACKAGE_NAME", CastDynamiteModuleV2Fingerprint,
"com.google.android.gms.phenotype.UPDATE", CastContextFetchFingerprint
"com.google.android.gms.phenotype",
"com.google.android.gms.auth.accounts",
"com.google.android.c2dm.intent.REGISTRATION",
"com.google.android.gsf.action.GET_GLS",
"com.google.android.gsf.login",
"content://com.google.settings/partner",
"content://com.google.android.gms.phenotype/",
"content://com.google.android.gsf.gservices",
"content://com.google.android.gsf.gservices/prefix",
"com.google.android.c2dm.intent.RECEIVE"
)
) {
StringReplaceMode.REPLACE_WITH_MICROG
} else if (stringValue.equalsAny(
"com.google.android.apps.youtube.music.SuggestionsProvider", "com.google.android.apps.youtube.music.fileprovider"
)
) {
StringReplaceMode.REPLACE_WITH_REVANCED
} else {
StringReplaceMode.DO_NOT_REPLACE
}
if (replaceMode != StringReplaceMode.DO_NOT_REPLACE) {
if (proxiedClass == null) {
proxiedClass = data.proxy(classDef).resolve()
}
if (proxiedImplementation == null) {
proxiedImplementation = proxiedClass!!.methods.first {
it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes)
}.implementation!!
}
val newString = if (replaceMode == StringReplaceMode.REPLACE_WITH_REVANCED) stringValue.replace(
"com.google.android.apps.youtube.music", REVANCED_MUSIC_PACKAGE_NAME
)
else stringValue.replace("com.google", BASE_MICROG_PACKAGE_NAME)
proxiedImplementation!!.replaceInstruction(
i, BuilderInstruction21c(
Opcode.CONST_STRING, instruction.registerA, ImmutableStringReference(newString)
)
)
}
}
}
}
return PatchResultSuccess()
}
private fun disablePlayServiceChecks() {
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
).forEach { fingerprint ->
val result = fingerprint.result!!
val stringInstructions = when (result.method.returnType.first()) {
'L' -> """
const/4 v0, 0x0
return-object v0
"""
'V' -> "return-void"
'I' -> """
const/4 v0, 0x0
return v0
"""
else -> throw Exception("This case should never happen.")
}
result.mutableMethod.addInstructions(
0, stringInstructions
) )
} ).let { PatchResultSuccess() }
val primeMethod = PrimeFingerprint.result!!.mutableMethod
val implementation = primeMethod.implementation!!
var register = 2
val index = implementation.instructions.indexOfFirst {
if (it.opcode != Opcode.CONST_STRING) return@indexOfFirst false
val instructionString = ((it as Instruction21c).reference as StringReference).string
if (instructionString != "com.google.android.apps.youtube.music") return@indexOfFirst false
register = it.registerA
return@indexOfFirst true
}
primeMethod.replaceInstruction(
index, "const-string v$register, \"$REVANCED_MUSIC_PACKAGE_NAME\""
)
}
} }

View File

@@ -1,44 +1,41 @@
package app.revanced.patches.music.misc.microg.patch.resource package app.revanced.patches.music.misc.microg.patch.resource
import app.revanced.patcher.annotation.Description import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.impl.ResourcePatch import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import app.revanced.patches.music.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_APP_NAME import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_APP_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
@Name("music-microg-resource-patch") import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
@Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.") import app.revanced.util.microg.MicroGManifestHelper
@MusicMicroGPatchCompatibility import app.revanced.util.microg.MicroGResourceHelper
@Version("0.0.1")
class MusicMicroGResourcePatch : ResourcePatch() { @Name("music-microg-resource-patch")
override fun execute(data: ResourceData): PatchResult { @Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.")
@MusicMicroGPatchCompatibility
val manifest = data["AndroidManifest.xml"].readText() @Version("0.0.2")
class MusicMicroGResourcePatch : ResourcePatch() {
data["AndroidManifest.xml"].writeText( override fun execute(data: ResourceData): PatchResult {
manifest.replace( // update manifest
"package=\"com.google.android.apps.youtube.music", "package=\"$REVANCED_MUSIC_PACKAGE_NAME" MicroGResourceHelper.patchManifest(
).replace( data,
"android:label=\"@string/app_name", "android:label=\"$REVANCED_MUSIC_APP_NAME" MUSIC_PACKAGE_NAME,
).replace( REVANCED_MUSIC_PACKAGE_NAME,
"android:label=\"@string/app_launcher_name", "android:label=\"$REVANCED_MUSIC_APP_NAME" REVANCED_MUSIC_APP_NAME
).replace( )
"android:authorities=\"com.google.android.apps.youtube.music", "android:authorities=\"$REVANCED_MUSIC_PACKAGE_NAME"
).replace( // add metadata to the manifest
"com.google.android.apps.youtube.music.permission.C2D_MESSAGE", "$REVANCED_MUSIC_PACKAGE_NAME.permission.C2D_MESSAGE" MicroGManifestHelper.addSpoofingMetadata(
).replace( data,
"com.google.android.c2dm", "$BASE_MICROG_PACKAGE_NAME.android.c2dm" SPOOFED_PACKAGE_NAME,
).replace( SPOOFED_PACKAGE_SIGNATURE
"</queries>", "<package android:name=\"$BASE_MICROG_PACKAGE_NAME.android.gms\"/></queries>" )
) return PatchResultSuccess()
) }
return PatchResultSuccess()
}
} }

View File

@@ -1,7 +1,9 @@
package app.revanced.patches.music.misc.microg.shared package app.revanced.patches.music.misc.microg.shared
object Constants { object Constants {
internal const val BASE_MICROG_PACKAGE_NAME = "com.mgoogle"
internal const val REVANCED_MUSIC_APP_NAME = "YouTube Music ReVanced" internal const val REVANCED_MUSIC_APP_NAME = "YouTube Music ReVanced"
internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music" 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"
internal const val SPOOFED_PACKAGE_NAME = MUSIC_PACKAGE_NAME
internal const val SPOOFED_PACKAGE_SIGNATURE = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"
} }

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50") "com.google.android.apps.youtube.music", arrayOf("5.14.53", "5.16.51", "5.17.51", "5.21.52", "5.22.54", "5.23.50", "5.25.51", "5.25.52", "5.26.52")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.spotify.audio.annotation
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package("com.spotify.music")]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class DisableCaptureRestrictionCompatibility

View File

@@ -0,0 +1,53 @@
package app.revanced.patches.spotify.audio.bytecode.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility
import app.revanced.patches.spotify.audio.fingerprints.DisableCaptureRestrictionAudioDriverFingerprint
import app.revanced.patches.spotify.audio.resource.patch.DisableCaptureRestrictionResourcePatch
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@Patch
@Name("disable-capture-restriction")
@DependsOn([DisableCaptureRestrictionResourcePatch::class])
@Description("Allows capturing Spotify's audio output while screen sharing or screen recording.")
@DisableCaptureRestrictionCompatibility
@Version("0.0.1")
class DisableCaptureRestrictionBytecodePatch : BytecodePatch(
listOf(
DisableCaptureRestrictionAudioDriverFingerprint
)
) {
private fun MutableMethod.replaceConstant4Instruction(index: Int, instruction: Instruction, with: Int) {
val register = (instruction as OneRegisterInstruction).registerA
this.replaceInstruction(
index, "const/4 v$register, $with"
)
}
override fun execute(data: BytecodeData): PatchResult {
val method = DisableCaptureRestrictionAudioDriverFingerprint.result!!.mutableMethod
// Replace constant that contains the capture policy parameter for AudioAttributesBuilder.setAllowedCapturePolicy()
val instruction = method.instruction(CONST_INSTRUCTION_POSITION)
method.replaceConstant4Instruction(CONST_INSTRUCTION_POSITION, instruction, ALLOW_CAPTURE_BY_ALL)
return PatchResultSuccess()
}
private companion object {
const val CONST_INSTRUCTION_POSITION = 2
const val ALLOW_CAPTURE_BY_ALL = 0x01
}
}

View File

@@ -0,0 +1,21 @@
package app.revanced.patches.spotify.audio.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.annotation.DirectPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility
@Name("disable-capture-restriction-audio-driver-fingerprint")
@MatchingMethod(
"Lcom/spotify/playback/playbacknative/AudioDriver;", "constructAudioAttributes"
)
@DirectPatternScanMethod
@DisableCaptureRestrictionCompatibility
@Version("0.0.1")
object DisableCaptureRestrictionAudioDriverFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass == "Lcom/spotify/playback/playbacknative/AudioDriver;" && methodDef.name == "constructAudioAttributes"
}
)

View File

@@ -0,0 +1,35 @@
package app.revanced.patches.spotify.audio.resource.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.spotify.audio.annotation.DisableCaptureRestrictionCompatibility
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import org.w3c.dom.Element
@Name("disable-capture-restriction-resource-patch")
@Description("Sets allowAudioPlaybackCapture in manifest to true.")
@DisableCaptureRestrictionCompatibility
@Version("0.0.1")
class DisableCaptureRestrictionResourcePatch : ResourcePatch() {
override fun execute(data: ResourceData): PatchResult {
// create an xml editor instance
data.xmlEditor["AndroidManifest.xml"].use { dom ->
// get the application node
val applicationNode = dom
.file
.getElementsByTagName("application")
.item(0) as Element
// set allowAudioPlaybackCapture attribute to true
applicationNode.setAttribute("android:allowAudioPlaybackCapture", "true")
}
return PatchResultSuccess()
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.spotify.layout.theme.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.spotify.music")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class ThemeCompatibility

View File

@@ -0,0 +1,70 @@
package app.revanced.patches.spotify.layout.theme.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.OptionsContainer
import app.revanced.patcher.patch.PatchOption
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.spotify.layout.theme.annotations.ThemeCompatibility
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import org.w3c.dom.Element
@Patch
@DependsOn([FixLocaleConfigErrorPatch::class])
@Name("spotify-theme")
@Description("Applies a custom theme.")
@ThemeCompatibility
@Version("0.0.1")
class ThemePatch : ResourcePatch() {
override fun execute(data: ResourceData): PatchResult {
data.xmlEditor["res/values/colors.xml"].use { editor ->
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element
for (i in 0 until resourcesNode.childNodes.length) {
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
node.textContent = when (node.getAttribute("name")) {
"gray_7" -> backgroundColor!!
"dark_brightaccent_background_base", "dark_base_text_brightaccent", "green_light" -> accentColor!!
"dark_brightaccent_background_press" -> accentPressedColor!!
else -> continue
}
}
}
return PatchResultSuccess()
}
companion object : OptionsContainer() {
var backgroundColor: String? by option(
PatchOption.StringOption(
key = "backgroundColor",
default = "@android:color/black",
title = "Background color",
description = "The background color. Can be a hex color or a resource reference.",
)
)
var accentColor: String? by option(
PatchOption.StringOption(
key = "accentColor",
default = "#ff1ed760",
title = "Accent color",
description = "The accent color ('spotify green' by default). Can be a hex color or a resource reference.",
)
)
var accentPressedColor: String? by option(
PatchOption.StringOption(
key = "accentPressedColor",
default = "#ff169c46",
title = "Pressed accent for the dark theme",
description = "The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference.",
)
)
}
}

View File

@@ -14,7 +14,7 @@ import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patches.spotify.premium_navbar_tab.annotations.PremiumNavbarTabCompatibility import app.revanced.patches.spotify.premium_navbar_tab.annotations.PremiumNavbarTabCompatibility
import app.revanced.patches.spotify.premium_navbar_tab.fingerprints.AddPremiumNavbarTabFingerprint import app.revanced.patches.spotify.premium_navbar_tab.fingerprints.AddPremiumNavbarTabFingerprint
import app.revanced.patches.spotify.premium_navbar_tab.fingerprints.AddPremiumNavbarTabParentFingerprint import app.revanced.patches.spotify.premium_navbar_tab.fingerprints.AddPremiumNavbarTabParentFingerprint
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import org.jf.dexlib2.Opcode import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
@@ -23,7 +23,7 @@ import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
@Description("Removes the premium tab from the navbar.") @Description("Removes the premium tab from the navbar.")
@PremiumNavbarTabCompatibility @PremiumNavbarTabCompatibility
@Version("0.0.1") @Version("0.0.1")
@DependsOn([ResourceIdMappingProviderResourcePatch::class]) @DependsOn([ResourceMappingResourcePatch::class])
class PremiumNavbarTabPatch : BytecodePatch( class PremiumNavbarTabPatch : BytecodePatch(
listOf( listOf(
AddPremiumNavbarTabParentFingerprint AddPremiumNavbarTabParentFingerprint
@@ -39,7 +39,7 @@ class PremiumNavbarTabPatch : BytecodePatch(
val methodInstructions = method.implementation!!.instructions val methodInstructions = method.implementation!!.instructions
val lastInstructionIdx = methodInstructions.size - 1 val lastInstructionIdx = methodInstructions.size - 1
val premiumTabId = ResourceIdMappingProviderResourcePatch.resourceMappings.single{it.type == "id" && it.name == "premium_tab"}.id val premiumTabId = ResourceMappingResourcePatch.resourceMappings.single{it.type == "id" && it.name == "premium_tab"}.id
var removeAmount = 2 var removeAmount = 2
// 2nd const remove method // 2nd const remove method

View File

@@ -0,0 +1,14 @@
package app.revanced.patches.tiktok.interaction.speed.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[
Package("com.ss.android.ugc.trill"),
Package("com.zhiliaoapp.musically")
]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class SpeedCompatibility

View File

@@ -0,0 +1,24 @@
package app.revanced.patches.tiktok.interaction.speed.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.tiktok.interaction.speed.annotations.SpeedCompatibility
import org.jf.dexlib2.AccessFlags
@Name("speed-control-parent-fingerprint")
@MatchingMethod("LX/4cP;", "LJIILL")
@SpeedCompatibility
@Version("0.0.1")
object SpeedControlParentFingerprint : MethodFingerprint(
returnType = "L",
access = AccessFlags.PRIVATE or AccessFlags.FINAL,
parameters = listOf(
"L"
),
strings = listOf(
"playback_speed"
)
)

View File

@@ -0,0 +1,48 @@
package app.revanced.patches.tiktok.interaction.speed.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.data.impl.toMethodWalker
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.tiktok.interaction.speed.annotations.SpeedCompatibility
import app.revanced.patches.tiktok.interaction.speed.fingerprints.SpeedControlParentFingerprint
import org.jf.dexlib2.Opcode
@Patch
@Name("tiktok-speed")
@Description("Enables the playback speed option for all videos.")
@SpeedCompatibility
@Version("0.0.1")
class SpeedPatch : BytecodePatch(
listOf(
SpeedControlParentFingerprint
)
) {
override fun execute(data: BytecodeData): PatchResult {
val parentMethod = SpeedControlParentFingerprint.result!!.mutableMethod
val parentMethodInstructions = parentMethod.implementation!!.instructions
for ((index, instruction) in parentMethodInstructions.withIndex()) {
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) continue
val isSpeedEnableMethod = data
.toMethodWalker(parentMethod)
.nextMethod(index, true)
.getMethod() as MutableMethod
isSpeedEnableMethod.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
break
}
return PatchResultSuccess()
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.twitter.misc.dynamiccolor.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.twitter.android")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class DynamicColorCompatibility

View File

@@ -0,0 +1,87 @@
package app.revanced.patches.twitter.misc.dynamiccolor.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.twitter.misc.dynamiccolor.annotations.DynamicColorCompatibility
import java.io.FileWriter
import java.nio.file.Files
@Patch
@Name("dynamic-color")
@Description("Replaces the default Twitter Blue with the users Material You palette.")
@DynamicColorCompatibility
@Version("0.0.1")
class DynamicColorPatch : ResourcePatch() {
override fun execute(data: ResourceData): PatchResult {
val resDirectory = data["res"]
if (!resDirectory.isDirectory) return PatchResultError("The res folder can not be found.")
val valuesV31Directory = resDirectory.resolve("values-v31")
if (!valuesV31Directory.isDirectory) Files.createDirectories(valuesV31Directory.toPath())
val valuesNightV31Directory = resDirectory.resolve("values-night-v31")
if (!valuesNightV31Directory.isDirectory) Files.createDirectories(valuesNightV31Directory.toPath())
listOf(valuesV31Directory, valuesNightV31Directory).forEach { it ->
val colorsXml = it.resolve("colors.xml")
if(!colorsXml.exists()) {
FileWriter(colorsXml).use {
it.write("<?xml version=\"1.0\" encoding=\"utf-8\"?><resources></resources>")
}
}
}
data.xmlEditor["res/values-v31/colors.xml"].use { editor ->
val document = editor.file
mapOf(
"ps__twitter_blue" to "@color/twitter_blue",
"ps__twitter_blue_pressed" to "@color/twitter_blue_fill_pressed",
"twitter_blue" to "@android:color/system_accent1_400",
"twitter_blue_fill_pressed" to "@android:color/system_accent1_300",
"twitter_blue_opacity_30" to "@android:color/system_accent1_100",
"twitter_blue_opacity_50" to "@android:color/system_accent1_200",
"twitter_blue_opacity_58" to "@android:color/system_accent1_300",
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200",
"ic_launcher_background" to "#1DA1F2"
).forEach { (k, v) ->
val colorElement = document.createElement("color")
colorElement.setAttribute("name", k)
colorElement.textContent = v
document.getElementsByTagName("resources").item(0).appendChild(colorElement)
}
}
data.xmlEditor["res/values-night-v31/colors.xml"].use { editor ->
val document = editor.file
mapOf(
"twitter_blue" to "@android:color/system_accent1_200",
"twitter_blue_fill_pressed" to "@android:color/system_accent1_300",
"twitter_blue_opacity_30" to "@android:color/system_accent1_50",
"twitter_blue_opacity_50" to "@android:color/system_accent1_100",
"twitter_blue_opacity_58" to "@android:color/system_accent1_200",
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200"
).forEach { (k, v) ->
val colorElement = document.createElement("color")
colorElement.setAttribute("name", k)
colorElement.textContent = v
document.getElementsByTagName("resources").item(0).appendChild(colorElement)
}
}
return PatchResultSuccess()
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.twitter.misc.monochrome.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.twitter.android")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class MonochromeIconCompatibility

View File

@@ -0,0 +1,55 @@
package app.revanced.patches.twitter.misc.monochrome.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.twitter.misc.monochrome.annotations.MonochromeIconCompatibility
import java.io.FileWriter
import java.nio.file.Files
@Patch
@Name("monochrome-icon")
@Description("Adds a monochrome icon.")
@MonochromeIconCompatibility
@Version("0.0.1")
class MonochromeIconPatch : ResourcePatch() {
override fun execute(data: ResourceData): PatchResult {
val resDirectory = data["res"]
if (!resDirectory.isDirectory) return PatchResultError("The res folder can not be found.")
val mipmapV33Directory = resDirectory.resolve("mipmap-anydpi-v33")
if (!mipmapV33Directory.isDirectory) Files.createDirectories(mipmapV33Directory.toPath())
FileWriter(mipmapV33Directory.resolve("ic_launcher_twitter.xml")).use {
it.write(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<adaptive-icon\n" +
" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" +
" <background android:drawable=\"@color/ic_launcher_background\" />\n" +
" <foreground android:drawable=\"@mipmap/ic_launcher_twitter_foreground\" />\n" +
" <monochrome android:drawable=\"@mipmap/ic_launcher_twitter_foreground\" />\n" +
"</adaptive-icon>"
)
}
FileWriter(mipmapV33Directory.resolve("ic_launcher_twitter_round.xml")).use {
it.write(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<adaptive-icon\n" +
" xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" +
" <background android:drawable=\"@color/ic_launcher_background\" />\n" +
" <foreground android:drawable=\"@mipmap/ic_launcher_twitter_foreground\" />\n" +
" <monochrome android:drawable=\"@mipmap/ic_launcher_twitter_foreground\" />\n" +
"</adaptive-icon>"
)
}
return PatchResultSuccess()
}
}

View File

@@ -22,7 +22,7 @@ import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensi
import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.toDescriptor import app.revanced.patches.youtube.ad.general.bytecode.extensions.MethodExtensions.toDescriptor
import app.revanced.patches.youtube.ad.general.bytecode.utils.MethodUtils.createMutableMethod import app.revanced.patches.youtube.ad.general.bytecode.utils.MethodUtils.createMutableMethod
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
@@ -41,7 +41,7 @@ import org.jf.dexlib2.iface.reference.StringReference
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
@Patch @Patch
@DependsOn([ResourceIdMappingProviderResourcePatch::class, IntegrationsPatch::class, SettingsPatch::class]) @DependsOn([ResourceMappingResourcePatch::class, IntegrationsPatch::class, SettingsPatch::class])
@Name("general-ads") @Name("general-ads")
@Description("Removes general ads.") @Description("Removes general ads.")
@GeneralAdsCompatibility @GeneralAdsCompatibility
@@ -61,7 +61,7 @@ class GeneralBytecodeAdsPatch : BytecodePatch() {
"promoted_video_item_land", "promoted_video_item_land",
"promoted_video_item_full_bleed", "promoted_video_item_full_bleed",
).map { name -> ).map { name ->
ResourceIdMappingProviderResourcePatch.resourceMappings.single { it.name == name }.id ResourceMappingResourcePatch.resourceMappings.single { it.name == name }.id
} }
private val stringReferences = arrayOf( private val stringReferences = arrayOf(

View File

@@ -15,7 +15,7 @@ import app.revanced.patches.youtube.layout.autoplaybutton.annotations.AutoplayBu
import app.revanced.patches.youtube.layout.autoplaybutton.fingerprints.AutoNavInformerFingerprint import app.revanced.patches.youtube.layout.autoplaybutton.fingerprints.AutoNavInformerFingerprint
import app.revanced.patches.youtube.layout.autoplaybutton.fingerprints.LayoutConstructorFingerprint import app.revanced.patches.youtube.layout.autoplaybutton.fingerprints.LayoutConstructorFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
@@ -25,7 +25,7 @@ import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
import org.jf.dexlib2.iface.reference.MethodReference import org.jf.dexlib2.iface.reference.MethodReference
@Patch @Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class, ResourceIdMappingProviderResourcePatch::class]) @DependsOn([IntegrationsPatch::class, SettingsPatch::class, ResourceMappingResourcePatch::class])
@Name("hide-autoplay-button") @Name("hide-autoplay-button")
@Description("Hides the autoplay button in the video player.") @Description("Hides the autoplay button in the video player.")
@AutoplayButtonCompatibility @AutoplayButtonCompatibility
@@ -53,7 +53,7 @@ class HideAutoplayButtonPatch : BytecodePatch(
val layoutGenMethodInstructions = layoutGenMethod.implementation!!.instructions val layoutGenMethodInstructions = layoutGenMethod.implementation!!.instructions
// resolve the offsets such as ... // resolve the offsets such as ...
val autoNavPreviewStubId = ResourceIdMappingProviderResourcePatch.resourceMappings.single { val autoNavPreviewStubId = ResourceMappingResourcePatch.resourceMappings.single {
it.name == "autonav_preview_stub" it.name == "autonav_preview_stub"
}.id }.id
// where to insert the branch instructions and ... // where to insert the branch instructions and ...

View File

@@ -10,7 +10,6 @@ import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.SeekbarTappingFingerprint
import app.revanced.patches.youtube.layout.hidetimeandseekbar.fingerprints.TimeCounterFingerprint import app.revanced.patches.youtube.layout.hidetimeandseekbar.fingerprints.TimeCounterFingerprint
import app.revanced.patches.youtube.layout.hidetimeandseekbar.annotations.HideTimeAndSeekbarCompatibility import app.revanced.patches.youtube.layout.hidetimeandseekbar.annotations.HideTimeAndSeekbarCompatibility
import app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints.CreateVideoPlayerSeekbarFingerprint import app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints.CreateVideoPlayerSeekbarFingerprint
@@ -27,7 +26,7 @@ import app.revanced.patches.youtube.misc.settings.framework.components.impl.Swit
@Version("0.0.1") @Version("0.0.1")
class HideTimeAndSeekbarPatch : BytecodePatch( class HideTimeAndSeekbarPatch : BytecodePatch(
listOf( listOf(
CreateVideoPlayerSeekbarFingerprint, SeekbarTappingFingerprint, TimeCounterFingerprint CreateVideoPlayerSeekbarFingerprint, TimeCounterFingerprint
) )
) { ) {
override fun execute(data: BytecodeData): PatchResult { override fun execute(data: BytecodeData): PatchResult {
@@ -55,20 +54,6 @@ class HideTimeAndSeekbarPatch : BytecodePatch(
""" """
) )
val seekbarTappingMethod = SeekbarTappingFingerprint.result!!.mutableMethod
seekbarTappingMethod.addInstructions(
0, """
invoke-static { }, Lapp/revanced/integrations/patches/HideTimeAndSeekbarPatch;->hideTimeAndSeekbar()Z
move-result v0
if-eqz v0, :hide_time_and_seekbar
const/4 v0, 0x0
return v0
:hide_time_and_seekbar
nop
"""
)
val timeCounterMethod = TimeCounterFingerprint.result!!.mutableMethod val timeCounterMethod = TimeCounterFingerprint.result!!.mutableMethod
timeCounterMethod.addInstructions( timeCounterMethod.addInstructions(

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.youtube.layout.personalinformation.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.youtube", arrayOf("17.25.34", "17.26.35", "17.27.39", "17.28.34", "17.29.34", "17.32.35", "17.33.42", "17.34.35", "17.34.36", "17.36.37")
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class HideEmailAddressCompatibility

View File

@@ -0,0 +1,46 @@
package app.revanced.patches.youtube.layout.personalinformation.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.layout.personalinformation.annotations.HideEmailAddressCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("account-switcher-accessibility-label-fingerprint")
@MatchingMethod("Lqvs;", "mH")
@FuzzyPatternScanMethod(3)
@HideEmailAddressCompatibility
@Version("0.0.1")
object AccountSwitcherAccessibilityLabelFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.BRIDGE or AccessFlags.SYNTHETIC,
listOf("L", "L"),
listOf(
Opcode.CHECK_CAST,
Opcode.IGET_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.IGET_OBJECT,
Opcode.INVOKE_DIRECT,
Opcode.CONST_4,
Opcode.INVOKE_INTERFACE,
Opcode.IGET,
Opcode.CONST_4,
Opcode.ADD_INT_2ADDR,
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.GOTO,
Opcode.MOVE_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
)
)

View File

@@ -0,0 +1,76 @@
package app.revanced.patches.youtube.layout.personalinformation.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patches.youtube.layout.personalinformation.annotations.HideEmailAddressCompatibility
import app.revanced.patches.youtube.layout.personalinformation.fingerprints.AccountSwitcherAccessibilityLabelFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction35c
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
import org.jf.dexlib2.iface.reference.MethodReference
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class, ResourceMappingResourcePatch::class])
@Name("hide-email-address")
@Description("Hides the email address in the account switcher.")
@HideEmailAddressCompatibility
@Version("0.0.1")
class HideEmailAddressPatch : BytecodePatch(
listOf(
AccountSwitcherAccessibilityLabelFingerprint
)
) {
override fun execute(data: BytecodeData): PatchResult {
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
SwitchPreference(
"revanced_hide_email_address",
StringResource("revanced_hide_email_address_title", "Hide the email address"),
false,
StringResource("revanced_hide_email_address_summary_on", "Email address is hidden"),
StringResource("revanced_hide_email_address_summary_off", "Email address is visible")
)
)
val accountSwitcherAccessibilityLabelId =
ResourceMappingResourcePatch.resourceMappings.single {
it.type == "string" && it.name == "account_switcher_accessibility_label"
}.id
val accountSwitcherAccessibilityLabelMethod = AccountSwitcherAccessibilityLabelFingerprint.result!!.mutableMethod
val accountSwitcherAccessibilityLabelInstruction = accountSwitcherAccessibilityLabelMethod.implementation!!.instructions
val setVisibilityConstIndex = accountSwitcherAccessibilityLabelInstruction.indexOfFirst {
(it as? WideLiteralInstruction)?.wideLiteral == accountSwitcherAccessibilityLabelId
} - 1
val setVisibilityConstRegister = (accountSwitcherAccessibilityLabelInstruction[setVisibilityConstIndex] as OneRegisterInstruction).registerA
val toggleRegister = (setVisibilityConstRegister + 1)
accountSwitcherAccessibilityLabelMethod.addInstructions(
setVisibilityConstIndex + 1, """
invoke-static {}, Lapp/revanced/integrations/patches/HideEmailAddressPatch;->hideEmailAddress()Z
move-result v$toggleRegister
if-eqz v$toggleRegister, :hide
const/16 v$setVisibilityConstRegister, 0x8
:hide
nop
"""
)
return PatchResultSuccess()
}
}

View File

@@ -18,13 +18,13 @@ import app.revanced.patches.youtube.layout.pivotbar.createbutton.annotations.Cre
import app.revanced.patches.youtube.layout.pivotbar.createbutton.fingerprints.PivotBarCreateButtonViewFingerprint import app.revanced.patches.youtube.layout.pivotbar.createbutton.fingerprints.PivotBarCreateButtonViewFingerprint
import app.revanced.patches.youtube.layout.pivotbar.fingerprints.PivotBarFingerprint import app.revanced.patches.youtube.layout.pivotbar.fingerprints.PivotBarFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
@Patch @Patch
@DependsOn([IntegrationsPatch::class, ResourceIdMappingProviderResourcePatch::class, SettingsPatch::class]) @DependsOn([IntegrationsPatch::class, ResourceMappingResourcePatch::class, SettingsPatch::class])
@Name("hide-create-button") @Name("hide-create-button")
@Description("Hides the create button in the navigation bar.") @Description("Hides the create button in the navigation bar.")
@CreateButtonCompatibility @CreateButtonCompatibility

View File

@@ -34,8 +34,8 @@ class PlayerPopupPanelsPatch : BytecodePatch(
"revanced_player_popup_panels_enabled", "revanced_player_popup_panels_enabled",
StringResource("revanced_player_popup_panels_title", "Disable player popup panels"), StringResource("revanced_player_popup_panels_title", "Disable player popup panels"),
false, false,
StringResource("revanced_player_popup_panels_summary_on", "Player popup panels are enabled"), StringResource("revanced_player_popup_panels_summary_on", "Player popup panels are disabled"),
StringResource("revanced_player_popup_panels_summary_off", "Player popup panels are disabled") StringResource("revanced_player_popup_panels_summary_off", "Player popup panels are enabled")
) )
) )

View File

@@ -22,7 +22,7 @@ import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlock
import app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints.* import app.revanced.patches.youtube.layout.sponsorblock.bytecode.fingerprints.*
import app.revanced.patches.youtube.layout.sponsorblock.resource.patch.SponsorBlockResourcePatch import app.revanced.patches.youtube.layout.sponsorblock.resource.patch.SponsorBlockResourcePatch
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.playercontrols.bytecode.patch.PlayerControlsBytecodePatch import app.revanced.patches.youtube.misc.playercontrols.bytecode.patch.PlayerControlsBytecodePatch
import app.revanced.patches.youtube.misc.videoid.patch.VideoIdPatch import app.revanced.patches.youtube.misc.videoid.patch.VideoIdPatch
import app.revanced.patches.youtube.layout.autocaptions.fingerprints.StartVideoInformerFingerprint import app.revanced.patches.youtube.layout.autocaptions.fingerprints.StartVideoInformerFingerprint
@@ -99,10 +99,14 @@ class SponsorBlockBytecodePatch : BytecodePatch(
/* /*
Get the instance of the seekbar rectangle Get the instance of the seekbar rectangle
*/ */
seekbarMethod.addInstruction( for ((index, instruction) in seekbarMethodInstructions.withIndex()) {
1, if (instruction.opcode != Opcode.MOVE_OBJECT_FROM16) continue
"invoke-static {v0}, Lapp/revanced/integrations/sponsorblock/PlayerController;->setSponsorBarRect(Ljava/lang/Object;)V" seekbarMethod.addInstruction(
) index + 1,
"invoke-static {v0}, Lapp/revanced/integrations/sponsorblock/PlayerController;->setSponsorBarRect(Ljava/lang/Object;)V"
)
break
}
for ((index, instruction) in seekbarMethodInstructions.withIndex()) { for ((index, instruction) in seekbarMethodInstructions.withIndex()) {
if (instruction.opcode != Opcode.INVOKE_STATIC) continue if (instruction.opcode != Opcode.INVOKE_STATIC) continue
@@ -178,9 +182,9 @@ class SponsorBlockBytecodePatch : BytecodePatch(
val controlsMethodResult = PlayerControlsBytecodePatch.showPlayerControlsFingerprintResult val controlsMethodResult = PlayerControlsBytecodePatch.showPlayerControlsFingerprintResult
val controlsLayoutStubResourceId = val controlsLayoutStubResourceId =
ResourceIdMappingProviderResourcePatch.resourceMappings.single { it.type == "id" && it.name == "controls_layout_stub" }.id ResourceMappingResourcePatch.resourceMappings.single { it.type == "id" && it.name == "controls_layout_stub" }.id
val zoomOverlayResourceId = val zoomOverlayResourceId =
ResourceIdMappingProviderResourcePatch.resourceMappings.single { it.type == "id" && it.name == "video_zoom_overlay_stub" }.id ResourceMappingResourcePatch.resourceMappings.single { it.type == "id" && it.name == "video_zoom_overlay_stub" }.id
methods@ for (method in controlsMethodResult.mutableClass.methods) { methods@ for (method in controlsMethodResult.mutableClass.methods) {
val instructions = method.implementation?.instructions!! val instructions = method.implementation?.instructions!!
@@ -331,7 +335,7 @@ class SponsorBlockBytecodePatch : BytecodePatch(
startVideoInformerMethod.addInstructions( startVideoInformerMethod.addInstructions(
0, """ 0, """
const/4 v0, 0x0 const/4 v0, 0x0
sput-boolean v0, Lapp/revanced/integrations/settings/SettingsEnum;->shorts_playing:Z sput-boolean v0, Lapp/revanced/integrations/sponsorblock/PlayerController;->shorts_playing:Z
""" """
) )
@@ -340,7 +344,7 @@ class SponsorBlockBytecodePatch : BytecodePatch(
shortsPlayerConstructorMethod.addInstructions( shortsPlayerConstructorMethod.addInstructions(
0, """ 0, """
const/4 v0, 0x1 const/4 v0, 0x1
sput-boolean v0, Lapp/revanced/integrations/settings/SettingsEnum;->shorts_playing:Z sput-boolean v0, Lapp/revanced/integrations/sponsorblock/PlayerController;->shorts_playing:Z
""" """
) )

View File

@@ -9,7 +9,7 @@ import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.impl.ResourcePatch import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility import app.revanced.patches.youtube.layout.sponsorblock.annotations.SponsorBlockCompatibility
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
@@ -20,7 +20,7 @@ import app.revanced.util.resources.ResourceUtils.copyXmlNode
@Name("sponsorblock-resource-patch") @Name("sponsorblock-resource-patch")
@SponsorBlockCompatibility @SponsorBlockCompatibility
@DependsOn([FixLocaleConfigErrorPatch::class, SettingsPatch::class, ResourceIdMappingProviderResourcePatch::class]) @DependsOn([FixLocaleConfigErrorPatch::class, SettingsPatch::class, ResourceMappingResourcePatch::class])
@Version("0.0.1") @Version("0.0.1")
class SponsorBlockResourcePatch : ResourcePatch() { class SponsorBlockResourcePatch : ResourcePatch() {
companion object { companion object {
@@ -107,7 +107,7 @@ class SponsorBlockResourcePatch : ResourcePatch() {
} }
}.close() // close afterwards }.close() // close afterwards
reelButtonGroupResourceId = ResourceIdMappingProviderResourcePatch.resourceMappings.single { reelButtonGroupResourceId = ResourceMappingResourcePatch.resourceMappings.single {
it.type == "id" && it.name == "reel_persistent_edu_button_group" it.type == "id" && it.name == "reel_persistent_edu_button_group"
}.id }.id

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.youtube.layout.startupshortsreset.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.youtube", arrayOf("17.33.42", "17.36.37")
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class StartupShortsResetCompatibility

View File

@@ -0,0 +1,19 @@
package app.revanced.patches.youtube.layout.startupshortsreset.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.layout.startupshortsreset.annotations.StartupShortsResetCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("action-open-shorts-fingerprint")
@MatchingMethod("Lkyt;", "l")
@StartupShortsResetCompatibility
@Version("0.0.1")
object ActionOpenShortsFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L", "L"),
strings = listOf("com.google.android.youtube.action.open.shorts"),
)

View File

@@ -0,0 +1,68 @@
package app.revanced.patches.youtube.layout.startupshortsreset.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patches.youtube.layout.startupshortsreset.annotations.StartupShortsResetCompatibility
import app.revanced.patches.youtube.layout.startupshortsreset.fingerprints.ActionOpenShortsFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
import app.revanced.patches.youtube.misc.settings.framework.components.impl.SwitchPreference
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
@Patch
@DependsOn([IntegrationsPatch::class, SettingsPatch::class])
@Name("disable-startup-shorts-player")
@Description("Disables playing YouTube Shorts when launching YouTube.")
@StartupShortsResetCompatibility
@Version("0.0.1")
class DisableShortsOnStartupPatch : BytecodePatch(
listOf(
ActionOpenShortsFingerprint
)
) {
override fun execute(data: BytecodeData): PatchResult {
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
SwitchPreference(
"revanced_startup_shorts_player_enabled",
StringResource("revanced_startup_shorts_player_title", "Disable shorts player at app startup"),
false,
StringResource("revanced_startup_shorts_player_summary_on", "Shorts player is disabled at app startup"),
StringResource("revanced_startup_shorts_player_summary_off", "Shorts player is enabled at app startup")
)
)
val actionOpenShortsResult = ActionOpenShortsFingerprint.result
val actionOpenShortsMethod = actionOpenShortsResult!!.mutableMethod
val actionOpenShortsInstructions = actionOpenShortsMethod.implementation!!.instructions
val moveResultIndex = actionOpenShortsResult.scanResult.stringsScanResult!!.matches.first().index + 4
val iPutBooleanIndex = moveResultIndex + 2
val moveResultRegister = (actionOpenShortsInstructions[moveResultIndex] as OneRegisterInstruction).registerA
val iPutBooleanRegister = (actionOpenShortsInstructions[iPutBooleanIndex] as TwoRegisterInstruction).registerA
actionOpenShortsMethod.addInstructions(
moveResultIndex + 1, """
invoke-static { }, Lapp/revanced/integrations/patches/DisableStartupShortsPlayerPatch;->disableStartupShortsPlayer()Z
move-result v$moveResultRegister
if-eqz v$moveResultRegister, :cond_startup_shorts_reset
const/4 v$iPutBooleanRegister, 0x0
:cond_startup_shorts_reset
nop
"""
)
return PatchResultSuccess()
}
}

View File

@@ -15,6 +15,7 @@ import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.SpeedA
import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.SpeedLimiterFingerprint import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.SpeedLimiterFingerprint
import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.VideoSpeedPatchFingerprint import app.revanced.patches.youtube.misc.customplaybackspeed.fingerprints.VideoSpeedPatchFingerprint
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import org.jf.dexlib2.builder.instruction.BuilderArrayPayload import org.jf.dexlib2.builder.instruction.BuilderArrayPayload
import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction import org.jf.dexlib2.iface.instruction.NarrowLiteralInstruction
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@@ -27,7 +28,7 @@ import kotlin.math.roundToInt
@Patch @Patch
@Name("custom-playback-speed") @Name("custom-playback-speed")
@Description("Adds more video playback speed options.") @Description("Adds more video playback speed options.")
@DependsOn([IntegrationsPatch::class]) @DependsOn([IntegrationsPatch::class, ResourceMappingResourcePatch::class])
@CustomPlaybackSpeedCompatibility @CustomPlaybackSpeedCompatibility
@Version("0.0.1") @Version("0.0.1")
class CustomPlaybackSpeedPatch : BytecodePatch( class CustomPlaybackSpeedPatch : BytecodePatch(

View File

@@ -5,7 +5,7 @@ import app.revanced.patcher.annotation.Package
@Compatibility( @Compatibility(
[Package( [Package(
"com.google.android.youtube", arrayOf("17.25.34", "17.26.35", "17.27.39", "17.28.34", "17.29.34", "17.32.35", "17.33.42", "17.34.35", "17.34.36", "17.36.37") "com.google.android.youtube", arrayOf("17.33.42", "17.34.35", "17.34.36", "17.36.37")
)] )]
) )
@Target(AnnotationTarget.CLASS) @Target(AnnotationTarget.CLASS)

View File

@@ -5,6 +5,7 @@ import app.revanced.patcher.annotation.Version
import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod import app.revanced.patcher.fingerprint.method.annotation.MatchingMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patches.youtube.misc.hdrbrightness.annotations.HDRBrightnessCompatibility import app.revanced.patches.youtube.misc.hdrbrightness.annotations.HDRBrightnessCompatibility
import org.jf.dexlib2.AccessFlags
@Name("hdr-brightness-fingerprint") @Name("hdr-brightness-fingerprint")
@MatchingMethod( @MatchingMethod(
@@ -13,6 +14,6 @@ import app.revanced.patches.youtube.misc.hdrbrightness.annotations.HDRBrightness
@HDRBrightnessCompatibility @HDRBrightnessCompatibility
@Version("0.0.1") @Version("0.0.1")
object HDRBrightnessFingerprint : MethodFingerprint( object HDRBrightnessFingerprint : MethodFingerprint(
"V", "V", AccessFlags.FINAL.value,
strings = listOf("c.SettingNotFound;", "screen_brightness", "android.mediaview", "lux"), strings = listOf("c.SettingNotFound;", "screen_brightness", "android.mediaview"),
) )

View File

@@ -1,40 +0,0 @@
package app.revanced.patches.youtube.misc.mapping.patch
import app.revanced.extensions.doRecursively
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.impl.ResourcePatch
import org.w3c.dom.Element
@Name("resource-id-mapping-provider-resource-patch-dependency")
@Description("Acts as a provider/dependency for resource mappings.")
@Version("0.0.1")
class ResourceIdMappingProviderResourcePatch : ResourcePatch() {
companion object {
internal lateinit var resourceMappings: List<ResourceElement>
private set
}
override fun execute(data: ResourceData): PatchResult {
data.xmlEditor["res/values/public.xml"].use { editor ->
resourceMappings = buildList {
editor.file.documentElement.doRecursively { node ->
if (node !is Element) return@doRecursively
val nameAttribute = node.getAttribute("name")
val typeAttribute = node.getAttribute("type")
if (node.nodeName != "public" || nameAttribute.startsWith("APKTOOL")) return@doRecursively
val id = node.getAttribute("id").substring(2).toLong(16)
add(ResourceElement(typeAttribute, nameAttribute, id))
}
}
}
return PatchResultSuccess()
}
}
data class ResourceElement(val type: String, val name: String, val id: Long)

View File

@@ -0,0 +1,73 @@
package app.revanced.patches.youtube.misc.mapping.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.impl.ResourcePatch
import org.w3c.dom.Element
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
@Name("resource-mapping")
@Description("Creates a map of public resources.")
@Version("0.0.1")
class ResourceMappingResourcePatch : ResourcePatch() {
companion object {
internal lateinit var resourceMappings: List<ResourceElement>
private set
private val THREAD_COUNT = Runtime.getRuntime().availableProcessors()
private val threadPoolExecutor = Executors.newFixedThreadPool(THREAD_COUNT)
}
override fun execute(data: ResourceData): PatchResult {
// save the file in memory to concurrently read from
val resourceXmlFile = data["res/values/public.xml"].readBytes()
// create a synchronized list to store the resource mappings
val mappings = Collections.synchronizedList(mutableListOf<ResourceElement>())
for (threadIndex in 0 until THREAD_COUNT) {
threadPoolExecutor.execute thread@{
data.xmlEditor[resourceXmlFile.inputStream()].use { editor ->
val resources = editor.file.documentElement.childNodes
val resourcesLength = resources.length
val jobSize = resourcesLength / THREAD_COUNT
val batchStart = jobSize * threadIndex
val batchEnd = jobSize * (threadIndex + 1)
element@ for (i in batchStart until batchEnd) {
// make sure to not to go out of bounds when rounding errors occur at calculating the jobSize
if (i >= resourcesLength) return@thread
val node = resources.item(i)
if (node !is Element) continue
val nameAttribute = node.getAttribute("name")
val typeAttribute = node.getAttribute("type")
if (node.nodeName != "public" || nameAttribute.startsWith("APKTOOL")) continue
val id = node.getAttribute("id").substring(2).toLong(16)
mappings.add(ResourceElement(typeAttribute, nameAttribute, id))
}
}
}
}
threadPoolExecutor
.also { it.shutdown() }
.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
resourceMappings = mappings
return PatchResultSuccess()
}
}
data class ResourceElement(val type: String, val name: String, val id: Long)

View File

@@ -1,32 +1,21 @@
package app.revanced.patches.youtube.misc.microg.patch.bytecode package app.revanced.patches.youtube.misc.microg.patch.bytecode
import app.revanced.extensions.equalsAny
import app.revanced.patcher.annotation.Description import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.impl.BytecodeData import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patches.youtube.layout.castbutton.patch.HideCastButtonPatch import app.revanced.patches.youtube.layout.castbutton.patch.HideCastButtonPatch
import app.revanced.patches.youtube.misc.clientspoof.patch.ClientSpoofPatch import app.revanced.patches.youtube.misc.clientspoof.patch.ClientSpoofPatch
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
import app.revanced.patches.youtube.misc.microg.fingerprints.* import app.revanced.patches.youtube.misc.microg.fingerprints.*
import app.revanced.patches.youtube.misc.microg.patch.resource.MicroGResourcePatch import app.revanced.patches.youtube.misc.microg.patch.resource.MicroGResourcePatch
import app.revanced.patches.youtube.misc.microg.patch.resource.enum.StringReplaceMode import app.revanced.patches.youtube.misc.microg.shared.Constants.PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME
import org.jf.dexlib2.Opcode import app.revanced.util.microg.MicroGBytecodeHelper
import org.jf.dexlib2.builder.MutableMethodImplementation
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
import org.jf.dexlib2.iface.reference.StringReference
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
@Patch @Patch
@DependsOn( @DependsOn(
@@ -51,121 +40,27 @@ class MicroGBytecodePatch : BytecodePatch(
PrimeFingerprint, PrimeFingerprint,
) )
) { ) {
override fun execute(data: BytecodeData): PatchResult { override fun execute(data: BytecodeData) =
disablePlayServiceChecksAndFixCastIssues() // apply common microG patch
data.classes.forEach { classDef -> MicroGBytecodeHelper.patchBytecode(
var proxiedClass: MutableClass? = null data, arrayOf(
MicroGBytecodeHelper.packageNameTransform(
classDef.methods.forEach methodLoop@{ method -> PACKAGE_NAME,
val implementation = method.implementation ?: return@methodLoop REVANCED_PACKAGE_NAME
)
var proxiedImplementation: MutableMethodImplementation? = null ),
MicroGBytecodeHelper.PrimeMethodTransformationData(
implementation.instructions.forEachIndexed { i, instruction -> PrimeFingerprint,
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed PACKAGE_NAME,
REVANCED_PACKAGE_NAME
val stringValue = ((instruction as Instruction21c).reference as StringReference).string ),
listOf(
val replaceMode = if (stringValue.equalsAny( IntegrityCheckFingerprint,
"com.google.android.gms", ServiceCheckFingerprint,
"com.google.android.c2dm.intent.REGISTER", GooglePlayUtilityFingerprint,
"com.google.android.c2dm.permission.SEND", CastDynamiteModuleFingerprint,
"com.google.iid.TOKEN_REQUEST", CastDynamiteModuleV2Fingerprint,
"com.google", CastContextFetchFingerprint
"com.google.android.gms.auth.accounts",
"com.google.android.c2dm.intent.REGISTRATION",
"com.google.android.gsf.action.GET_GLS",
"com.google.android.gsf.login",
"content://com.google.settings/partner",
"content://com.google.android.gsf.gservices",
"content://com.google.android.gsf.gservices/prefix",
"com.google.android.c2dm.intent.RECEIVE"
)
) {
StringReplaceMode.REPLACE_WITH_MICROG
} else if (stringValue.equalsAny(
"com.google.android.youtube.SuggestionsProvider", "com.google.android.youtube.fileprovider"
)
) {
StringReplaceMode.REPLACE_WITH_REVANCED
} else {
StringReplaceMode.DO_NOT_REPLACE
}
if (replaceMode != StringReplaceMode.DO_NOT_REPLACE) {
if (proxiedClass == null) {
proxiedClass = data.proxy(classDef).resolve()
}
if (proxiedImplementation == null) {
proxiedImplementation = proxiedClass!!.methods.first {
it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes)
}.implementation!!
}
val newString = if (replaceMode == StringReplaceMode.REPLACE_WITH_REVANCED) stringValue.replace(
"com.google.android.youtube", REVANCED_PACKAGE_NAME
)
else stringValue.replace("com.google", BASE_MICROG_PACKAGE_NAME)
proxiedImplementation!!.replaceInstruction(
i, BuilderInstruction21c(
Opcode.CONST_STRING, instruction.registerA, ImmutableStringReference(newString)
)
)
}
}
}
}
return PatchResultSuccess()
}
private fun disablePlayServiceChecksAndFixCastIssues() {
listOf(
IntegrityCheckFingerprint,
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint
).forEach { fingerprint ->
val result = fingerprint.result!!
val stringInstructions = when (result.method.returnType.first()) {
'L' -> """
const/4 v0, 0x0
return-object v0
"""
'V' -> "return-void"
'I' -> """
const/4 v0, 0x0
return v0
"""
else -> throw Exception("This case should never happen.")
}
result.mutableMethod.addInstructions(
0, stringInstructions
) )
} ).let { PatchResultSuccess() }
val primeMethod = PrimeFingerprint.result!!.mutableMethod
val implementation = primeMethod.implementation!!
var register = 2
val index = implementation.instructions.indexOfFirst {
if (it.opcode != Opcode.CONST_STRING) return@indexOfFirst false
val instructionString = ((it as Instruction21c).reference as StringReference).string
if (instructionString != "com.google.android.youtube") return@indexOfFirst false
register = it.registerA
return@indexOfFirst true
}
primeMethod.replaceInstruction(
index, "const-string v$register, \"$REVANCED_PACKAGE_NAME\""
)
}
} }

View File

@@ -8,14 +8,21 @@ import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.impl.ResourcePatch import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.music.misc.microg.shared.Constants
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility import app.revanced.patches.youtube.misc.microg.annotations.MicroGPatchCompatibility
import app.revanced.patches.youtube.misc.microg.shared.Constants.BASE_MICROG_PACKAGE_NAME import app.revanced.patches.youtube.misc.microg.shared.Constants.PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_APP_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch import app.revanced.patches.youtube.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference import app.revanced.patches.youtube.misc.settings.framework.components.impl.Preference
import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource import app.revanced.patches.youtube.misc.settings.framework.components.impl.StringResource
import app.revanced.patches.youtube.misc.settings.resource.patch.SettingsResourcePatch
import app.revanced.util.microg.Constants.MICROG_VENDOR
import app.revanced.util.microg.MicroGManifestHelper
import app.revanced.util.microg.MicroGResourceHelper
@Name("microg-resource-patch") @Name("microg-resource-patch")
@DependsOn([FixLocaleConfigErrorPatch::class, SettingsResourcePatch::class]) @DependsOn([FixLocaleConfigErrorPatch::class, SettingsResourcePatch::class])
@@ -27,32 +34,26 @@ class MicroGResourcePatch : ResourcePatch() {
SettingsPatch.addPreference( SettingsPatch.addPreference(
Preference( Preference(
StringResource("microg_settings", "MicroG Settings"), StringResource("microg_settings", "MicroG Settings"),
Preference.Intent("$BASE_MICROG_PACKAGE_NAME.android.gms", "", "org.microg.gms.ui.SettingsActivity"), Preference.Intent("$MICROG_VENDOR.android.gms", "", "org.microg.gms.ui.SettingsActivity"),
StringResource("microg_settings_summary", "Settings for MicroG"), StringResource("microg_settings_summary", "Settings for MicroG"),
) )
) )
SettingsPatch.renameIntentsTargetPackage(REVANCED_PACKAGE_NAME) SettingsPatch.renameIntentsTargetPackage(REVANCED_PACKAGE_NAME)
val manifest = data["AndroidManifest.xml"] // update manifest
manifest.writeText( MicroGResourceHelper.patchManifest(
manifest.readText() data,
.replace( PACKAGE_NAME,
"package=\"com.google.android.youtube", "package=\"$REVANCED_PACKAGE_NAME" REVANCED_PACKAGE_NAME,
).replace( REVANCED_APP_NAME
"android:authorities=\"com.google.android.youtube", "android:authorities=\"$REVANCED_PACKAGE_NAME"
).replace(
"com.google.android.youtube.permission.C2D_MESSAGE", "$REVANCED_PACKAGE_NAME.permission.C2D_MESSAGE"
).replace( // might not be needed
"com.google.android.youtube.lifecycle-trojan", "$REVANCED_PACKAGE_NAME.lifecycle-trojan"
).replace( // might not be needed
"com.google.android.youtube.photopicker_images", "$REVANCED_PACKAGE_NAME.photopicker_images"
).replace(
"com.google.android.c2dm", "$BASE_MICROG_PACKAGE_NAME.android.c2dm"
).replace(
"</queries>", "<package android:name=\"$BASE_MICROG_PACKAGE_NAME.android.gms\"/></queries>"
)
) )
// add metadata to manifest
MicroGManifestHelper.addSpoofingMetadata(
data,
SPOOFED_PACKAGE_NAME,
SPOOFED_PACKAGE_SIGNATURE
)
return PatchResultSuccess() return PatchResultSuccess()
} }
} }

View File

@@ -1,5 +0,0 @@
package app.revanced.patches.youtube.misc.microg.patch.resource.enum
enum class StringReplaceMode {
REPLACE_WITH_MICROG, REPLACE_WITH_REVANCED, DO_NOT_REPLACE
}

View File

@@ -1,6 +1,9 @@
package app.revanced.patches.youtube.misc.microg.shared package app.revanced.patches.youtube.misc.microg.shared
object Constants { object Constants {
internal const val BASE_MICROG_PACKAGE_NAME = "com.mgoogle" internal const val REVANCED_APP_NAME = "YouTube ReVanced"
internal const val REVANCED_PACKAGE_NAME = "app.revanced.android.youtube" internal const val REVANCED_PACKAGE_NAME = "app.revanced.android.youtube"
internal const val PACKAGE_NAME = "com.google.android.youtube"
internal const val SPOOFED_PACKAGE_NAME = PACKAGE_NAME
internal const val SPOOFED_PACKAGE_SIGNATURE = "24bb24c05e47e0aefa68a58a766179d9b613a600"
} }

View File

@@ -11,14 +11,14 @@ import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.playercontrols.annotation.PlayerControlsCompatibility import app.revanced.patches.youtube.misc.playercontrols.annotation.PlayerControlsCompatibility
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.BottomControlsInflateFingerprint import app.revanced.patches.youtube.misc.playercontrols.fingerprints.BottomControlsInflateFingerprint
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerControlsVisibilityFingerprint import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerControlsVisibilityFingerprint
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@Name("player-controls-bytecode-patch") @Name("player-controls-bytecode-patch")
@DependsOn([ResourceIdMappingProviderResourcePatch::class]) @DependsOn([ResourceMappingResourcePatch::class])
@Description("Manages the code for the player controls of the YouTube player.") @Description("Manages the code for the player controls of the YouTube player.")
@PlayerControlsCompatibility @PlayerControlsCompatibility
@Version("0.0.1") @Version("0.0.1")
@@ -28,7 +28,7 @@ class PlayerControlsBytecodePatch : BytecodePatch(
override fun execute(data: BytecodeData): PatchResult { override fun execute(data: BytecodeData): PatchResult {
showPlayerControlsFingerprintResult = PlayerControlsVisibilityFingerprint.result!! showPlayerControlsFingerprintResult = PlayerControlsVisibilityFingerprint.result!!
bottomUiContainerResourceId = ResourceIdMappingProviderResourcePatch bottomUiContainerResourceId = ResourceMappingResourcePatch
.resourceMappings .resourceMappings
.single { it.type == "id" && it.name == "bottom_ui_container_stub" }.id .single { it.type == "id" && it.name == "bottom_ui_container_stub" }.id

View File

@@ -13,7 +13,7 @@ import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.impl.BytecodePatch import app.revanced.patcher.patch.impl.BytecodePatch
import app.revanced.patcher.util.smali.toInstruction import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.LicenseActivityFingerprint import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.LicenseActivityFingerprint
import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ReVancedSettingsActivityFingerprint import app.revanced.patches.youtube.misc.settings.bytecode.fingerprints.ReVancedSettingsActivityFingerprint
@@ -94,7 +94,7 @@ class SettingsPatch : BytecodePatch(
internal companion object { internal companion object {
// TODO: hide this somehow // TODO: hide this somehow
var appearanceStringId: Long = ResourceIdMappingProviderResourcePatch.resourceMappings.find { var appearanceStringId: Long = ResourceMappingResourcePatch.resourceMappings.find {
it.type == "string" && it.name == "app_theme_appearance_dark" it.type == "string" && it.name == "app_theme_appearance_dark"
}!!.id }!!.id

View File

@@ -9,7 +9,7 @@ import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.impl.ResourcePatch import app.revanced.patcher.patch.impl.ResourcePatch
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import app.revanced.patches.youtube.misc.mapping.patch.ResourceIdMappingProviderResourcePatch import app.revanced.patches.youtube.misc.mapping.patch.ResourceMappingResourcePatch
import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility import app.revanced.patches.youtube.misc.settings.annotations.SettingsCompatibility
import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch
import app.revanced.patches.youtube.misc.settings.framework.components.BasePreference import app.revanced.patches.youtube.misc.settings.framework.components.BasePreference
@@ -22,7 +22,7 @@ import java.io.Closeable
@Name("settings-resource-patch") @Name("settings-resource-patch")
@SettingsCompatibility @SettingsCompatibility
@DependsOn([FixLocaleConfigErrorPatch::class, ResourceIdMappingProviderResourcePatch::class]) @DependsOn([FixLocaleConfigErrorPatch::class, ResourceMappingResourcePatch::class])
@Version("0.0.1") @Version("0.0.1")
class SettingsResourcePatch : ResourcePatch(), Closeable { class SettingsResourcePatch : ResourcePatch(), Closeable {

View File

@@ -0,0 +1,130 @@
package app.revanced.util.microg
/**
* constants for microG builds with signature spoofing
*/
internal object Constants {
/**
* microG vendor name
* aka. package prefix / package base
*/
const val MICROG_VENDOR = "com.mgoogle"
/**
* meta-data for microG package name spoofing on patched builds
*/
const val META_SPOOFED_PACKAGE_NAME = "$MICROG_VENDOR.android.gms.SPOOFED_PACKAGE_NAME"
/**
* meta-data for microG package signature spoofing on patched builds
*/
const val META_SPOOFED_PACKAGE_SIGNATURE = "$MICROG_VENDOR.android.gms.SPOOFED_PACKAGE_SIGNATURE"
/**
* meta-data for microG package detection
*/
const val META_GMS_PACKAGE_NAME = "app.revanced.MICROG_PACKAGE_NAME"
/**
* a list of all permissions in microG
*/
val PERMISSIONS = listOf(
// C2DM / GCM
"com.google.android.c2dm.permission.RECEIVE",
"com.google.android.c2dm.permission.SEND",
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
// GAuth
"com.google.android.googleapps.permission.GOOGLE_AUTH",
"com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
"com.google.android.googleapps.permission.GOOGLE_AUTH.local",
"com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
"com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
)
/**
* a list of all (intent) actions in microG
*/
val ACTIONS = listOf(
// location
"com.google.android.gms.location.places.ui.PICK_PLACE",
"com.google.android.gms.location.places.GeoDataApi",
"com.google.android.gms.location.places.PlacesApi",
"com.google.android.gms.location.places.PlaceDetectionApi",
"com.google.android.gms.wearable.MESSAGE_RECEIVED",
// C2DM / GCM
"com.google.android.c2dm.intent.REGISTER",
"com.google.android.c2dm.intent.REGISTRATION",
"com.google.android.c2dm.intent.UNREGISTER",
"com.google.android.c2dm.intent.RECEIVE",
"com.google.iid.TOKEN_REQUEST",
"com.google.android.gcm.intent.SEND",
// car
"com.google.android.gms.car.service.START",
// people
"com.google.android.gms.people.service.START",
// wearable
"com.google.android.gms.wearable.BIND",
// auth
"com.google.android.gsf.login",
"com.google.android.gsf.action.GET_GLS",
"com.google.android.gms.common.account.CHOOSE_ACCOUNT",
"com.google.android.gms.auth.login.LOGIN",
"com.google.android.gms.auth.api.credentials.PICKER",
"com.google.android.gms.auth.api.credentials.service.START",
"com.google.android.gms.auth.service.START",
"com.google.firebase.auth.api.gms.service.START",
"com.google.android.gms.auth.be.appcert.AppCertService",
// fido
"com.google.android.gms.fido.fido2.privileged.START",
// games
"com.google.android.gms.games.service.START",
"com.google.android.gms.games.PLAY_GAMES_UPGRADE",
// chimera
"com.google.android.gms.chimera",
// fonts
"com.google.android.gms.fonts",
// phenotype
"com.google.android.gms.phenotype.service.START",
// location
"com.google.android.gms.location.reporting.service.START",
// misc
"com.google.android.gms.gmscompliance.service.START",
"com.google.android.gms.oss.licenses.service.START",
"com.google.android.gms.safetynet.service.START",
"com.google.android.gms.tapandpay.service.BIND"
)
/**
* a list of all content provider authorities in microG
*/
val AUTHORITIES = listOf(
// gsf
"com.google.android.gsf.gservices",
"com.google.settings",
// auth
"com.google.android.gms.auth.accounts",
// chimera
"com.google.android.gms.chimera",
// fonts
"com.google.android.gms.fonts",
// phenotype
"com.google.android.gms.phenotype"
)
}

View File

@@ -0,0 +1,229 @@
package app.revanced.util.microg
import app.revanced.patcher.data.impl.BytecodeData
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.util.microg.Constants.ACTIONS
import app.revanced.util.microg.Constants.AUTHORITIES
import app.revanced.util.microg.Constants.MICROG_VENDOR
import app.revanced.util.microg.Constants.PERMISSIONS
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
import org.jf.dexlib2.iface.instruction.formats.Instruction21c
import org.jf.dexlib2.iface.reference.StringReference
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
/**
* Helper class for applying bytecode patches needed for the microg-support patches.
*/
internal object MicroGBytecodeHelper {
/**
* Transform strings with package name out of [fromPackageName] and [toPackageName].
*
* @param fromPackageName Original package name.
* @param toPackageName The package name to accept.
**/
fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? {
return { referencedString ->
when (referencedString) {
"$fromPackageName.SuggestionsProvider",
"$fromPackageName.fileprovider" -> referencedString.replace(fromPackageName, toPackageName)
else -> null
}
}
}
/**
* Prime method data class for the [MicroGBytecodeHelper] class.
*
* @param primeMethodFingerprint The prime methods [MethodFingerprint].
* @param fromPackageName Original package name.
* @param toPackageName The package name to accept.
**/
data class PrimeMethodTransformationData(
val primeMethodFingerprint: MethodFingerprint,
val fromPackageName: String,
val toPackageName: String
) {
/**
* Patch the prime method to accept the new package name.
*/
fun transformPrimeMethodPackageName() {
val primeMethod = primeMethodFingerprint.result!!.mutableMethod
val implementation = primeMethod.implementation!!
var register = 2
val index = implementation.instructions.indexOfFirst {
if (it.opcode != Opcode.CONST_STRING) return@indexOfFirst false
val instructionString = ((it as Instruction21c).reference as StringReference).string
if (instructionString != fromPackageName) return@indexOfFirst false
register = it.registerA
return@indexOfFirst true
}
primeMethod.replaceInstruction(
index, "const-string v$register, \"$toPackageName\""
)
}
}
/**
* Patch the bytecode to work with MicroG.
* Note: this only handles string constants to gms (intent actions, authorities, ...).
* If the app employs additional checks to validate the installed gms package, you'll have to handle those in the app- specific patch
*
* @param data Bytecode data instance.
* @param additionalStringTransforms Additional transformations applied to all const-string references.
* @param primeMethodTransformationData Data to patch the prime method.
* @param earlyReturns List of [MethodFingerprint] to return the resolved methods early.
*/
fun patchBytecode(
data: BytecodeData,
additionalStringTransforms: Array<(str: String) -> String?>,
primeMethodTransformationData: PrimeMethodTransformationData,
earlyReturns: List<MethodFingerprint>
) {
earlyReturns.returnEarly()
primeMethodTransformationData.transformPrimeMethodPackageName()
val allTransforms = arrayOf(
MicroGBytecodeHelper::commonTransform,
MicroGBytecodeHelper::contentUrisTransform,
*additionalStringTransforms
)
// transform all strings using all provided transforms, first match wins
data.transformStringReferences transform@{
for (transformFn in allTransforms) {
val s = transformFn(it)
if (s != null) return@transform s
}
return@transform null
}
}
/**
* const-string transform function for common gms string references.
*
* @param referencedString The string to transform.
*/
private fun commonTransform(referencedString: String): String? =
when (referencedString) {
"com.google",
"com.google.android.gms",
in PERMISSIONS,
in ACTIONS,
in AUTHORITIES -> referencedString.replace("com.google", MICROG_VENDOR)
// subscribedfeeds has no vendor prefix for whatever reason...
"subscribedfeeds" -> "${MICROG_VENDOR}.subscribedfeeds"
else -> null
}
/**
* const-string transform function for strings containing gms content uris / authorities.
*/
private fun contentUrisTransform(str: String): String? {
// only when content:// uri
if (str.startsWith("content://")) {
// check if matches any authority
for (authority in AUTHORITIES) {
val uriPrefix = "content://$authority"
if (str.startsWith(uriPrefix)) {
return str.replace(
uriPrefix,
"content://${authority.replace("com.google", MICROG_VENDOR)}"
)
}
}
// gms also has a 'subscribedfeeds' authority, check for that one too
val subFeedsUriPrefix = "content://subscribedfeeds"
if (str.startsWith(subFeedsUriPrefix)) {
return str.replace(subFeedsUriPrefix, "content://${MICROG_VENDOR}.subscribedfeeds")
}
}
return null
}
/**
* Transform all constant string references using a transformation function.
*
* @param transformFn string transformation function. if null, string is not changed.
*/
private fun BytecodeData.transformStringReferences(transformFn: (str: String) -> String?) {
classes.forEach { classDef ->
var mutableClass: MutableClass? = null
// enumerate all methods
classDef.methods.forEach classLoop@{ methodDef ->
var mutableMethod: MutableMethod? = null
val implementation = methodDef.implementation ?: return@classLoop
// enumerate all instructions and find const-string
implementation.instructions.forEachIndexed implLoop@{ index, instruction ->
// skip all that are not const-string
if (instruction.opcode != Opcode.CONST_STRING) return@implLoop
val str = ((instruction as Instruction21c).reference as StringReference).string
// call transform function
val transformedStr = transformFn(str)
if (transformedStr != null) {
// make class and method mutable, if not already
mutableClass = mutableClass ?: proxy(classDef).resolve()
mutableMethod = mutableMethod ?: mutableClass!!.methods.first {
it.name == methodDef.name && it.parameterTypes.containsAll(methodDef.parameterTypes)
}
// replace instruction with updated string
mutableMethod!!.implementation!!.replaceInstruction(
index,
BuilderInstruction21c(
Opcode.CONST_STRING,
instruction.registerA,
ImmutableStringReference(
transformedStr
)
)
)
}
}
}
}
}
/**
* Return the resolved methods of a list of [MethodFingerprint] early.
*/
private fun List<MethodFingerprint>.returnEarly() {
this.forEach { fingerprint ->
val result = fingerprint.result!!
val stringInstructions = when (result.method.returnType.first()) {
'L' -> """
const/4 v0, 0x0
return-object v0
"""
'V' -> "return-void"
'I' -> """
const/4 v0, 0x0
return v0
"""
else -> throw Exception("This case should never happen.")
}
result.mutableMethod.addInstructions(
0, stringInstructions
)
}
}
}

View File

@@ -0,0 +1,58 @@
package app.revanced.util.microg
import app.revanced.patcher.data.impl.ResourceData
import app.revanced.util.microg.Constants.META_GMS_PACKAGE_NAME
import app.revanced.util.microg.Constants.META_SPOOFED_PACKAGE_NAME
import app.revanced.util.microg.Constants.META_SPOOFED_PACKAGE_SIGNATURE
import app.revanced.util.microg.Constants.MICROG_VENDOR
import org.w3c.dom.Element
import org.w3c.dom.Node
/**
* helper class for adding manifest metadata needed for microG builds with signature spoofing
*/
internal object MicroGManifestHelper {
/**
* Add manifest entries needed for package and signature spoofing when using MicroG.
* Note: this only adds metadata entries for signature spoofing, other changes may still be required to make a microG patch work.
*
* @param data Resource data.
* @param spoofedPackage The package to spoof.
* @param spoofedSignature The signature to spoof.
*/
fun addSpoofingMetadata(
data: ResourceData,
spoofedPackage: String,
spoofedSignature: String
) {
data.xmlEditor["AndroidManifest.xml"].use {
val applicationNode = it
.file
.getElementsByTagName("application")
.item(0)
// package spoofing
applicationNode.adoptChild("meta-data") {
setAttribute("android:name", META_SPOOFED_PACKAGE_NAME)
setAttribute("android:value", spoofedPackage)
}
applicationNode.adoptChild("meta-data") {
setAttribute("android:name", META_SPOOFED_PACKAGE_SIGNATURE)
setAttribute("android:value", spoofedSignature)
}
// microG presence detection in integrations
applicationNode.adoptChild("meta-data") {
setAttribute("android:name", META_GMS_PACKAGE_NAME)
setAttribute("android:value", "${MICROG_VENDOR}.android.gms")
}
}
}
private fun Node.adoptChild(tagName: String, block: Element.() -> Unit) {
val child = ownerDocument.createElement(tagName)
child.block()
appendChild(child)
}
}

View File

@@ -0,0 +1,49 @@
package app.revanced.util.microg
import app.revanced.patcher.data.impl.ResourceData
/**
* Helper class for applying resource patches needed for the microg-support patches.
*/
internal object MicroGResourceHelper {
/**
* Patch the manifest to work with MicroG.
*
* @param data Bytecode data instance.
* @param fromPackageName Original package name.
* @param toPackageName The package name to accept.
* @param toName The new name of the app.
*/
fun patchManifest(
data: ResourceData,
fromPackageName: String,
toPackageName: String,
toName: String
) {
val manifest = data["AndroidManifest.xml"].readText()
data["AndroidManifest.xml"].writeText(
manifest.replace(
"package=\"$fromPackageName",
"package=\"$toPackageName"
).replace(
"android:label=\"@string/app_name",
"android:label=\"$toName"
).replace(
"android:label=\"@string/app_launcher_name",
"android:label=\"$toName"
).replace(
"android:authorities=\"$fromPackageName",
"android:authorities=\"$toPackageName"
).replace(
"$fromPackageName.permission.C2D_MESSAGE",
"$toPackageName.permission.C2D_MESSAGE"
).replace(
"com.google.android.c2dm",
"${Constants.MICROG_VENDOR}.android.c2dm"
).replace(
"</queries>",
"<package android:name=\"${Constants.MICROG_VENDOR}.android.gms\"/></queries>"
)
)
}
}

View File

@@ -14,10 +14,10 @@
<string name="general_adjusting_sum">This is the number of milliseconds you can move when you use the time adjustment buttons while adding new segment</string> <string name="general_adjusting_sum">This is the number of milliseconds you can move when you use the time adjustment buttons while adding new segment</string>
<string name="general_min_duration">Minimum segment duration</string> <string name="general_min_duration">Minimum segment duration</string>
<string name="general_min_duration_sum">Segments shorter than the set value (in seconds) will not be skipped or shown in the player</string> <string name="general_min_duration_sum">Segments shorter than the set value (in seconds) will not be skipped or shown in the player</string>
<string name="general_uuid">Your unique user id</string> <string name="general_uuid">Your private user id</string>
<string name="general_uuid_sum">This should be kept private. This is like a password and should not be shared with anyone. If someone has this, they can impersonate you</string> <string name="general_uuid_sum">This should be kept private. This is like a password and should not be shared with anyone. If someone has this, they can impersonate you</string>
<string name="settings_ie">Import/Export settings</string> <string name="settings_ie">Import/Export settings</string>
<string name="settings_ie_sum">This is your entire configuration that is applicable in the desktop extension in JSON. This includes your userID, so be sure to share this wisely.</string> <string name="settings_ie_sum">This is your entire configuration that is applicable in the desktop extension in JSON. This includes your Private userID, so be sure to share this wisely.</string>
<string name="general_api_url">Change API URL</string> <string name="general_api_url">Change API URL</string>
<string name="general_api_url_sum">The address SponsorBlock uses to make calls to the server. &lt;b>Don\'t change this unless you know what you\'re doing.&lt;/b></string> <string name="general_api_url_sum">The address SponsorBlock uses to make calls to the server. &lt;b>Don\'t change this unless you know what you\'re doing.&lt;/b></string>
<string name="settings_import_successful">Settings were successfully imported</string> <string name="settings_import_successful">Settings were successfully imported</string>