Compare commits

...

17 Commits

Author SHA1 Message Date
semantic-release-bot
19f74bcdb1 chore(release): 3.0.0-dev.8 [skip ci]
# [3.0.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-12-11)

### Features

* **YouTube - Alternative Thumbnails:** Add option to use DeArrow ([#3378](https://github.com/ReVanced/revanced-patches/issues/3378)) ([dbe123c](dbe123c93c))
2023-12-11 01:09:24 +00:00
Chris
dbe123c93c feat(YouTube - Alternative Thumbnails): Add option to use DeArrow (#3378)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2023-12-11 05:07:09 +04:00
semantic-release-bot
693d8a0f56 chore(release): 3.0.0-dev.7 [skip ci]
# [3.0.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-12-07)

### Features

* **Twitter - Dynamic Color:** Remove blue icon and update app name ([#3384](https://github.com/ReVanced/revanced-patches/issues/3384)) ([6a03c1f](6a03c1f8c3))
2023-12-07 13:13:03 +00:00
Advyte
6a03c1f8c3 feat(Twitter - Dynamic Color): Remove blue icon and update app name (#3384) 2023-12-07 14:10:55 +01:00
semantic-release-bot
e820724111 chore(release): 3.0.0-dev.6 [skip ci]
# [3.0.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-12-05)

### Features

* **Twitch - Settings:** Support version `16.1.0` and `15.4.1` ([#3377](https://github.com/ReVanced/revanced-patches/issues/3377)) ([ba58edb](ba58edbd7f))
2023-12-05 15:43:34 +00:00
LizenzFass78851
ba58edbd7f feat(Twitch - Settings): Support version 16.1.0 and 15.4.1 (#3377) 2023-12-05 16:41:22 +01:00
semantic-release-bot
a6d7c633f5 chore(release): 3.0.0-dev.5 [skip ci]
# [3.0.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-12-04)

### Bug Fixes

* Use correct class loader to load resources ([17f44ca](17f44ca780))

### Features

* **Photomath:** Constrain patches to last working version ([3bd6640](3bd66406cc))
2023-12-04 23:57:16 +00:00
oSumAtrIX
17f44ca780 fix: Use correct class loader to load resources 2023-12-05 00:55:09 +01:00
oSumAtrIX
3bd66406cc feat(Photomath): Constrain patches to last working version 2023-12-05 00:55:09 +01:00
semantic-release-bot
536b354fd6 chore(release): 3.0.0-dev.4 [skip ci]
# [3.0.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-12-04)

### Bug Fixes

* **YouTube - Minimized playback:** Fix PIP incorrectly shown for some Shorts playback ([#3364](https://github.com/ReVanced/revanced-patches/issues/3364)) ([e6a1573](e6a1573c59))
2023-12-04 23:49:54 +00:00
LisoUseInAIKyrios
e6a1573c59 fix(YouTube - Minimized playback): Fix PIP incorrectly shown for some Shorts playback (#3364) 2023-12-05 03:47:46 +04:00
semantic-release-bot
d63288cc3e chore(release): 3.0.0-dev.3 [skip ci]
# [3.0.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.2...v3.0.0-dev.3) (2023-12-04)

### Bug Fixes

* **YouTube - Return YouTube Dislike:** Prevent the first Short opened from freezing the UI ([#3359](https://github.com/ReVanced/revanced-patches/issues/3359)) ([aaad913](aaad91333f))
2023-12-04 08:49:47 +00:00
LisoUseInAIKyrios
aaad91333f fix(YouTube - Return YouTube Dislike): Prevent the first Short opened from freezing the UI (#3359) 2023-12-04 10:47:41 +02:00
semantic-release-bot
a98c178ca7 chore(release): 3.0.0-dev.2 [skip ci]
# [3.0.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-12-02)

### Features

* **YouTube:** Clarify patch descriptions ([#3350](https://github.com/ReVanced/revanced-patches/issues/3350)) ([4672118](4672118e88))
2023-12-02 22:56:50 +00:00
KobeW50
4672118e88 feat(YouTube): Clarify patch descriptions (#3350)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-12-02 23:54:49 +01:00
semantic-release-bot
5587a5c869 chore(release): 3.0.0-dev.1 [skip ci]
# [3.0.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v2.203.0-dev.2...v3.0.0-dev.1) (2023-12-02)

### Features

* Allow choosing the vendor of GmsCore via patch options ([#3347](https://github.com/ReVanced/revanced-patches/issues/3347)) ([55bc7ec](55bc7ecbae))

### BREAKING CHANGES

* Various APIs have been moved to `GmsCoreSupportAbstractPatch`
2023-12-02 21:52:39 +00:00
oSumAtrIX
55bc7ecbae feat: Allow choosing the vendor of GmsCore via patch options (#3347)
BREAKING CHANGE: Various APIs have been moved to `GmsCoreSupportAbstractPatch`
2023-12-02 22:50:40 +01:00
72 changed files with 1021 additions and 919 deletions

View File

@@ -1,3 +1,69 @@
# [3.0.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.7...v3.0.0-dev.8) (2023-12-11)
### Features
* **YouTube - Alternative Thumbnails:** Add option to use DeArrow ([#3378](https://github.com/ReVanced/revanced-patches/issues/3378)) ([41217f6](https://github.com/ReVanced/revanced-patches/commit/41217f61e600e47dd6812864bff22ee054521d3c))
# [3.0.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.6...v3.0.0-dev.7) (2023-12-07)
### Features
* **Twitter - Dynamic Color:** Remove blue icon and update app name ([#3384](https://github.com/ReVanced/revanced-patches/issues/3384)) ([3db6615](https://github.com/ReVanced/revanced-patches/commit/3db6615568e399aa13dac093868df3d0e1ebc4c3))
# [3.0.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.5...v3.0.0-dev.6) (2023-12-05)
### Features
* **Twitch - Settings:** Support version `16.1.0` and `15.4.1` ([#3377](https://github.com/ReVanced/revanced-patches/issues/3377)) ([062310d](https://github.com/ReVanced/revanced-patches/commit/062310dcc3923568c96171420c7fb9c0c2144233))
# [3.0.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.4...v3.0.0-dev.5) (2023-12-04)
### Bug Fixes
* Use correct class loader to load resources ([1d5f1f8](https://github.com/ReVanced/revanced-patches/commit/1d5f1f83be1f4eb78381887cd59f1649f1ed6d71))
### Features
* **Photomath:** Constrain patches to last working version ([f9a5dc6](https://github.com/ReVanced/revanced-patches/commit/f9a5dc6c91f37e9d7018e631739ca61511940d29))
# [3.0.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.3...v3.0.0-dev.4) (2023-12-04)
### Bug Fixes
* **YouTube - Minimized playback:** Fix PIP incorrectly shown for some Shorts playback ([#3364](https://github.com/ReVanced/revanced-patches/issues/3364)) ([84607ff](https://github.com/ReVanced/revanced-patches/commit/84607ff5f4bd30d328cdc4e1d46070a86d6c56bf))
# [3.0.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.2...v3.0.0-dev.3) (2023-12-04)
### Bug Fixes
* **YouTube - Return YouTube Dislike:** Prevent the first Short opened from freezing the UI ([#3359](https://github.com/ReVanced/revanced-patches/issues/3359)) ([e024409](https://github.com/ReVanced/revanced-patches/commit/e024409219bfbccc32c337d95da24b7146b6c7b7))
# [3.0.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v3.0.0-dev.1...v3.0.0-dev.2) (2023-12-02)
### Features
* **YouTube:** Clarify patch descriptions ([#3350](https://github.com/ReVanced/revanced-patches/issues/3350)) ([f2b9df4](https://github.com/ReVanced/revanced-patches/commit/f2b9df4e22a1c537cbd383087a3d724c3cdc1784))
# [3.0.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v2.203.0-dev.2...v3.0.0-dev.1) (2023-12-02)
### Features
* Allow choosing the vendor of GmsCore via patch options ([#3347](https://github.com/ReVanced/revanced-patches/issues/3347)) ([161261c](https://github.com/ReVanced/revanced-patches/commit/161261cfeab4616764d20efe46fcfd483299741c))
### BREAKING CHANGES
* Various APIs have been moved to `GmsCoreSupportAbstractPatch`
# [2.203.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v2.203.0-dev.1...v2.203.0-dev.2) (2023-12-02)

View File

@@ -309,20 +309,16 @@ public final class app/revanced/patches/music/misc/androidauto/BypassCertificate
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/music/misc/microg/MicroGBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/music/misc/microg/MicroGBytecodePatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public final class app/revanced/patches/music/misc/gms/Constants {
public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/Constants;
}
public final class app/revanced/patches/music/misc/microg/MicroGResourcePatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/music/misc/microg/MicroGResourcePatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/AbstractGmsCoreSupportPatch {
public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/GmsCoreSupportPatch;
}
public final class app/revanced/patches/music/misc/microg/shared/Constants {
public static final field INSTANCE Lapp/revanced/patches/music/misc/microg/shared/Constants;
public final class app/revanced/patches/music/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/AbstractGmsCoreSupportResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/music/misc/gms/GmsCoreSupportResourcePatch;
}
public final class app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch : app/revanced/patcher/patch/BytecodePatch {
@@ -573,6 +569,26 @@ public final class app/revanced/patches/shared/misc/fix/verticalscroll/VerticalS
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public abstract class app/revanced/patches/shared/misc/gms/AbstractGmsCoreSupportPatch : app/revanced/patcher/patch/BytecodePatch {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patches/shared/misc/gms/AbstractGmsCoreSupportResourcePatch;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Lapp/revanced/patches/shared/misc/gms/AbstractGmsCoreSupportResourcePatch;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public abstract class app/revanced/patches/shared/misc/gms/AbstractGmsCoreSupportResourcePatch : app/revanced/patcher/patch/ResourcePatch {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
protected final fun getGmsCoreVendor ()Ljava/lang/String;
}
public final class app/revanced/patches/shared/misc/gms/fingerprints/GmsCoreSupportFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
public static final field GET_GMS_CORE_VENDOR_METHOD_NAME Ljava/lang/String;
public static final field INSTANCE Lapp/revanced/patches/shared/misc/gms/fingerprints/GmsCoreSupportFingerprint;
}
public abstract class app/revanced/patches/shared/settings/AbstractSettingsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public fun close ()V
@@ -641,7 +657,9 @@ public final class app/revanced/patches/shared/settings/preference/impl/ListPref
}
public final class app/revanced/patches/shared/settings/preference/impl/NonInteractivePreference : app/revanced/patches/shared/settings/preference/BasePreference {
public fun <init> (Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Lapp/revanced/patches/shared/settings/preference/impl/StringResource;)V
public fun <init> (Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Ljava/lang/String;Z)V
public synthetic fun <init> (Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Lapp/revanced/patches/shared/settings/preference/impl/StringResource;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSelectable ()Z
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
@@ -1347,7 +1365,6 @@ public final class app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch
public final class app/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/thumbnails/AlternativeThumbnailsPatch;
public final fun addImageUrlErrorCallbackHook (Ljava/lang/String;)V
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
@@ -1394,6 +1411,18 @@ public final class app/revanced/patches/youtube/misc/fix/playback/SpoofSignature
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch : app/revanced/patches/shared/misc/gms/AbstractGmsCoreSupportPatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/gms/GmsCoreSupportPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportResourcePatch : app/revanced/patches/shared/misc/gms/AbstractGmsCoreSupportResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/gms/GmsCoreSupportResourcePatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/youtube/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/integrations/AbstractIntegrationsPatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/integrations/IntegrationsPatch;
}
@@ -1421,18 +1450,6 @@ public final class app/revanced/patches/youtube/misc/litho/filter/LithoFilterPat
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/misc/microg/MicroGBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/microg/MicroGBytecodePatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/youtube/misc/microg/MicroGResourcePatch : app/revanced/patcher/patch/ResourcePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/microg/MicroGResourcePatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}
public final class app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V

View File

@@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 2.203.0-dev.2
version = 3.0.0-dev.8

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
package app.revanced.patches.music.misc.gms
object Constants {
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"
}

View File

@@ -0,0 +1,34 @@
package app.revanced.patches.music.misc.gms
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorOption
import app.revanced.patches.music.misc.gms.fingerprints.*
import app.revanced.patches.shared.misc.gms.AbstractGmsCoreSupportPatch
import app.revanced.patches.youtube.misc.gms.fingerprints.CastContextFetchFingerprint
@Suppress("unused")
object GmsCoreSupportPatch : AbstractGmsCoreSupportPatch(
fromPackageName = REVANCED_MUSIC_PACKAGE_NAME,
toPackageName = MUSIC_PACKAGE_NAME,
primeMethodFingerprint = PrimeMethodFingerprint,
earlyReturnFingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
),
abstractGmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
compatiblePackages = setOf(CompatiblePackage("com.google.android.apps.youtube.music")),
fingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeMethodFingerprint,
)
) {
override val gmsCoreVendor by gmsCoreVendorOption
}

View File

@@ -0,0 +1,11 @@
package app.revanced.patches.music.misc.gms
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.shared.misc.gms.AbstractGmsCoreSupportResourcePatch
object GmsCoreSupportResourcePatch : AbstractGmsCoreSupportResourcePatch(
fromPackageName = MUSIC_PACKAGE_NAME,
toPackageName = REVANCED_MUSIC_PACKAGE_NAME,
spoofedPackageSignature = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"
)

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.music.misc.microg.fingerprints
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.music.misc.microg.fingerprints
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -1,18 +1,18 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object GooglePlayUtilityFingerprint : MethodFingerprint(
"I",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf(
"This should never happen.",
"MetadataValueReader",
"GooglePlayServicesUtil",
"com.android.vending",
"android.hardware.type.embedded"
)
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object GooglePlayUtilityFingerprint : MethodFingerprint(
"I",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf(
"This should never happen.",
"MetadataValueReader",
"GooglePlayServicesUtil",
"com.android.vending",
"android.hardware.type.embedded"
)
)

View File

@@ -1,8 +1,8 @@
package app.revanced.patches.youtube.misc.microg.fingerprints
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object PrimeFingerprint : MethodFingerprint(
internal object PrimeMethodFingerprint : MethodFingerprint(
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
)

View File

@@ -1,15 +1,14 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
internal object ServiceCheckFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf("Google Play Services not available")
)
package app.revanced.patches.music.misc.gms.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object ServiceCheckFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf("Google Play Services not available")
)

View File

@@ -1,70 +0,0 @@
package app.revanced.patches.music.misc.microg
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
import app.revanced.patches.music.misc.microg.fingerprints.*
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.util.microg.MicroGBytecodeHelper
@Patch(
name = "Vanced MicroG support",
description = "Allows YouTube Music to run without root and under a different package name.",
dependencies = [
ChangePackageNamePatch::class,
MicroGResourcePatch::class,
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.apps.youtube.music"
)
]
)
@Suppress("unused")
object MicroGBytecodePatch : BytecodePatch(
setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeFingerprint,
)
) {
// NOTE: the previous patch also replaced the following strings, but it seems like they are not needed:
// - "com.google.android.gms.chimera.GmsIntentOperationService",
// - "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
// - "com.google.android.gms.phenotype.internal.IPhenotypeService",
// - "com.google.android.gms.phenotype.PACKAGE_NAME",
// - "com.google.android.gms.phenotype.UPDATE",
// - "com.google.android.gms.phenotype",
override fun execute(context: BytecodeContext) {
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(REVANCED_MUSIC_PACKAGE_NAME)
MicroGBytecodeHelper.patchBytecode(
context,
arrayOf(
MicroGBytecodeHelper.packageNameTransform(
MUSIC_PACKAGE_NAME,
packageName
)
),
MicroGBytecodeHelper.PrimeMethodTransformationData(
PrimeFingerprint,
MUSIC_PACKAGE_NAME,
packageName
),
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint
)
)
}
}

View File

@@ -1,40 +0,0 @@
package app.revanced.patches.music.misc.microg
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
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_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
import app.revanced.util.microg.MicroGManifestHelper
import app.revanced.util.microg.MicroGResourceHelper
@Patch(
description = "Resource patch to allow YouTube Music ReVanced to run without root " +
"and under a different package name.",
dependencies = [ChangePackageNamePatch::class]
)
object MicroGResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(REVANCED_MUSIC_PACKAGE_NAME)
// update manifest
MicroGResourceHelper.patchManifest(
context,
MUSIC_PACKAGE_NAME,
packageName,
REVANCED_MUSIC_APP_NAME
)
// add metadata to the manifest
MicroGManifestHelper.addSpoofingMetadata(
context,
SPOOFED_PACKAGE_NAME,
SPOOFED_PACKAGE_SIGNATURE
)
}
}

View File

@@ -1,9 +0,0 @@
package app.revanced.patches.music.misc.microg.shared
object Constants {
internal const val REVANCED_MUSIC_APP_NAME = "YT Music ReVanced"
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

@@ -14,7 +14,7 @@ import kotlin.random.Random
name = "Spoof device ID",
description = "Spoofs device ID to mitigate manual bans by developers.",
dependencies = [SignatureDetectionPatch::class],
compatiblePackages = [CompatiblePackage("com.microblink.photomath")]
compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.32.0"])]
)
@Suppress("unused")
object SpoofDeviceIdPatch : BytecodePatch(

View File

@@ -13,7 +13,7 @@ import app.revanced.patches.photomath.misc.unlockplus.fingerprints.IsPlusUnlocke
@Patch(
name = "Unlock plus",
dependencies = [SignatureDetectionPatch::class, EnableBookpointPatch::class],
compatiblePackages = [CompatiblePackage("com.microblink.photomath")]
compatiblePackages = [CompatiblePackage("com.microblink.photomath", ["8.32.0"])]
)
@Suppress("unused")
object UnlockPlusPatch : BytecodePatch(

View File

@@ -0,0 +1,308 @@
package app.revanced.patches.shared.misc.gms
import app.revanced.patcher.PatchClass
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
import app.revanced.patches.shared.misc.gms.AbstractGmsCoreSupportPatch.Constants.ACTIONS
import app.revanced.patches.shared.misc.gms.AbstractGmsCoreSupportPatch.Constants.AUTHORITIES
import app.revanced.patches.shared.misc.gms.AbstractGmsCoreSupportPatch.Constants.PERMISSIONS
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint
import app.revanced.patches.shared.misc.gms.fingerprints.GmsCoreSupportFingerprint.GET_GMS_CORE_VENDOR_METHOD_NAME
import app.revanced.util.exception
import app.revanced.util.returnEarly
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
import com.android.tools.smali.dexlib2.util.MethodUtil
/**
* A patch that allows Google apps to run without root and under a different package name
* by using GmsCore instead of Google Play Services.
*
* @param fromPackageName The package name of the original app.
* @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
* @param primeMethodFingerprint The fingerprint of the "prime" method that needs to be patched.
* @param earlyReturnFingerprints The fingerprints of methods that need to be returned early.
* @param abstractGmsCoreSupportResourcePatch The corresponding resource patch that is used to patch the resources.
* @param dependencies Additional dependencies of this patch.
* @param compatiblePackages The compatible packages of this patch.
* @param fingerprints The fingerprints of this patch.
*/
abstract class AbstractGmsCoreSupportPatch(
private val fromPackageName: String,
private val toPackageName: String,
private val primeMethodFingerprint: MethodFingerprint,
private val earlyReturnFingerprints: Set<MethodFingerprint>,
abstractGmsCoreSupportResourcePatch: AbstractGmsCoreSupportResourcePatch,
dependencies: Set<PatchClass> = setOf(),
compatiblePackages: Set<CompatiblePackage>? = null,
fingerprints: Set<MethodFingerprint> = emptySet(),
) : BytecodePatch(
name = "GmsCore support",
description = "Allows Google apps to run without root and under a different package name " +
"by using GmsCore instead of Google Play Services.",
dependencies = setOf(ChangePackageNamePatch::class, abstractGmsCoreSupportResourcePatch::class) + dependencies,
compatiblePackages = compatiblePackages,
fingerprints = setOf(GmsCoreSupportFingerprint) + fingerprints,
requiresIntegrations = true
) {
init {
// Manually register all options of the resource patch so that they are visible in the patch API.
abstractGmsCoreSupportResourcePatch.options.values.forEach(options::register)
}
internal abstract val gmsCoreVendor: String?
override fun execute(context: BytecodeContext) {
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName)
// Transform all strings using all provided transforms, first match wins.
val transformations = arrayOf(
::commonTransform,
::contentUrisTransform,
packageNameTransform(fromPackageName, packageName)
)
context.transformStringReferences transform@{ string ->
transformations.forEach { transform ->
transform(string)?.let { transformedString -> return@transform transformedString }
}
return@transform null
}
// Specific method that needs to be patched.
transformPrimeMethod(packageName)
// Return these methods early to prevent the app from crashing.
earlyReturnFingerprints.toList().returnEarly()
// Change the vendor of GmsCore in ReVanced Integrations.
GmsCoreSupportFingerprint.result?.mutableClass?.methods
?.single { it.name == GET_GMS_CORE_VENDOR_METHOD_NAME }
?.replaceInstruction(0, "const-string v0, \"$gmsCoreVendor\"")
?: throw GmsCoreSupportFingerprint.exception
}
private fun BytecodeContext.transformStringReferences(transform: (str: String) -> String?) = classes.forEach {
val mutableClass by lazy {
proxy(it).mutableClass
}
it.methods.forEach classLoop@{ methodDef ->
val implementation = methodDef.implementation ?: return@classLoop
val mutableMethod by lazy {
mutableClass.methods.first { MethodUtil.methodSignaturesMatch(it, methodDef) }
}
implementation.instructions.forEachIndexed insnLoop@{ index, instruction ->
val string = ((instruction as? Instruction21c)?.reference as? StringReference)?.string
?: return@insnLoop
// Apply transformation.
val transformedString = transform(string) ?: return@insnLoop
mutableMethod.replaceInstruction(
index,
BuilderInstruction21c(
Opcode.CONST_STRING,
instruction.registerA,
ImmutableStringReference(transformedString)
)
)
}
}
}
// region Collection of transformations that are applied to all strings.
private fun commonTransform(referencedString: String): String? =
when (referencedString) {
"com.google",
"com.google.android.gms",
in PERMISSIONS,
in ACTIONS,
in AUTHORITIES -> referencedString.replace("com.google", gmsCoreVendor!!)
// No vendor prefix for whatever reason...
"subscribedfeeds" -> "$gmsCoreVendor.subscribedfeeds"
else -> null
}
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", gmsCoreVendor!!)}"
)
}
}
// gms also has a 'subscribedfeeds' authority, check for that one too
val subFeedsUriPrefix = "content://subscribedfeeds"
if (str.startsWith(subFeedsUriPrefix)) {
return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendor.subscribedfeeds")
}
}
return null
}
private fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? = { string ->
when (string) {
"$fromPackageName.SuggestionsProvider",
"$fromPackageName.fileprovider" -> string.replace(fromPackageName, toPackageName)
else -> null
}
}
private fun transformPrimeMethod(packageName: String) = primeMethodFingerprint.result?.mutableMethod?.apply {
var register = 2
val index = getInstructions().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
}
replaceInstruction(
index, "const-string v$register, \"$packageName\""
)
}
?: throw primeMethodFingerprint.exception
/**
* A collection of permissions, intents and content provider authorities
* that are present in GmsCore which need to be transformed.
*
* NOTE: The following were present, but it seems like they are not needed to be transformed:
* - com.google.android.gms.chimera.GmsIntentOperationService
* - com.google.android.gms.phenotype.internal.IPhenotypeCallbacks
* - com.google.android.gms.phenotype.internal.IPhenotypeService
* - com.google.android.gms.phenotype.PACKAGE_NAME
* - com.google.android.gms.phenotype.UPDATE
* - com.google.android.gms.phenotype
*/
private object Constants {
/**
* A list of all permissions.
*/
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",
)
/**
* All intent actions.
*/
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"
)
/**
* All content provider authorities.
*/
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"
)
}
// endregion
}

View File

@@ -0,0 +1,112 @@
package app.revanced.patches.shared.misc.gms
import app.revanced.patcher.PatchClass
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
import app.revanced.util.mergeStrings
import org.w3c.dom.Element
import org.w3c.dom.Node
/**
* Abstract resource patch that allows Google apps to run without root and under a different package name
* by using GmsCore instead of Google Play Services.
*
* @param fromPackageName The package name of the original app.
* @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
* @param spoofedPackageSignature The signature of the package to spoof to.
* @param dependencies Additional dependencies of this patch.
*/
abstract class AbstractGmsCoreSupportResourcePatch(
private val fromPackageName: String,
private val toPackageName: String,
private val spoofedPackageSignature: String,
dependencies: Set<PatchClass> = setOf()
) : ResourcePatch(dependencies = setOf(ChangePackageNamePatch::class) + dependencies) {
internal val gmsCoreVendorOption = stringPatchOption(
key = "gmsCoreVendor",
default = "com.mgoogle",
values = mapOf(
"Vanced" to "com.mgoogle",
"ReVanced" to "app.revanced"
),
title = "GmsCore Vendor",
description = "The group id of the GmsCore vendor.",
required = true
) { it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$")) }
protected val gmsCoreVendor by gmsCoreVendorOption
override fun execute(context: ResourceContext) {
context.mergeStrings("gms/host/values/strings.xml")
context.patchManifest()
context.addSpoofingMetadata()
}
/**
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
*/
private fun ResourceContext.addSpoofingMetadata() {
fun Node.adoptChild(tagName: String, block: Element.() -> Unit) {
val child = ownerDocument.createElement(tagName)
child.block()
appendChild(child)
}
xmlEditor["AndroidManifest.xml"].use {
val applicationNode = it
.file
.getElementsByTagName("application")
.item(0)
// Spoof package name and signature.
applicationNode.adoptChild("meta-data") {
setAttribute("android:name", "$gmsCoreVendor.android.gms.SPOOFED_PACKAGE_NAME")
setAttribute("android:value", fromPackageName)
}
applicationNode.adoptChild("meta-data") {
setAttribute("android:name", "$gmsCoreVendor.android.gms.SPOOFED_PACKAGE_SIGNATURE")
setAttribute("android:value", spoofedPackageSignature)
}
// GmsCore presence detection in ReVanced Integrations.
applicationNode.adoptChild("meta-data") {
// TODO: The name of this metadata should be dynamic.
setAttribute("android:name", "app.revanced.MICROG_PACKAGE_NAME")
setAttribute("android:value", "$gmsCoreVendor.android.gms")
}
}
}
/**
* Patch the manifest to support GmsCore.
*/
private fun ResourceContext.patchManifest() {
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(toPackageName)
val manifest = this["AndroidManifest.xml"].readText()
this["AndroidManifest.xml"].writeText(
manifest.replace(
"package=\"$fromPackageName",
"package=\"$packageName"
).replace(
"android:authorities=\"$fromPackageName",
"android:authorities=\"$packageName"
).replace(
"$fromPackageName.permission.C2D_MESSAGE",
"$packageName.permission.C2D_MESSAGE"
).replace(
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
"$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
).replace(
"com.google.android.c2dm",
"$gmsCoreVendor.android.c2dm"
).replace(
"</queries>",
"<package android:name=\"$gmsCoreVendor.android.gms\"/></queries>"
)
)
}
}

View File

@@ -0,0 +1,11 @@
package app.revanced.patches.shared.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
object GmsCoreSupportFingerprint : MethodFingerprint(
customFingerprint = { _, classDef ->
classDef.type.endsWith("GmsCoreSupport;")
}
) {
const val GET_GMS_CORE_VENDOR_METHOD_NAME = "getGmsCoreVendor"
}

View File

@@ -9,7 +9,7 @@ import org.w3c.dom.Element
*
* @param key The key of the preference.
* @param title The title of the preference.
* @param tag The tag of the preference.
* @param tag The full class name for the preference.
* @param summary The summary of the preference.
*/
abstract class BasePreference(

View File

@@ -14,15 +14,21 @@ import org.w3c.dom.Element
*
* @param title The title of the preference.
* @param summary The summary of the text preference.
* @param selectable If this preference responds to tapping.
* Setting to 'true' restores the horizontal dividers on the top and bottom,
* but tapping will still do nothing since this Preference has no key.
*/
class NonInteractivePreference(
title: StringResource,
summary: StringResource,
) : BasePreference(null, title, summary, "Preference") {
summary: StringResource?,
tag: String = "Preference",
// If androidx.preference is later used, this can be changed to the show top/bottom dividers feature.
val selectable: Boolean = false
) : BasePreference(null, title, summary, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit): Element {
return super.serialize(ownerDocument, resourceCallback).apply {
addSummary(summary?.also { resourceCallback.invoke(it)
setAttribute("android:selectable", false.toString())
setAttribute("android:selectable", selectable.toString())
})
}
}

View File

@@ -30,7 +30,7 @@ import java.io.Closeable
description = "Adds settings menu to Twitch.",
dependencies = [IntegrationsPatch::class, SettingsResourcePatch::class],
compatiblePackages = [
CompatiblePackage("tv.twitch.android.app", ["16.9.1"])
CompatiblePackage("tv.twitch.android.app", ["15.4.1", "16.1.0", "16.9.1"])
]
)
object SettingsPatch : BytecodePatch(

View File

@@ -10,7 +10,7 @@ import java.nio.file.Files
@Patch(
name = "Dynamic color",
description = "Replaces the default Twitter Blue with the user's Material You palette.",
description = "Replaces the default X (Formerly Twitter) Blue with the user's Material You palette.",
compatiblePackages = [CompatiblePackage("com.twitter.android")]
)
@Suppress("unused")
@@ -46,8 +46,7 @@ object DynamicColorPatch : ResourcePatch() {
"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"
"deep_transparent_twitter_blue" to "@android:color/system_accent1_200"
).forEach { (k, v) ->
val colorElement = document.createElement("color")

View File

@@ -17,7 +17,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Enable slide to seek",
description = "Enable slide to seek instead of playing at 2x speed.",
description = "Enable slide to seek instead of playing at 2x speed when pressing and holding in the video player.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
compatiblePackages = [
CompatiblePackage(

View File

@@ -12,7 +12,7 @@ import java.nio.file.Files
@Patch(
name = "Custom branding",
description = "Changes the app icon and name to your choice (defaults to YouTube ReVanced and the ReVanced logo).",
description = "Changes the app name and icon to your choice (defaults to \"YouTube ReVanced\" and the ReVanced logo).",
compatiblePackages = [
CompatiblePackage("com.google.android.youtube")
],

View File

@@ -13,7 +13,7 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
@Patch(
name = "Hide video action buttons",
description = "Adds the options to hide action buttons under a video.",
description = "Adds options to hide action buttons under a video.",
dependencies = [
ResourceMappingPatch::class,
LithoFilterPatch::class

View File

@@ -14,7 +14,7 @@ import com.android.tools.smali.dexlib2.Opcode
@Patch(
name = "Hide captions button",
description = "Hides the captions button on video player.",
description = "Hides the captions button in the video player.",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class

View File

@@ -18,7 +18,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction3rc
@Patch(
name = "Hide player buttons",
description = "Adds the option to hide video player previous and next buttons.",
description = "Hides previous and next buttons in the video player.",
dependencies = [
IntegrationsPatch::class,
SettingsPatch::class

View File

@@ -16,7 +16,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
@Patch(
name = "Hide endscreen cards",
description = "Hides the suggested video cards at the end of a video in fullscreen.",
description = "Hides the suggested video cards at the end of videos.",
dependencies = [
IntegrationsPatch::class,
HideEndscreenCardsResourcePatch::class

View File

@@ -18,7 +18,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Disable rolling number animations",
description = "Disables rolling number animations of video view count, upload time, and user likes",
description = "Disables rolling number animations of video view count, user likes, and upload time.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
compatiblePackages = [
CompatiblePackage(

View File

@@ -14,7 +14,7 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
@Patch(
name = "Disable player popup panels",
description = "Disables panels from appearing automatically when going into fullscreen (playlist or live chat).",
description = "Disables panels (such as live chat) from opening automatically.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
compatiblePackages = [
CompatiblePackage(

View File

@@ -13,7 +13,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Custom player overlay opacity",
description = "Change the opacity of the player background, when player controls are visible.",
description = "Change the opacity of the player background when player controls are visible.",
dependencies = [CustomPlayerOverlayOpacityResourcePatch::class],
compatiblePackages = [
CompatiblePackage("com.google.android.youtube")

View File

@@ -34,7 +34,7 @@ import com.android.tools.smali.dexlib2.iface.reference.StringReference
@Patch(
name = "SponsorBlock",
description = "Integrates SponsorBlock which allows skipping video segments such as sponsored content.",
description = "Integrates SponsorBlock, which can skip undesired video segments such as sponsored content.",
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [

View File

@@ -18,7 +18,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Spoof app version",
description = "Tricks YouTube into thinking you are running an older version of the app. " +
"One of the side effects also includes restoring the old UI.",
"This can be used to restore old UI elements and features.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
compatiblePackages = [
CompatiblePackage(

View File

@@ -1,22 +1,36 @@
package app.revanced.patches.youtube.layout.thumbnails
import app.revanced.util.exception
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.shared.settings.preference.impl.*
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.*
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.MessageDigestImageUrlFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.MessageDigestImageUrlParentFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.RequestFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnFailureFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnResponseStartedFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback.OnSucceededFingerprint
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
@Patch(
name = "Alternative thumbnails",
description = "Adds options to replace video thumbnails with still image captures of the video.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AlternativeThumbnailsResourcePatch::class],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
@@ -34,7 +48,11 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
)
@Suppress("unused")
object AlternativeThumbnailsPatch : BytecodePatch(
setOf(MessageDigestImageUrlParentFingerprint, CronetURLRequestCallbackOnResponseStartedFingerprint)
setOf(
MessageDigestImageUrlParentFingerprint,
OnResponseStartedFingerprint,
RequestFingerprint,
)
) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/patches/AlternativeThumbnailsPatch;"
@@ -51,11 +69,13 @@ object AlternativeThumbnailsPatch : BytecodePatch(
/**
* @param highPriority If the hook should be called before all other hooks.
*/
@Suppress("SameParameterValue")
private fun addImageUrlHook(targetMethodClass: String, highPriority: Boolean) {
loadImageUrlMethod.addInstructions(
if (highPriority) 0 else loadImageUrlIndex, """
invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String;
move-result-object p1
if (highPriority) 0 else loadImageUrlIndex,
"""
invoke-static { p1 }, $targetMethodClass->overrideImageURL(Ljava/lang/String;)Ljava/lang/String;
move-result-object p1
"""
)
loadImageUrlIndex += 2
@@ -65,103 +85,197 @@ object AlternativeThumbnailsPatch : BytecodePatch(
* If a connection completed, which includes normal 200 responses but also includes
* status 404 and other error like http responses.
*/
@Suppress("SameParameterValue")
private fun addImageUrlSuccessCallbackHook(targetMethodClass: String) {
loadImageSuccessCallbackMethod.addInstruction(
loadImageSuccessCallbackIndex++,
"invoke-static { p2 }, $targetMethodClass->handleCronetSuccess(Lorg/chromium/net/UrlResponseInfo;)V"
"invoke-static { p1, p2 }, $targetMethodClass->handleCronetSuccess(" +
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;)V"
)
}
/**
* If a connection outright failed to complete any connection.
*/
fun addImageUrlErrorCallbackHook(targetMethodClass: String) {
@Suppress("SameParameterValue")
private fun addImageUrlErrorCallbackHook(targetMethodClass: String) {
loadImageErrorCallbackMethod.addInstruction(
loadImageErrorCallbackIndex++,
"invoke-static { p2, p3 }, $targetMethodClass->handleCronetFailure(Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V"
"invoke-static { p1, p2, p3 }, $targetMethodClass->handleCronetFailure(" +
"Lorg/chromium/net/UrlRequest;Lorg/chromium/net/UrlResponseInfo;Ljava/io/IOException;)V"
)
}
override fun execute(context: BytecodeContext) {
SettingsPatch.PreferenceScreen.LAYOUT.addPreferences(
PreferenceScreen(
"revanced_alt_thumbnails_preference_screen",
StringResource("revanced_alt_thumbnails_preference_screen_title", "Alternative thumbnails"),
"revanced_alt_thumbnail_preference_screen",
StringResource("revanced_alt_thumbnail_preference_screen_title", "Alternative thumbnails"),
listOf(
NonInteractivePreference(
StringResource("revanced_alt_thumbnail_about_title", "Thumbnails in use"),
null, // Summary is dynamically updated based on the current settings.
tag = "app.revanced.integrations.settingsmenu.AlternativeThumbnailsStatusPreference"
),
SwitchPreference(
"revanced_alt_thumbnail",
StringResource("revanced_alt_thumbnail_title", "Enable alternative thumbnails"),
StringResource("revanced_alt_thumbnail_summary_on", "YouTube video stills shown"),
StringResource("revanced_alt_thumbnail_summary_off", "Original YouTube thumbnails shown")
"revanced_alt_thumbnail_dearrow",
StringResource("revanced_alt_thumbnail_dearrow_title", "Enable DeArrow"),
StringResource("revanced_alt_thumbnail_dearrow_summary_on", "Using DeArrow"),
StringResource("revanced_alt_thumbnail_dearrow_summary_off", "Not using DeArrow")
),
SwitchPreference(
"revanced_alt_thumbnail_dearrow_connection_toast",
StringResource("revanced_alt_thumbnail_dearrow_connection_toast_title", "Show toast if API is not available"),
StringResource("revanced_alt_thumbnail_dearrow_connection_toast_summary_on", "Toast shown if DeArrow is not available"),
StringResource("revanced_alt_thumbnail_dearrow_connection_toast_summary_off", "Toast not shown if DeArrow is not available")
),
TextPreference(
"revanced_alt_thumbnail_dearrow_api_url",
StringResource(
"revanced_alt_thumbnail_dearrow_api_url_title",
"DeArrow API endpoint"
),
StringResource(
"revanced_alt_thumbnail_dearrow_api_url_summary",
"The URL of the DeArrow thumbnail cache endpoint. " +
"Do not change this unless you know what you\\\'re doing"
),
),
NonInteractivePreference(
StringResource(
"revanced_alt_thumbnail_dearrow_about_title",
"About DeArrow"
),
StringResource(
"revanced_alt_thumbnail_dearrow_about_summary",
"DeArrow provides crowd sourced thumbnails for YouTube videos. " +
"These thumbnails are often more relevant than those provided by YouTube. " +
"If enabled, video URLs will be sent to the API server and no other data is sent."
+ "\\n\\nTap here to learn more about DeArrow"
),
// Custom about preference with link to the DeArrow website.
tag = "app.revanced.integrations.settingsmenu.AlternativeThumbnailsAboutDeArrowPreference",
selectable = true
),
SwitchPreference(
"revanced_alt_thumbnail_stills",
StringResource("revanced_alt_thumbnail_stills_title", "Enable still video captures"),
StringResource("revanced_alt_thumbnail_stills_summary_on", "Using YouTube video still captures"),
StringResource("revanced_alt_thumbnail_stills_summary_off", "Not using YouTube video still captures")
),
ListPreference(
"revanced_alt_thumbnail_type",
StringResource("revanced_alt_thumbnail_type_title", "Video time to take the still from"),
"revanced_alt_thumbnail_stills_time",
StringResource("revanced_alt_thumbnail_stills_time_title", "Video time to take the still from"),
ArrayResource(
"revanced_alt_thumbnail_type_entries",
listOf(
StringResource("revanced_alt_thumbnail_type_entry_1", "Beginning of video"),
StringResource("revanced_alt_thumbnail_type_entry_2", "Middle of video"),
StringResource("revanced_alt_thumbnail_type_entry_3", "End of video"),
StringResource("revanced_alt_thumbnail_stills_time_entry_1", "Beginning of video"),
StringResource("revanced_alt_thumbnail_stills_time_entry_2", "Middle of video"),
StringResource("revanced_alt_thumbnail_stills_time_entry_3", "End of video"),
)
),
ArrayResource(
"revanced_alt_thumbnail_type_entry_values",
"revanced_alt_thumbnail_stills_time_entry_values",
listOf(
StringResource("revanced_alt_thumbnail_type_entry_value_1", "1"),
StringResource("revanced_alt_thumbnail_type_entry_value_2", "2"),
StringResource("revanced_alt_thumbnail_type_entry_value_3", "3"),
StringResource("revanced_alt_thumbnail_stills_time_entry_value_1", "1"),
StringResource("revanced_alt_thumbnail_stills_time_entry_value_2", "2"),
StringResource("revanced_alt_thumbnail_stills_time_entry_value_3", "3"),
)
)
),
SwitchPreference(
"revanced_alt_thumbnail_fast_quality",
StringResource("revanced_alt_thumbnail_fast_quality_title", "Use fast alternative thumbnails"),
"revanced_alt_thumbnail_stills_fast",
StringResource(
"revanced_alt_thumbnail_fast_quality_summary_on",
"Using medium quality stills. Thumbnails will load faster, but live streams, unreleased, or very old videos may show blank thumbnails"
"revanced_alt_thumbnail_stills_fast_title",
"Use fast still captures"
),
StringResource("revanced_alt_thumbnail_fast_quality_summary_off", "Using high quality stills")
StringResource(
"revanced_alt_thumbnail_stills_fast_summary_on",
"Using medium quality still captures. " +
"Thumbnails will load faster, but live streams, unreleased, " +
"or very old videos may show blank thumbnails"
),
StringResource(
"revanced_alt_thumbnail_stills_fast_summary_off",
"Using high quality still captures"
)
),
NonInteractivePreference(
StringResource("revanced_alt_thumbnail_about_title", "About"),
StringResource(
"revanced_alt_thumbnail_about_summary",
"Alternative thumbnails are still images from the beginning/middle/end of each video. No external API is used, as these images are built into YouTube"
)
"revanced_alt_thumbnail_stills_about_title",
"About still video captures"
),
StringResource(
"revanced_alt_thumbnail_stills_about_summary",
"Still captures are taken from the beginning/middle/end of each video. " +
"These images are built into YouTube and no external API is used"
),
// Restore the preference dividers to keep it from looking weird.
selectable = true
)
),
StringResource("revanced_alt_thumbnails_preference_screen_summary", "Video thumbnail settings")
StringResource("revanced_alt_thumbnail_preference_screen_summary", "Video thumbnail settings")
)
)
MessageDigestImageUrlParentFingerprint.result
?: throw MessageDigestImageUrlParentFingerprint.exception
MessageDigestImageUrlFingerprint.resolve(context, MessageDigestImageUrlParentFingerprint.result!!.classDef)
MessageDigestImageUrlFingerprint.result?.apply {
loadImageUrlMethod = mutableMethod
} ?: throw MessageDigestImageUrlFingerprint.exception
addImageUrlHook(INTEGRATIONS_CLASS_DESCRIPTOR, true)
fun MethodFingerprint.getResultOrThrow() =
result ?: throw exception
fun MethodFingerprint.alsoResolve(fingerprint: MethodFingerprint) =
also { resolve(context, fingerprint.getResultOrThrow().classDef) }.getResultOrThrow()
CronetURLRequestCallbackOnResponseStartedFingerprint.result
?: throw CronetURLRequestCallbackOnResponseStartedFingerprint.exception
CronetURLRequestCallbackOnSucceededFingerprint.resolve(
context,
CronetURLRequestCallbackOnResponseStartedFingerprint.result!!.classDef
)
CronetURLRequestCallbackOnSucceededFingerprint.result?.apply {
loadImageSuccessCallbackMethod = mutableMethod
} ?: throw CronetURLRequestCallbackOnSucceededFingerprint.exception
addImageUrlSuccessCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
fun MethodFingerprint.resolveAndLetMutableMethod(
fingerprint: MethodFingerprint,
block: (MutableMethod) -> Unit
) = alsoResolve(fingerprint).also { block(it.mutableMethod) }
MessageDigestImageUrlFingerprint.resolveAndLetMutableMethod(MessageDigestImageUrlParentFingerprint) {
loadImageUrlMethod = it
addImageUrlHook(INTEGRATIONS_CLASS_DESCRIPTOR, true)
}
CronetURLRequestCallbackOnFailureFingerprint.resolve(
context,
CronetURLRequestCallbackOnResponseStartedFingerprint.result!!.classDef
)
CronetURLRequestCallbackOnFailureFingerprint.result?.apply {
loadImageErrorCallbackMethod = mutableMethod
} ?: throw CronetURLRequestCallbackOnFailureFingerprint.exception
OnSucceededFingerprint.resolveAndLetMutableMethod(OnResponseStartedFingerprint) {
loadImageSuccessCallbackMethod = it
addImageUrlSuccessCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
}
OnFailureFingerprint.resolveAndLetMutableMethod(OnResponseStartedFingerprint) {
loadImageErrorCallbackMethod = it
addImageUrlErrorCallbackHook(INTEGRATIONS_CLASS_DESCRIPTOR)
}
// The URL is required for the failure callback hook, but the URL field is obfuscated.
// Add a helper get method that returns the URL field.
RequestFingerprint.getResultOrThrow().apply {
// The url is the only string field that is set inside the constructor.
val urlFieldInstruction = mutableMethod.getInstructions().first {
if (it.opcode != Opcode.IPUT_OBJECT) return@first false
val reference = (it as ReferenceInstruction).reference as FieldReference
reference.type == "Ljava/lang/String;"
} as ReferenceInstruction
val urlFieldName = (urlFieldInstruction.reference as FieldReference).name
val definingClass = RequestFingerprint.IMPLEMENTATION_CLASS_NAME
val addedMethodName = "getHookedUrl"
mutableClass.methods.add(
ImmutableMethod(
definingClass,
addedMethodName,
emptyList(),
"Ljava/lang/String;",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(2)
).toMutable().apply {
addInstructions(
"""
iget-object v0, p0, $definingClass->${urlFieldName}:Ljava/lang/String;
return-object v0
"""
)
})
}
}
}

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.thumbnails
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.mergeStrings
@Patch(
dependencies = [SettingsPatch::class]
)
internal object AlternativeThumbnailsResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
context.mergeStrings("alternativethumbnails/host/values/strings.xml")
}
}

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.RequestFingerprint.IMPLEMENTATION_CLASS_NAME
import com.android.tools.smali.dexlib2.AccessFlags
internal object RequestFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
customFingerprint = { _, classDef ->
classDef.type == IMPLEMENTATION_CLASS_NAME
}
) {
const val IMPLEMENTATION_CLASS_NAME = "Lorg/chromium/net/impl/CronetUrlRequest;"
}

View File

@@ -1,10 +1,10 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object CronetURLRequestCallbackOnFailureFingerprint : MethodFingerprint(
internal object OnFailureFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;", "Lorg/chromium/net/CronetException;"),

View File

@@ -1,11 +1,11 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
// Acts as a parent fingerprint.
internal object CronetURLRequestCallbackOnResponseStartedFingerprint : MethodFingerprint(
internal object OnResponseStartedFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),

View File

@@ -1,10 +1,10 @@
package app.revanced.patches.youtube.layout.thumbnails.fingerprints
package app.revanced.patches.youtube.layout.thumbnails.fingerprints.cronet.request.callback
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object CronetURLRequestCallbackOnSucceededFingerprint : MethodFingerprint(
internal object OnSucceededFingerprint : MethodFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Lorg/chromium/net/UrlRequest;", "Lorg/chromium/net/UrlResponseInfo;"),

View File

@@ -112,7 +112,7 @@ object SpoofSignaturePatch : BytecodePatch(
// Hook the player parameters.
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameter(
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;)Ljava/lang/String;"
"$INTEGRATIONS_CLASS_DESCRIPTOR->spoofParameter(Ljava/lang/String;Z)Ljava/lang/String;"
)
// Force the seekbar time and chapters to always show up.

View File

@@ -0,0 +1,6 @@
package app.revanced.patches.youtube.misc.gms
internal object Constants {
const val YOUTUBE_PACKAGE_NAME = "com.google.android.youtube"
const val REVANCED_YOUTUBE_PACKAGE_NAME = "app.revanced.android.youtube"
}

View File

@@ -0,0 +1,67 @@
package app.revanced.patches.youtube.misc.gms
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patches.shared.misc.gms.AbstractGmsCoreSupportPatch
import app.revanced.patches.youtube.layout.buttons.cast.HideCastButtonPatch
import app.revanced.patches.youtube.misc.fix.playback.ClientSpoofPatch
import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.gms.GmsCoreSupportResourcePatch.gmsCoreVendorOption
import app.revanced.patches.youtube.misc.gms.fingerprints.*
import app.revanced.patches.youtube.shared.fingerprints.WatchWhileActivityFingerprint
import app.revanced.util.exception
@Suppress("unused")
object GmsCoreSupportPatch : AbstractGmsCoreSupportPatch(
fromPackageName = YOUTUBE_PACKAGE_NAME,
toPackageName = REVANCED_YOUTUBE_PACKAGE_NAME,
primeMethodFingerprint = PrimeMethodFingerprint,
earlyReturnFingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint
),
dependencies = setOf(
HideCastButtonPatch::class,
ClientSpoofPatch::class
),
abstractGmsCoreSupportResourcePatch = GmsCoreSupportResourcePatch,
compatiblePackages = setOf(
CompatiblePackage(
"com.google.android.youtube", setOf(
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.41",
"18.45.43"
)
)
),
fingerprints = setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeMethodFingerprint,
WatchWhileActivityFingerprint
)
) {
override val gmsCoreVendor by gmsCoreVendorOption
override fun execute(context: BytecodeContext) {
// Check the availability of GmsCore.
WatchWhileActivityFingerprint.result?.mutableMethod?.addInstruction(
0,
"invoke-static {}, Lapp/revanced/integrations/patches/GmsCoreSupport;->checkAvailability()V"
) ?: throw WatchWhileActivityFingerprint.exception
super.execute(context)
}
}

View File

@@ -0,0 +1,33 @@
package app.revanced.patches.youtube.misc.gms
import app.revanced.patcher.data.ResourceContext
import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
import app.revanced.patches.shared.misc.gms.AbstractGmsCoreSupportResourcePatch
import app.revanced.patches.shared.settings.preference.impl.Preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.youtube.misc.gms.Constants.REVANCED_YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.gms.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.youtube.misc.settings.SettingsPatch
object GmsCoreSupportResourcePatch : AbstractGmsCoreSupportResourcePatch(
fromPackageName = YOUTUBE_PACKAGE_NAME,
toPackageName = REVANCED_YOUTUBE_PACKAGE_NAME,
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
dependencies = setOf(SettingsPatch::class)
) {
override fun execute(context: ResourceContext) {
SettingsPatch.addPreference(
Preference(
StringResource("microg_settings", "GmsCore Settings"),
StringResource("microg_settings_summary", "Settings for GmsCore"),
Preference.Intent("$gmsCoreVendor.android.gms", "", "org.microg.gms.ui.SettingsActivity")
)
)
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(REVANCED_YOUTUBE_PACKAGE_NAME)
SettingsPatch.renameIntentsTargetPackage(packageName)
super.execute(context)
}
}

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.music.misc.microg.fingerprints
package app.revanced.patches.youtube.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.misc.microg.fingerprints
package app.revanced.patches.youtube.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.misc.microg.fingerprints
package app.revanced.patches.youtube.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.misc.microg.fingerprints
package app.revanced.patches.youtube.misc.gms.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -1,8 +1,8 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object PrimeFingerprint : MethodFingerprint(
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
package app.revanced.patches.youtube.misc.gms.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object PrimeMethodFingerprint : MethodFingerprint(
strings = listOf("com.google.android.GoogleCamera", "com.android.vending")
)

View File

@@ -1,4 +1,4 @@
package app.revanced.patches.youtube.misc.microg.fingerprints
package app.revanced.patches.youtube.misc.gms.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint

View File

@@ -1,79 +0,0 @@
package app.revanced.patches.youtube.misc.microg
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
import app.revanced.patches.youtube.shared.fingerprints.WatchWhileActivityFingerprint
import app.revanced.patches.youtube.layout.buttons.cast.HideCastButtonPatch
import app.revanced.patches.youtube.misc.fix.playback.ClientSpoofPatch
import app.revanced.patches.youtube.misc.microg.fingerprints.*
import app.revanced.patches.youtube.misc.microg.shared.Constants.PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants.REVANCED_PACKAGE_NAME
import app.revanced.util.microg.MicroGBytecodeHelper
@Patch(
name = "Vanced MicroG support",
description = "Allows YouTube to run without root and under a different package name with Vanced MicroG.",
dependencies = [
ChangePackageNamePatch::class,
MicroGResourcePatch::class,
HideCastButtonPatch::class,
ClientSpoofPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube", [
"18.32.39",
"18.37.36",
"18.38.44",
"18.43.45",
"18.44.41",
"18.45.41",
"18.45.43"
]
)
]
)
@Suppress("unused")
object MicroGBytecodePatch : BytecodePatch(
setOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeFingerprint,
WatchWhileActivityFingerprint
)
) {
override fun execute(context: BytecodeContext) {
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(REVANCED_PACKAGE_NAME)
// apply common microG patch
MicroGBytecodeHelper.patchBytecode(
context, arrayOf(
MicroGBytecodeHelper.packageNameTransform(
PACKAGE_NAME,
packageName
)
),
MicroGBytecodeHelper.PrimeMethodTransformationData(
PrimeFingerprint,
PACKAGE_NAME,
packageName
),
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint
)
)
// inject the notice for MicroG
MicroGBytecodeHelper.injectNotice(WatchWhileActivityFingerprint)
}
}

View File

@@ -1,57 +0,0 @@
package app.revanced.patches.youtube.misc.microg
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.packagename.ChangePackageNamePatch
import app.revanced.patches.shared.settings.preference.impl.Preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
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.SPOOFED_PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.microg.Constants.MICROG_VENDOR
import app.revanced.util.microg.MicroGManifestHelper
import app.revanced.util.microg.MicroGResourceHelper
@Patch(
dependencies = [
SettingsPatch::class,
ChangePackageNamePatch::class
]
)
object MicroGResourcePatch : ResourcePatch() {
override fun execute(context: ResourceContext) {
SettingsPatch.addPreference(
Preference(
StringResource("microg_settings", "MicroG Settings"),
StringResource("microg_settings_summary", "Settings for MicroG"),
Preference.Intent("$MICROG_VENDOR.android.gms", "", "org.microg.gms.ui.SettingsActivity")
)
)
val packageName = ChangePackageNamePatch.setOrGetFallbackPackageName(REVANCED_PACKAGE_NAME)
SettingsPatch.renameIntentsTargetPackage(packageName)
// update manifest
MicroGResourceHelper.patchManifest(
context,
PACKAGE_NAME,
packageName,
REVANCED_APP_NAME
)
// add metadata to manifest
MicroGManifestHelper.addSpoofingMetadata(
context,
SPOOFED_PACKAGE_NAME,
SPOOFED_PACKAGE_SIGNATURE
)
// add strings
MicroGResourceHelper.addStrings(context)
}
}

View File

@@ -1,8 +0,0 @@
package app.revanced.patches.youtube.misc.microg.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
internal object CastContextFetchFingerprint : MethodFingerprint(
strings = listOf("Error fetching CastContext.")
)

View File

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

View File

@@ -17,13 +17,19 @@ import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.Minimize
import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsParentFingerprint
import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@Patch(
name = "Minimized playback",
description = "Enables minimized and background playback.",
dependencies = [IntegrationsPatch::class, PlayerTypeHookPatch::class, SettingsPatch::class],
dependencies = [
IntegrationsPatch::class,
PlayerTypeHookPatch::class,
VideoInformationPatch::class,
SettingsPatch::class
],
compatiblePackages = [
CompatiblePackage(
"com.google.android.youtube",
@@ -65,7 +71,7 @@ object MinimizedPlaybackPatch : BytecodePatch(
mutableMethod.addInstructions(
0,
"""
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->isPlaybackNotShort()Z
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z
move-result v0
return v0
"""

View File

@@ -22,7 +22,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@Patch(
name = "Remove tracking query parameter",
description = "Remove the tracking query parameter from links.",
description = "Remove the tracking query parameter from links you share.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class],
compatiblePackages = [
CompatiblePackage(

View File

@@ -12,6 +12,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
import app.revanced.patches.youtube.video.information.fingerprints.*
import app.revanced.patches.youtube.video.playerresponse.PlayerResponseMethodHookPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -26,7 +27,7 @@ import com.android.tools.smali.dexlib2.util.MethodUtil
@Patch(
description = "Hooks YouTube to get information about the current playing video.",
dependencies = [IntegrationsPatch::class, VideoIdPatch::class]
dependencies = [IntegrationsPatch::class, VideoIdPatch::class, PlayerResponseMethodHookPatch::class]
)
object VideoInformationPatch : BytecodePatch(
setOf(
@@ -115,6 +116,10 @@ object VideoInformationPatch : BytecodePatch(
VideoIdPatch.hookBackgroundPlayVideoId(videoIdMethodDescriptor)
VideoIdPatch.hookPlayerResponseVideoId(
"$INTEGRATIONS_CLASS_DESCRIPTOR->setPlayerResponseVideoId(Ljava/lang/String;Z)V")
// Call before any other video id hooks,
// so they can use VideoInformation and check if the video id is for a Short.
PlayerResponseMethodHookPatch += PlayerResponseMethodHookPatch.Hook.ProtoBufferParameterBeforeVideoId(
"$INTEGRATIONS_CLASS_DESCRIPTOR->newPlayerResponseSignature(Ljava/lang/String;Z)Ljava/lang/String;")
/*
* Set the video time method

View File

@@ -19,7 +19,7 @@ object PlayerResponseMethodHookPatch :
Closeable,
MutableSet<PlayerResponseMethodHookPatch.Hook> by mutableSetOf() {
private const val VIDEO_ID_PARAMETER = 1
private const val VIDEO_IS_OPENING_OR_PLAYING_PARAMETER = 11
private const val IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER = 11
private const val PROTO_BUFFER_PARAMETER_PARAMETER = 3
private lateinit var playerResponseMethod: MutableMethod
@@ -31,13 +31,13 @@ object PlayerResponseMethodHookPatch :
override fun close() {
fun hookVideoId(hook: Hook) = playerResponseMethod.addInstruction(
0, "invoke-static {p$VIDEO_ID_PARAMETER, p$VIDEO_IS_OPENING_OR_PLAYING_PARAMETER}, $hook"
0, "invoke-static {p$VIDEO_ID_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook"
)
fun hookProtoBufferParameter(hook: Hook) = playerResponseMethod.addInstructions(
0,
"""
invoke-static {p$PROTO_BUFFER_PARAMETER_PARAMETER}, $hook
invoke-static {p$PROTO_BUFFER_PARAMETER_PARAMETER, p$IS_SHORT_AND_OPENING_OR_PLAYING_PARAMETER}, $hook
move-result-object p$PROTO_BUFFER_PARAMETER_PARAMETER
"""
)

View File

@@ -26,7 +26,7 @@ import com.android.tools.smali.dexlib2.iface.reference.FieldReference
@Patch(
name = "Remember video quality",
description = "Adds the ability to remember the video quality you chose in the video quality flyout.",
description = "Adds the ability to remember the last video quality selected.",
dependencies = [IntegrationsPatch::class, VideoInformationPatch::class, SettingsPatch::class],
compatiblePackages = [
CompatiblePackage(

View File

@@ -9,8 +9,7 @@ import app.revanced.patches.youtube.video.speed.remember.RememberPlaybackSpeedPa
@Patch(
name = "Playback speed",
description = "Adds custom playback speeds and ability " +
"to remember the playback speed you chose in the video playback speed flyout.",
description = "Adds custom playback speeds and ability to remember the last playback speed selected.",
dependencies = [CustomPlaybackSpeedPatch::class, RememberPlaybackSpeedPatch::class],
compatiblePackages = [
CompatiblePackage(

View File

@@ -19,7 +19,6 @@ import app.revanced.patches.youtube.video.speed.remember.fingerprint.InitializeP
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
@Patch(
description = "Adds the ability to remember the playback speed you chose in the playback speed flyout.",
dependencies = [IntegrationsPatch::class, SettingsPatch::class, VideoInformationPatch::class, CustomPlaybackSpeedPatch::class]
)
object RememberPlaybackSpeedPatch : BytecodePatch(

View File

@@ -104,7 +104,7 @@ object VideoIdPatch : BytecodePatch(
* Supports all videos and functions in all situations.
*
* First parameter is the video id.
* Second parameter is if the video is being opened or is currently playing.
* Second parameter is if the video is a Short AND it is being opened or is currently playing.
*
* Hook is always called off the main thread.
*

View File

@@ -8,6 +8,8 @@ import org.w3c.dom.Node
import java.nio.file.Files
import java.nio.file.StandardCopyOption
private val classLoader = object {}.javaClass.classLoader
/**
* Recursively traverse the DOM tree starting from the given root node.
*
@@ -45,7 +47,6 @@ fun ResourceContext.mergeStrings(host: String) {
* @param resources The resources to copy.
*/
fun ResourceContext.copyResources(sourceResourceDirectory: String, vararg resources: ResourceGroup) {
val classLoader = javaClass.classLoader
val targetResourceDirectory = this["res"]
for (resourceGroup in resources) {
@@ -77,7 +78,7 @@ fun ResourceContext.iterateXmlNodeChildren(
targetTag: String,
callback: (node: Node) -> Unit
) =
xmlEditor[javaClass.classLoader.getResourceAsStream(resource)!!].use {
xmlEditor[classLoader.getResourceAsStream(resource)!!].use {
val stringsNode = it.file.getElementsByTagName(targetTag).item(0).childNodes
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
}

View File

@@ -1,130 +0,0 @@
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

@@ -1,215 +0,0 @@
package app.revanced.util.microg
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.fingerprint.MethodFingerprint
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.util.returnEarly
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 com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
/**
* Helper class for applying bytecode patches needed for the microg-support patches.
*/
internal object MicroGBytecodeHelper {
/**
* Hook a method to check the availability of MicroG.
*
* @param fingerprint The fingerprint of the method to add the call for the notice in.
*/
fun injectNotice(fingerprint: MethodFingerprint) {
fingerprint.result!!.mutableMethod.addInstruction(
0,
"invoke-static {}, Lapp/revanced/integrations/patches/MicroGSupport;->checkAvailability()V"
)
}
/**
* 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 context The context.
* @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(
context: BytecodeContext,
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
context.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 BytecodeContext.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).mutableClass
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
)
)
)
}
}
}
}
}
}

View File

@@ -1,58 +0,0 @@
package app.revanced.util.microg
import app.revanced.patcher.data.ResourceContext
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 context Resource context.
* @param spoofedPackage The package to spoof.
* @param spoofedSignature The signature to spoof.
*/
fun addSpoofingMetadata(
context: ResourceContext,
spoofedPackage: String,
spoofedSignature: String
) {
context.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

@@ -1,63 +0,0 @@
package app.revanced.util.microg
import app.revanced.patcher.data.ResourceContext
import app.revanced.util.mergeStrings
/**
* Helper class for applying resource patches needed for the microg-support patches.
*/
internal object MicroGResourceHelper {
/**
* Add necessary strings to the strings.xml file.
*
* @param context The resource context.
* @param stringsHost The file which hosts the strings.
*/
fun addStrings(context: ResourceContext, stringsHost: String = "microg/host/values/strings.xml") =
context.mergeStrings(stringsHost)
/**
* Patch the manifest to work with MicroG.
*
* @param context The resource context.
* @param fromPackageName The original package name.
* @param toPackageName The package name to accept.
* @param toName The new name of the app.
*/
fun patchManifest(
context: ResourceContext,
fromPackageName: String,
toPackageName: String,
toName: String
) {
val manifest = context["AndroidManifest.xml"].readText()
context["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(
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
"$toPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
).replace(
"com.google.android.c2dm",
"${Constants.MICROG_VENDOR}.android.c2dm"
).replace(
"</queries>",
"<package android:name=\"${Constants.MICROG_VENDOR}.android.gms\"/></queries>"
)
)
}
}

View File

@@ -0,0 +1,9 @@
<resources>
<string name="revanced_alt_thumbnail_about_status_disabled">Showing original YouTube thumbnails</string>
<string name="revanced_alt_thumbnail_about_status_stills">Showing still video captures</string>
<string name="revanced_alt_thumbnail_about_status_dearrow">Showing DeArrow thumbnails. If a video has no DeArrow thumbnails then the original YouTube thumbnails are shown</string>
<string name="revanced_alt_thumbnail_about_status_dearrow_stills">Showing DeArrow thumbnails. If a video has no DeArrow thumbnails then still video captures are shown</string>
<string name="revanced_alt_thumbnail_dearrow_error">DeArrow temporarily not available (status code: %s)</string>
<string name="revanced_alt_thumbnail_dearrow_error_generic">DeArrow temporarily not available</string>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="gms_core_not_installed_warning">GmsCore is not installed. Please install it.</string>
<string name="gms_core_not_running_warning">GmsCore is failing to run. Please follow the "Don't kill my app" guide for GmsCore.</string>
</resources>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="microg_not_installed_warning">Vanced MicroG is not installed. Please install it.</string>
<string name="microg_not_running_warning">Vanced MicroG is failing to run. Please follow the "Don't kill my app" guide for Vanced MicroG.</string>
</resources>

View File

@@ -26,7 +26,7 @@
<string name="revanced_ryd_compact_layout_summary_on">Like button styled for minimum width</string>
<string name="revanced_ryd_compact_layout_summary_off">Like button styled for best appearance</string>
<string name="ryd_toast_on_connection_error_title">Show toast if API not available</string>
<string name="ryd_toast_on_connection_error_title">Show toast if API is not available</string>
<string name="ryd_toast_on_connection_error_summary_on">Toast shown if ReturnYouTubeDislike API is not available</string>
<string name="ryd_toast_on_connection_error_summary_off">Toast not shown if ReturnYouTubeDislike API is not available</string>