Compare commits

...

15 Commits

Author SHA1 Message Date
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
32 changed files with 911 additions and 506 deletions

View File

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

145
README.md
View File

@@ -4,52 +4,22 @@ 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 |
| `spotify-theme` | Applies a custom theme. | all |
</details> </details>
### 📦 `de.dwd.warnapp` ### 📦 `de.dwd.warnapp`
@@ -60,32 +30,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 +43,12 @@ 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-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 +56,12 @@ 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-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`
@@ -128,12 +72,69 @@ The official Patch bundle provided by ReVanced and the community.
| `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.23.50 |
| `codecs-unlock` | Adds more audio codec options. The new audio codecs usually result in better audio quality. | 5.23.50 |
| `music-microg-support` | Allows YouTube Music ReVanced to run without root and under a different package name. | 5.23.50 |
| `music-video-ads` | Removes ads in the music player. | 5.23.50 |
| `tasteBuilder-remover` | Removes the "Tell us which artists you like" card from the home screen. | 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 |
| `upgrade-button-remover` | Removes the upgrade tab from the pivot bar. | 5.23.50 |
| `hide-get-premium` | Removes all "Get Premium" evidences from the avatar menu. | 5.23.50 |
| `background-play` | Enables playing music in the background. | 5.23.50 |
</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 |
| `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 |
| `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.0")
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.69.4

File diff suppressed because one or more lines are too long

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

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

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

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

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

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

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