diff --git a/CHANGELOG.md b/CHANGELOG.md index b0ecf944f..47933c9ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,74 @@ +# [4.10.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.9...v4.10.0-dev.10) (2024-06-18) + + +### Bug Fixes + +* Correct invalid string name ([b84494f](https://github.com/ReVanced/revanced-patches/commit/b84494f4e26e040ada69ed7a516f331f2d47da87)) + +# [4.10.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.8...v4.10.0-dev.9) (2024-06-17) + + +### Bug Fixes + +* **YouTube - Hide description components:** Replace `Hide game section` and `Hide music section` with `Hide attributes section` ([#3327](https://github.com/ReVanced/revanced-patches/issues/3327)) ([0198a43](https://github.com/ReVanced/revanced-patches/commit/0198a436f97b127a2a5dd283644254f9a0ae3e43)) + +# [4.10.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.7...v4.10.0-dev.8) (2024-06-17) + + +### Features + +* **YouTube - Comments:** Add `Hide 'Create a Short' button` option ([#3333](https://github.com/ReVanced/revanced-patches/issues/3333)) ([be9e244](https://github.com/ReVanced/revanced-patches/commit/be9e24420fda80903e44e2e2278ea4904ecac4e1)) + +# [4.10.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.6...v4.10.0-dev.7) (2024-06-15) + + +### Features + +* Add `Change version code` patch ([#3338](https://github.com/ReVanced/revanced-patches/issues/3338)) ([685ef39](https://github.com/ReVanced/revanced-patches/commit/685ef39119daf1033a83262982519531c481c40f)) + +# [4.10.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.5...v4.10.0-dev.6) (2024-06-09) + + +### Features + +* **YouTube - Comments:** Add `Hide Thanks button` and `Hide 'Comments by members' header` options ([#3317](https://github.com/ReVanced/revanced-patches/issues/3317)) ([9c4c4f0](https://github.com/ReVanced/revanced-patches/commit/9c4c4f05a762d745404101bbc3925ab4eba2deb8)) + +# [4.10.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.4...v4.10.0-dev.5) (2024-06-09) + + +### Bug Fixes + +* **YouTube - Client spoof:** Correctly play more livestreams using Android VR ([#3316](https://github.com/ReVanced/revanced-patches/issues/3316)) ([c05264a](https://github.com/ReVanced/revanced-patches/commit/c05264af3944cbfe8d9aa34fb0e0fddb05a1d42f)) + +# [4.10.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.3...v4.10.0-dev.4) (2024-06-08) + + +### Features + +* **Boost for Reddit:** Add `Fix audio missing in video downloads` patch ([#3287](https://github.com/ReVanced/revanced-patches/issues/3287)) ([a9258d4](https://github.com/ReVanced/revanced-patches/commit/a9258d48d3ddf8552ab56219677a3b31ee553666)) + +# [4.10.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.2...v4.10.0-dev.3) (2024-06-08) + + +### Features + +* **Boost For Reddit:** Add `Fix /s/ links` patch ([#3154](https://github.com/ReVanced/revanced-patches/issues/3154)) ([5fa9fd2](https://github.com/ReVanced/revanced-patches/commit/5fa9fd2dfef43838d7311a967a3e805256a5d116)) + +# [4.10.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.10.0-dev.1...v4.10.0-dev.2) (2024-06-08) + + +### Bug Fixes + +* **YouTube Music:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3315](https://github.com/ReVanced/revanced-patches/issues/3315)) ([3c31e55](https://github.com/ReVanced/revanced-patches/commit/3c31e55b13d9495e857f068f8cd2b4320112d763)) +* **YouTube:** Rename `Minimized playback` to `Remove background playback restrictions` ([#3314](https://github.com/ReVanced/revanced-patches/issues/3314)) ([37d415b](https://github.com/ReVanced/revanced-patches/commit/37d415b53af4771d9c97a8b1c153be32bf3ac2e0)) + +# [4.10.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.9.0...v4.10.0-dev.1) (2024-06-07) + + +### Features + +* **YouTube - Miniplayer:** Rename `Tablet mini player` and allow selecting the style of the in-app miniplayer ([#3302](https://github.com/ReVanced/revanced-patches/issues/3302)) ([5511736](https://github.com/ReVanced/revanced-patches/commit/5511736b0c5dd409db6a68db0f85e389bb95be47)) + # [4.9.0](https://github.com/ReVanced/revanced-patches/compare/v4.8.3...v4.9.0) (2024-06-02) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ef79cf77f..087ef8221 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -115,7 +115,7 @@ with the maintainers of ReVanced Patches. This will help you determine whether y and whether it is worth your time to implement it 2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev` 3. Commit your changes. In case you are contributing a new patch, make sure to follow the conventions for patches -described in the [documentation](https://github.com/ReVanced/revanced-patches/tree/docs/docs) +described in the [ReVanced Patcher documentation](https://github.com/ReVanced/revanced-patcher/tree/main/docs) 4. Submit a pull request to the `dev` branch of the repository and reference issues that your pull request closes in the description of your pull request 5. Our team will review your pull request and provide feedback. Once your pull request is approved, diff --git a/api/revanced-patches.api b/api/revanced-patches.api index 75d89226c..2eb9a62f3 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -105,6 +105,12 @@ public final class app/revanced/patches/all/misc/transformation/IMethodCall$Defa public static fun replaceInvokeVirtualWithIntegrations (Lapp/revanced/patches/all/misc/transformation/IMethodCall;Ljava/lang/String;Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Lcom/android/tools/smali/dexlib2/iface/instruction/formats/Instruction35c;I)V } +public final class app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch : app/revanced/patcher/patch/ResourcePatch { + public static final field INSTANCE Lapp/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch; + 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/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch : app/revanced/patches/all/misc/transformation/BaseTransformInstructionsPatch { public static final field INSTANCE Lapp/revanced/patches/all/screencapture/removerestriction/RemoveCaptureRestrictionPatch; public synthetic fun filterMap (Lcom/android/tools/smali/dexlib2/iface/ClassDef;Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/iface/instruction/Instruction;I)Ljava/lang/Object; @@ -403,6 +409,12 @@ 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/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch; + 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; } @@ -529,6 +541,18 @@ public final class app/revanced/patches/reddit/ad/general/HideAdsPatch : app/rev public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public abstract class app/revanced/patches/reddit/customclients/BaseFixSLinksPatch : app/revanced/patcher/patch/BytecodePatch { + public fun (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/fingerprint/MethodFingerprint;Ljava/util/Set;Ljava/util/Set;)V + public synthetic fun (Lapp/revanced/patcher/fingerprint/MethodFingerprint;Lapp/revanced/patcher/fingerprint/MethodFingerprint;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 + protected abstract fun getIntegrationsClassDescriptor ()Ljava/lang/String; + protected final fun getResolveSLinkMethod ()Ljava/lang/String; + protected final fun getSetAccessTokenMethod ()Ljava/lang/String; + protected abstract fun patchNavigationHandler (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;Lapp/revanced/patcher/data/BytecodeContext;)V + protected abstract fun patchSetAccessToken (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;Lapp/revanced/patcher/data/BytecodeContext;)V +} + public abstract class app/revanced/patches/reddit/customclients/BaseSpoofClientPatch : app/revanced/patcher/patch/BytecodePatch { public fun (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V public synthetic fun (Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -564,6 +588,20 @@ public final class app/revanced/patches/reddit/customclients/boostforreddit/api/ public fun patchUserAgent (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V } +public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch; + 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/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch : app/revanced/patches/reddit/customclients/BaseFixSLinksPatch { + public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch; +} + +public final class app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { + public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch; +} + public final class app/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch : app/revanced/patches/reddit/customclients/BaseSpoofClientPatch { public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/infinityforreddit/api/SpoofClientPatch; public fun patchClientId (Ljava/util/Set;Lapp/revanced/patcher/data/BytecodeContext;)V @@ -642,10 +680,12 @@ public final class app/revanced/patches/reddit/customclients/syncforreddit/detec public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } -public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch : app/revanced/patcher/patch/BytecodePatch { +public final class app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch : app/revanced/patches/reddit/customclients/BaseFixSLinksPatch { public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch; - 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/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch : app/revanced/patches/shared/misc/integrations/BaseIntegrationsPatch { + public static final field INSTANCE Lapp/revanced/patches/reddit/customclients/syncforreddit/misc/integrations/IntegrationsPatch; } public final class app/revanced/patches/reddit/layout/disablescreenshotpopup/DisableScreenshotPopupPatch : app/revanced/patcher/patch/BytecodePatch { @@ -1528,6 +1568,12 @@ public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPa public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public final class app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch; + 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/layout/panels/popup/PlayerPopupPanelsPatch : app/revanced/patcher/patch/BytecodePatch { public static final field INSTANCE Lapp/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch; public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V @@ -1632,6 +1678,12 @@ public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatch public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public final class app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch; + 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/debugging/DebuggingPatch : app/revanced/patcher/patch/ResourcePatch { public static final field INSTANCE Lapp/revanced/patches/youtube/misc/debugging/DebuggingPatch; public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V @@ -1894,6 +1946,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat public final class app/revanced/util/BytecodeUtilsKt { public static final fun containsWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)Z public static final fun findMutableMethodOf (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableClass;Lcom/android/tools/smali/dexlib2/iface/Method;)Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod; + public static final fun findOpcodeIndicesReversed (Lcom/android/tools/smali/dexlib2/iface/Method;Lcom/android/tools/smali/dexlib2/Opcode;)Ljava/util/List; public static final fun getException (Lapp/revanced/patcher/fingerprint/MethodFingerprint;)Lapp/revanced/patcher/patch/PatchException; public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I @@ -1901,6 +1954,7 @@ public final class app/revanced/util/BytecodeUtilsKt { public static final fun indexOfFirstInstructionOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;)I public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I + public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V diff --git a/gradle.properties b/gradle.properties index 592a61479..cb77af6da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ org.gradle.parallel = true org.gradle.caching = true kotlin.code.style = official -version = 4.9.0 +version = 4.10.0-dev.10 diff --git a/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt b/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt new file mode 100644 index 000000000..5170332db --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/all/misc/versioncode/ChangeVersionCodePatch.kt @@ -0,0 +1,39 @@ +package app.revanced.patches.all.misc.versioncode + +import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.ResourcePatch +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.intPatchOption +import app.revanced.util.getNode +import org.w3c.dom.Element + +@Patch( + name = "Change version code", + description = "Changes the version code of the app. By default the highest version code is set. " + + "This allows older versions of an app to be installed " + + "if their version code is set to the same or a higher value and can stop app stores to update the app.", + use = false, +) +@Suppress("unused") +object ChangeVersionCodePatch : ResourcePatch() { + private val versionCode by intPatchOption( + key = "versionCode", + default = Int.MAX_VALUE, + values = mapOf( + "Lowest" to 1, + "Highest" to Int.MAX_VALUE, + ), + title = "Version code", + description = "The version code to use", + required = true, + ) { + it!! >= 1 + } + + override fun execute(context: ResourceContext) { + context.document["AndroidManifest.xml"].use { document -> + val manifestElement = document.getNode("manifest") as Element + manifestElement.setAttribute("android:versionCode", "$versionCode") + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch.kt b/src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch.kt index 918e08f2f..8d497d9b1 100644 --- a/src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch.kt +++ b/src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/MinimizedPlaybackPatch.kt @@ -1,50 +1,13 @@ package app.revanced.patches.music.layout.minimizedplayback import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.music.layout.minimizedplayback.fingerprints.BackgroundPlaybackDisableFingerprint -import app.revanced.patches.music.layout.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint -import app.revanced.util.exception +import app.revanced.patches.music.misc.backgroundplayback.BackgroundPlaybackPatch -@Patch( - name = "Minimized playback", - description = "Unlocks options for picture-in-picture and background playback.", - compatiblePackages = [ - CompatiblePackage( - "com.google.android.apps.youtube.music", - [ - "6.45.54", - "6.51.53", - "7.01.53", - "7.02.52", - "7.03.52", - ] - ) - ] -) -@Suppress("unused") +@Deprecated("This patch has been merged into BackgroundPlaybackPatch.") object MinimizedPlaybackPatch : BytecodePatch( - setOf( - KidsMinimizedPlaybackPolicyControllerFingerprint, - BackgroundPlaybackDisableFingerprint, - ), + dependencies = setOf(BackgroundPlaybackPatch::class), ) { override fun execute(context: BytecodeContext) { - KidsMinimizedPlaybackPolicyControllerFingerprint.result?.mutableMethod?.addInstruction( - 0, - "return-void", - ) ?: throw KidsMinimizedPlaybackPolicyControllerFingerprint.exception - - BackgroundPlaybackDisableFingerprint.result?.mutableMethod?.addInstructions( - 0, - """ - const/4 v0, 0x1 - return v0 - """, - ) ?: throw BackgroundPlaybackDisableFingerprint.exception } -} +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt b/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt new file mode 100644 index 000000000..51608bc5d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/BackgroundPlaybackPatch.kt @@ -0,0 +1,50 @@ +package app.revanced.patches.music.misc.backgroundplayback + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.music.misc.backgroundplayback.fingerprints.BackgroundPlaybackDisableFingerprint +import app.revanced.patches.music.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint +import app.revanced.util.resultOrThrow + +@Patch( + name = "Remove background playback restrictions", + description = "Removes restrictions on background playback.", + compatiblePackages = [ + CompatiblePackage( + "com.google.android.apps.youtube.music", + [ + "6.45.54", + "6.51.53", + "7.01.53", + "7.02.52", + "7.03.52", + ] + ) + ] +) +@Suppress("unused") +object BackgroundPlaybackPatch : BytecodePatch( + setOf( + KidsBackgroundPlaybackPolicyControllerFingerprint, + BackgroundPlaybackDisableFingerprint, + ), +) { + override fun execute(context: BytecodeContext) { + KidsBackgroundPlaybackPolicyControllerFingerprint.resultOrThrow().mutableMethod.addInstruction( + 0, + "return-void", + ) + + BackgroundPlaybackDisableFingerprint.resultOrThrow().mutableMethod.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """, + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/fingerprints/BackgroundPlaybackDisableFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/fingerprints/BackgroundPlaybackDisableFingerprint.kt similarity index 88% rename from src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/fingerprints/BackgroundPlaybackDisableFingerprint.kt rename to src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/fingerprints/BackgroundPlaybackDisableFingerprint.kt index d13410287..bf5f2119a 100644 --- a/src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/fingerprints/BackgroundPlaybackDisableFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/fingerprints/BackgroundPlaybackDisableFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.music.layout.minimizedplayback.fingerprints +package app.revanced.patches.music.misc.backgroundplayback.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/fingerprints/KidsMinimizedPlaybackPolicyControllerFingerprint.kt b/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/fingerprints/KidsBackgroundPlaybackPolicyControllerFingerprint.kt similarity index 78% rename from src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/fingerprints/KidsMinimizedPlaybackPolicyControllerFingerprint.kt rename to src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/fingerprints/KidsBackgroundPlaybackPolicyControllerFingerprint.kt index da6823c8a..45374d401 100644 --- a/src/main/kotlin/app/revanced/patches/music/layout/minimizedplayback/fingerprints/KidsMinimizedPlaybackPolicyControllerFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/music/misc/backgroundplayback/fingerprints/KidsBackgroundPlaybackPolicyControllerFingerprint.kt @@ -1,11 +1,11 @@ -package app.revanced.patches.music.layout.minimizedplayback.fingerprints +package app.revanced.patches.music.misc.backgroundplayback.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerprint( +internal object KidsBackgroundPlaybackPolicyControllerFingerprint : MethodFingerprint( "V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("I", "L", "Z"), diff --git a/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt b/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt index a17903a2d..da245e892 100644 --- a/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt +++ b/src/main/kotlin/app/revanced/patches/music/premium/backgroundplay/BackgroundPlayPatch.kt @@ -2,10 +2,11 @@ package app.revanced.patches.music.premium.backgroundplay import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patches.music.layout.minimizedplayback.MinimizedPlaybackPatch -@Deprecated("This patch has been merged into MinimizedPlaybackPatch.") +import app.revanced.patches.music.misc.backgroundplayback.BackgroundPlaybackPatch + +@Deprecated("This patch has been merged into BackgroundPlaybackPatch.") object BackgroundPlayPatch : BytecodePatch( - dependencies = setOf(MinimizedPlaybackPatch::class), + dependencies = setOf(BackgroundPlaybackPatch::class), ) { override fun execute(context: BytecodeContext) { } diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/BaseFixSLinksPatch.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/BaseFixSLinksPatch.kt new file mode 100644 index 000000000..bc7b4ee3c --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/BaseFixSLinksPatch.kt @@ -0,0 +1,49 @@ +package app.revanced.patches.reddit.customclients + +import app.revanced.patcher.PatchClass +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patcher.fingerprint.MethodFingerprintResult +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.util.resultOrThrow + +abstract class BaseFixSLinksPatch( + private val handleNavigationFingerprint: MethodFingerprint, + private val setAccessTokenFingerprint: MethodFingerprint, + compatiblePackages: Set, + dependencies: Set = emptySet(), +) : BytecodePatch( + name = "Fix /s/ links", + fingerprints = setOf(handleNavigationFingerprint, setAccessTokenFingerprint), + compatiblePackages = compatiblePackages, + dependencies = dependencies, +) { + protected abstract val integrationsClassDescriptor: String + + protected val resolveSLinkMethod = + "patchResolveSLink(Ljava/lang/String;)Z" + + protected val setAccessTokenMethod = + "patchSetAccessToken(Ljava/lang/String;)V" + + override fun execute(context: BytecodeContext) { + handleNavigationFingerprint.resultOrThrow().patchNavigationHandler(context) + setAccessTokenFingerprint.resultOrThrow().patchSetAccessToken(context) + } + + /** + * Patch app's navigation handler to resolve /s/ links. + * + * @param context The current [BytecodeContext]. + * + */ + protected abstract fun MethodFingerprintResult.patchNavigationHandler(context: BytecodeContext) + + /** + * Patch access token setup in app to resolve /s/ links with an access token + * in order to bypass API bans when making unauthorized requests. + * + * @param context The current [BytecodeContext]. + */ + protected abstract fun MethodFingerprintResult.patchSetAccessToken(context: BytecodeContext) +} diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt new file mode 100644 index 000000000..103569656 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/FixAudioMissingInDownloadsPatch.kt @@ -0,0 +1,38 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads.fingerprints.DownloadAudioFingerprint +import app.revanced.util.resultOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction + +@Patch( + name = "Fix missing audio in video downloads", + description = "Fixes audio missing in videos downloaded from v.redd.it.", + compatiblePackages = [CompatiblePackage("com.rubenmayayo.reddit")], +) +@Suppress("unused") +object FixAudioMissingInDownloadsPatch : BytecodePatch( + setOf(DownloadAudioFingerprint), +) { + private val endpointReplacements = mapOf( + "/DASH_audio.mp4" to "/DASH_AUDIO_128.mp4", + "/audio" to "/DASH_AUDIO_64.mp4", + ) + override fun execute(context: BytecodeContext) { + DownloadAudioFingerprint.resultOrThrow().let { result -> + result.scanResult.stringsScanResult!!.matches.take(2).forEach { match -> + result.mutableMethod.apply { + val replacement = endpointReplacements[match.string] + val register = getInstruction(match.index).registerA + + replaceInstruction(match.index, "const-string v$register, \"$replacement\"") + } + } + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/fingerprints/DownloadAudioFingerprint.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/fingerprints/DownloadAudioFingerprint.kt new file mode 100644 index 000000000..b5261523f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/downloads/fingerprints/DownloadAudioFingerprint.kt @@ -0,0 +1,7 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.downloads.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +internal object DownloadAudioFingerprint : MethodFingerprint( + strings = setOf("/DASH_audio.mp4", "/audio", "v.redd.it", "/"), +) diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt new file mode 100644 index 000000000..b651abded --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatch.kt @@ -0,0 +1,44 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.fingerprint.MethodFingerprintResult +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.reddit.customclients.BaseFixSLinksPatch +import app.revanced.patches.reddit.customclients.boostforreddit.fix.slink.fingerprints.GetOAuthAccessTokenFingerprint +import app.revanced.patches.reddit.customclients.boostforreddit.fix.slink.fingerprints.HandleNavigationFingerprint +import app.revanced.patches.reddit.customclients.boostforreddit.misc.integrations.IntegrationsPatch + +@Suppress("unused") +object FixSLinksPatch : BaseFixSLinksPatch( + handleNavigationFingerprint = HandleNavigationFingerprint, + setAccessTokenFingerprint = GetOAuthAccessTokenFingerprint, + compatiblePackages = setOf(CompatiblePackage("com.rubenmayayo.reddit")), + dependencies = setOf(IntegrationsPatch::class), +) { + override val integrationsClassDescriptor = "Lapp/revanced/integrations/boostforreddit/FixSLinksPatch;" + + override fun MethodFingerprintResult.patchNavigationHandler(context: BytecodeContext) { + mutableMethod.apply { + val urlRegister = "p1" + val tempRegister = "v1" + addInstructionsWithLabels( + 0, + """ + invoke-static { $urlRegister }, $integrationsClassDescriptor->$resolveSLinkMethod + move-result $tempRegister + if-eqz $tempRegister, :continue + return $tempRegister + """, + ExternalLabel("continue", getInstruction(0)), + ) + } + } + + override fun MethodFingerprintResult.patchSetAccessToken(context: BytecodeContext) = mutableMethod.addInstruction( + 3, + "invoke-static { v0 }, $integrationsClassDescriptor->$setAccessTokenMethod", + ) +} diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/fingerprints/GetOAuthAccessTokenFingerprint.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/fingerprints/GetOAuthAccessTokenFingerprint.kt new file mode 100644 index 000000000..9a9103f60 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/fingerprints/GetOAuthAccessTokenFingerprint.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink.fingerprints +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object GetOAuthAccessTokenFingerprint : MethodFingerprint( + strings = listOf("access_token"), + accessFlags = AccessFlags.PUBLIC.value, + returnType = "Ljava/lang/String", + customFingerprint = { _, classDef -> classDef.type == "Lnet/dean/jraw/http/oauth/OAuthData;" }, +) diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/fingerprints/HandleNavigationFingerprint.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/fingerprints/HandleNavigationFingerprint.kt new file mode 100644 index 000000000..2a7120a63 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/fingerprints/HandleNavigationFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.fix.slink.fingerprints +import app.revanced.patcher.fingerprint.MethodFingerprint + +internal object HandleNavigationFingerprint : MethodFingerprint( + strings = listOf( + "android.intent.action.SEARCH", + "subscription", + "sort", + "period", + "boostforreddit.com/themes", + ), +) diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch.kt new file mode 100644 index 000000000..c0909a7af --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/IntegrationsPatch.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.misc.integrations + +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch +import app.revanced.patches.reddit.customclients.boostforreddit.misc.integrations.fingerprints.InitFingerprint + +@Patch(requiresIntegrations = true) +object IntegrationsPatch : BaseIntegrationsPatch( + setOf(InitFingerprint) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/fingerprints/InitFingerprint.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/fingerprints/InitFingerprint.kt new file mode 100644 index 000000000..ae28bc63f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/boostforreddit/misc/integrations/fingerprints/InitFingerprint.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.reddit.customclients.boostforreddit.misc.integrations.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object InitFingerprint : IntegrationsFingerprint( + customFingerprint = { methodDef, _ -> methodDef.definingClass == "Lcom/rubenmayayo/reddit/MyApplication;" && methodDef.name == "onCreate" }, + insertIndexResolver = { 1 } // Insert after call to super class. +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch.kt index 09fa4e370..26c875827 100644 --- a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch.kt +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/FixSLinksPatch.kt @@ -1,32 +1,49 @@ package app.revanced.patches.reddit.customclients.syncforreddit.fix.slink import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotation.CompatiblePackage -import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.fingerprint.MethodFingerprintResult +import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.reddit.customclients.BaseFixSLinksPatch import app.revanced.patches.reddit.customclients.syncforreddit.fix.slink.fingerprints.LinkHelperOpenLinkFingerprint -import app.revanced.util.exception +import app.revanced.patches.reddit.customclients.syncforreddit.fix.slink.fingerprints.SetAuthorizationHeaderFingerprint +import app.revanced.patches.reddit.customclients.syncforreddit.misc.integrations.IntegrationsPatch -@Patch( - name = "Fix /s/ links", - description = "Fixes the issue where /s/ links do not work.", - compatiblePackages = [ +@Suppress("unused") +object FixSLinksPatch : BaseFixSLinksPatch( + handleNavigationFingerprint = LinkHelperOpenLinkFingerprint, + setAccessTokenFingerprint = SetAuthorizationHeaderFingerprint, + compatiblePackages = setOf( CompatiblePackage("com.laurencedawson.reddit_sync"), CompatiblePackage("com.laurencedawson.reddit_sync.pro"), - CompatiblePackage("com.laurencedawson.reddit_sync.dev") - ], - requiresIntegrations = true -) -object FixSLinksPatch : BytecodePatch( - setOf(LinkHelperOpenLinkFingerprint) + CompatiblePackage("com.laurencedawson.reddit_sync.dev"), + ), + dependencies = setOf(IntegrationsPatch::class), ) { - override fun execute(context: BytecodeContext) = - LinkHelperOpenLinkFingerprint.result?.mutableMethod?.addInstructions( - 1, - """ - invoke-static { p3 }, Lapp/revanced/integrations/syncforreddit/FixSLinksPatch;->resolveSLink(Ljava/lang/String;)Ljava/lang/String; - move-result-object p3 - """ - ) ?: throw LinkHelperOpenLinkFingerprint.exception + override val integrationsClassDescriptor = "Lapp/revanced/integrations/syncforreddit/FixSLinksPatch;" + + override fun MethodFingerprintResult.patchNavigationHandler(context: BytecodeContext) { + mutableMethod.apply { + val urlRegister = "p3" + val tempRegister = "v2" + + addInstructionsWithLabels( + 0, + """ + invoke-static { $urlRegister }, $integrationsClassDescriptor->$resolveSLinkMethod + move-result $tempRegister + if-eqz $tempRegister, :continue + return $tempRegister + """, + ExternalLabel("continue", getInstruction(0)), + ) + } + } + + override fun MethodFingerprintResult.patchSetAccessToken(context: BytecodeContext) = mutableMethod.addInstruction( + 0, + "invoke-static { p0 }, $integrationsClassDescriptor->$setAccessTokenMethod", + ) } diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/fingerprints/SetAuthorizationHeaderFingerprint.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/fingerprints/SetAuthorizationHeaderFingerprint.kt new file mode 100644 index 000000000..217ee8598 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/fix/slink/fingerprints/SetAuthorizationHeaderFingerprint.kt @@ -0,0 +1,9 @@ +package app.revanced.patches.reddit.customclients.syncforreddit.fix.slink.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +internal object SetAuthorizationHeaderFingerprint : MethodFingerprint( + strings = listOf("Authorization", "bearer "), + returnType = "Ljava/util/HashMap;", + customFingerprint = { methodDef, _ -> methodDef.definingClass == "Lcom/laurencedawson/reddit_sync/singleton/a;" }, +) diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/misc.integrations/IntegrationsPatch.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/misc.integrations/IntegrationsPatch.kt new file mode 100644 index 000000000..bf5ebc32f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/misc.integrations/IntegrationsPatch.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.reddit.customclients.syncforreddit.misc.integrations + +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch +import app.revanced.patches.reddit.customclients.syncforreddit.misc.integrations.fingerprints.InitFingerprint + +@Patch(requiresIntegrations = true) +object IntegrationsPatch : BaseIntegrationsPatch( + setOf(InitFingerprint) +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/misc.integrations/fingerprints/InitFingerprint.kt b/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/misc.integrations/fingerprints/InitFingerprint.kt new file mode 100644 index 000000000..950f2cbe2 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/reddit/customclients/syncforreddit/misc.integrations/fingerprints/InitFingerprint.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.reddit.customclients.syncforreddit.misc.integrations.fingerprints + +import app.revanced.patches.shared.misc.integrations.BaseIntegrationsPatch.IntegrationsFingerprint + +internal object InitFingerprint : IntegrationsFingerprint( + customFingerprint = { methodDef, classDef -> + methodDef.name == "onCreate" && classDef.type == "Lcom/laurencedawson/reddit_sync/RedditApplication;" + }, + insertIndexResolver = { 1 }, // Insert after call to super class. +) diff --git a/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt b/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt index 388c4ef61..2c165e1f1 100644 --- a/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt +++ b/src/main/kotlin/app/revanced/patches/shared/misc/mapping/ResourceMappingPatch.kt @@ -1,6 +1,7 @@ package app.revanced.patches.shared.misc.mapping import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.ResourcePatch import org.w3c.dom.Element import java.util.* @@ -51,9 +52,10 @@ object ResourceMappingPatch : ResourcePatch() { threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS) } - operator fun get(type: String, name: String) = resourceMappings.first { - it.type == type && it.name == name - }.id + operator fun get(type: String, name: String) = + resourceMappings.firstOrNull { + it.type == type && it.name == name + }?.id ?: throw PatchException("Could not find resource type: $type name: $name") data class ResourceElement(val type: String, val name: String, val id: Long) } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt index 30a77742b..51280ac18 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/comments/CommentsPatch.kt @@ -62,9 +62,12 @@ object CommentsPatch : ResourcePatch() { PreferenceScreen( "revanced_comments_screen", preferences = setOf( - SwitchPreference("revanced_hide_preview_comment"), + SwitchPreference("revanced_hide_comments_by_members_header"), SwitchPreference("revanced_hide_comments_section"), - SwitchPreference("revanced_hide_comment_timestamp_and_emoji_buttons") + SwitchPreference("revanced_hide_comments_create_a_short_button"), + SwitchPreference("revanced_hide_comments_preview_comment"), + SwitchPreference("revanced_hide_comments_thanks_button"), + SwitchPreference("revanced_hide_comments_timestamp_and_emoji_buttons") ), sorting = PreferenceScreen.Sorting.UNSORTED ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index b1d8dbf23..acd4d1055 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -94,10 +94,9 @@ object HideLayoutComponentsPatch : BytecodePatch( PreferenceScreen( key = "revanced_hide_description_components_screen", preferences = setOf( - SwitchPreference("revanced_hide_chapters"), + SwitchPreference("revanced_hide_attributes_section"), + SwitchPreference("revanced_hide_chapters_section"), SwitchPreference("revanced_hide_info_cards_section"), - SwitchPreference("revanced_hide_game_section"), - SwitchPreference("revanced_hide_music_section"), SwitchPreference("revanced_hide_podcast_section"), SwitchPreference("revanced_hide_transcript_section"), ), diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt new file mode 100644 index 000000000..4d7c52a48 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt @@ -0,0 +1,370 @@ +package app.revanced.patches.youtube.layout.miniplayer + +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.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +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.all.misc.resources.AddResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.shared.misc.settings.preference.TextPreference +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerClose +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerExpand +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerForwardButton +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.modernMiniplayerRewindButton +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.scrimOverlay +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24 +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch.ytOutlineXWhite24 +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerDimensionsCalculatorParentFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernAddViewListenerFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernCloseButtonFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernConstructorFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernExpandButtonFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernExpandCloseDrawablesFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernForwardButtonFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernOverlayViewFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernRewindButtonFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerModernViewParentFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerOverrideFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerOverrideNoContextFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.MiniplayerResponseModelSizeCheckFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint.YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME +import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint +import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.misc.settings.SettingsPatch +import app.revanced.util.findOpcodeIndicesReversed +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow +import app.revanced.util.patch.LiteralValueFingerprint +import app.revanced.util.resultOrThrow +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.Method +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.TypeReference +import com.android.tools.smali.dexlib2.immutable.ImmutableMethod +import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter + +// YT uses "Miniplayer" without a space between 'mini' and 'player: https://support.google.com/youtube/answer/9162927. +@Patch( + name = "Miniplayer", + description = "Adds options to change the in app minimized player, " + + "and if patching target 19.16+ adds options to use modern miniplayers.", + dependencies = [ + IntegrationsPatch::class, + SettingsPatch::class, + AddResourcesPatch::class, + MiniplayerResourcePatch::class + ], + compatiblePackages = [ + CompatiblePackage( + "com.google.android.youtube", [ + "18.32.39", + "18.37.36", + "18.38.44", + "18.43.45", + "18.44.41", + "18.45.43", + "18.48.39", + "18.49.37", + "19.01.34", + "19.02.39", + "19.03.36", + "19.04.38", + "19.05.36", + "19.06.39", + "19.07.40", + "19.08.36", + "19.09.38", + "19.10.39", + "19.11.43", + "19.12.41", + "19.13.37", + // 19.14 is left out, as it has incomplete miniplayer code and missing some UI resources. + // It's simpler to not bother with supporting this single old version. + // 19.15 has a different code for handling sub title texts, + // and also probably not worth making changes just to support this single old version. + "19.16.39" // Earliest supported version with modern miniplayers. + ] + ) + ] +) +@Suppress("unused") +object MiniplayerPatch : BytecodePatch( + setOf(GetFormFactorFingerprint, + MiniplayerDimensionsCalculatorParentFingerprint, + MiniplayerResponseModelSizeCheckFingerprint, + MiniplayerOverrideFingerprint, + MiniplayerModernConstructorFingerprint, + MiniplayerModernViewParentFingerprint, + YouTubePlayerOverlaysLayoutFingerprint + ) +) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/MiniplayerPatch;" + + override fun execute(context: BytecodeContext) { + AddResourcesPatch(this::class) + + // Modern mini player is only present and functional in 19.15+. + // Resource is not present in older versions. Using it to determine, if patching an old version. + val isPatchingOldVersion = ytOutlinePictureInPictureWhite24 < 0 + + SettingsPatch.PreferenceScreen.PLAYER.addPreferences( + PreferenceScreen( + key = "revanced_miniplayer_screen", + sorting = Sorting.UNSORTED, + preferences = + if (isPatchingOldVersion) { + setOf( + ListPreference( + "revanced_miniplayer_type", + summaryKey = null, + entriesKey = "revanced_miniplayer_type_legacy_entries", + entryValuesKey = "revanced_miniplayer_type_legacy_entry_values" + ) + ) + } else { + setOf( + ListPreference( + "revanced_miniplayer_type", + summaryKey = null, + entriesKey = "revanced_miniplayer_type_19_15_entries", + entryValuesKey = "revanced_miniplayer_type_19_15_entry_values" + ), + SwitchPreference("revanced_miniplayer_hide_expand_close"), + SwitchPreference("revanced_miniplayer_hide_subtext"), + SwitchPreference("revanced_miniplayer_hide_rewind_forward"), + TextPreference("revanced_miniplayer_opacity", inputType = InputType.NUMBER) + ) + } + ) + ) + + // region Enable tablet miniplayer. + + MiniplayerOverrideNoContextFingerprint.resolve( + context, + MiniplayerDimensionsCalculatorParentFingerprint.resultOrThrow().classDef + ) + MiniplayerOverrideNoContextFingerprint.resultOrThrow().mutableMethod.apply { + findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) } + } + + // endregion + + // region Legacy tablet Miniplayer hooks. + + MiniplayerOverrideFingerprint.resultOrThrow().let { + val appNameStringIndex = it.scanResult.stringsScanResult!!.matches.first().index + 2 + + it.mutableMethod.apply { + val walkerMethod = context.toMethodWalker(this) + .nextMethod(appNameStringIndex, true) + .getMethod() as MutableMethod + + walkerMethod.apply { + findReturnIndicesReversed().forEach { index -> insertLegacyTabletMiniplayerOverride(index) } + } + } + } + + MiniplayerResponseModelSizeCheckFingerprint.resultOrThrow().let { + it.mutableMethod.insertLegacyTabletMiniplayerOverride(it.scanResult.patternScanResult!!.endIndex) + } + + if (isPatchingOldVersion) { + // Return here, as patch below is only intended for new versions of the app. + return + } + + // endregion + + + // region Enable modern miniplayer. + + MiniplayerModernConstructorFingerprint.resultOrThrow().mutableClass.methods.forEach { + it.apply { + if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) { + val iPutIndex = indexOfFirstInstructionOrThrow { + this.opcode == Opcode.IPUT && this.getReference()?.type == "I" + } + + insertModernMiniplayerTypeOverride(iPutIndex) + } else { + findReturnIndicesReversed().forEach { index -> insertModernMiniplayerOverride(index) } + } + } + } + + // endregion + + // region Fix 19.16 using mixed up drawables for tablet modern. + // YT fixed this mistake in 19.17. + // Fix this, by swapping the drawable resource values with each other. + + MiniplayerModernExpandCloseDrawablesFingerprint.apply { + resolve( + context, + MiniplayerModernViewParentFingerprint.resultOrThrow().classDef + ) + }.resultOrThrow().mutableMethod.apply { + listOf( + ytOutlinePictureInPictureWhite24 to ytOutlineXWhite24, + ytOutlineXWhite24 to ytOutlinePictureInPictureWhite24, + ).forEach { (originalResource, replacementResource) -> + val imageResourceIndex = indexOfFirstWideLiteralInstructionValueOrThrow(originalResource) + val register = getInstruction(imageResourceIndex).registerA + + replaceInstruction(imageResourceIndex, "const v$register, $replacementResource") + } + } + + // endregion + + + // region Add hooks to hide tablet modern miniplayer buttons. + + listOf( + Triple(MiniplayerModernExpandButtonFingerprint, modernMiniplayerExpand,"hideMiniplayerExpandClose"), + Triple(MiniplayerModernCloseButtonFingerprint, modernMiniplayerClose, "hideMiniplayerExpandClose"), + Triple(MiniplayerModernRewindButtonFingerprint, modernMiniplayerRewindButton, "hideMiniplayerRewindForward"), + Triple(MiniplayerModernForwardButtonFingerprint, modernMiniplayerForwardButton, "hideMiniplayerRewindForward"), + Triple(MiniplayerModernOverlayViewFingerprint, scrimOverlay, "adjustMiniplayerOpacity") + ).forEach { (fingerprint, literalValue, methodName) -> + fingerprint.resolve( + context, + MiniplayerModernViewParentFingerprint.resultOrThrow().classDef + ) + + fingerprint.hookInflatedView( + literalValue, + "Landroid/widget/ImageView;", + "$INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V" + ) + } + + MiniplayerModernAddViewListenerFingerprint.apply { + resolve( + context, + MiniplayerModernViewParentFingerprint.resultOrThrow().classDef + ) + }.resultOrThrow().mutableMethod.addInstruction( + 0, + "invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->" + + "hideMiniplayerSubTexts(Landroid/view/View;)V" + ) + + + // Modern 2 has a broken overlay subtitle view that is always present. + // Modern 2 uses the same overlay controls as the regular video player, + // and the overlay views are added at runtime. + // Add a hook to the overlay class, and pass the added views to integrations. + YouTubePlayerOverlaysLayoutFingerprint.resultOrThrow().mutableClass.methods.add( + ImmutableMethod( + YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME, + "addView", + listOf( + ImmutableMethodParameter("Landroid/view/View;", null, null), + ImmutableMethodParameter("I", null, null), + ImmutableMethodParameter("Landroid/view/ViewGroup\$LayoutParams;", null, null), + ), + "V", + AccessFlags.PUBLIC.value, + null, + null, + MutableMethodImplementation(4), + ).toMutable().apply { + addInstructions( + """ + invoke-super { p0, p1, p2, p3 }, Landroid/view/ViewGroup;->addView(Landroid/view/View;ILandroid/view/ViewGroup${'$'}LayoutParams;)V + invoke-static { p1 }, $INTEGRATIONS_CLASS_DESCRIPTOR->playerOverlayGroupCreated(Landroid/view/View;)V + return-void + """, + ) + } + ) + + // endregion + } + + private fun Method.findReturnIndicesReversed() = findOpcodeIndicesReversed(Opcode.RETURN) + + /** + * Adds an override to force legacy tablet miniplayer to be used or not used. + */ + private fun MutableMethod.insertLegacyTabletMiniplayerOverride(index: Int) { + insertBooleanOverride(index, "getLegacyTabletMiniplayerOverride") + } + + /** + * Adds an override to force modern miniplayer to be used or not used. + */ + private fun MutableMethod.insertModernMiniplayerOverride(index: Int) { + insertBooleanOverride(index, "getModernMiniplayerOverride") + } + + private fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) { + val register = getInstruction(index).registerA + addInstructions( + index, + """ + invoke-static {v$register}, $INTEGRATIONS_CLASS_DESCRIPTOR->$methodName(Z)Z + move-result v$register + """ + ) + } + + /** + * Adds an override to specify which modern miniplayer is used. + */ + private fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) { + val targetInstruction = getInstruction(iPutIndex) + val targetReference = (targetInstruction as ReferenceInstruction).reference + + addInstructions( + iPutIndex + 1, """ + invoke-static { v${targetInstruction.registerA} }, $INTEGRATIONS_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I + move-result v${targetInstruction.registerA} + # Original instruction + iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference + """ + ) + removeInstruction(iPutIndex) + } + + private fun LiteralValueFingerprint.hookInflatedView( + literalValue: Long, + hookedClassType: String, + integrationsMethodName: String, + ) { + resultOrThrow().mutableMethod.apply { + val imageViewIndex = indexOfFirstInstructionOrThrow( + indexOfFirstWideLiteralInstructionValueOrThrow(literalValue) + ) { + opcode == Opcode.CHECK_CAST && getReference()?.type == hookedClassType + } + + val register = getInstruction(imageViewIndex).registerA + addInstruction( + imageViewIndex + 1, + "invoke-static { v$register }, $integrationsMethodName" + ) + } + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerResourcePatch.kt new file mode 100644 index 000000000..3870f0654 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerResourcePatch.kt @@ -0,0 +1,81 @@ +package app.revanced.patches.youtube.layout.miniplayer + +import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.ResourcePatch +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch + +@Patch(dependencies = [ResourceMappingPatch::class]) +internal object MiniplayerResourcePatch : ResourcePatch() { + var floatyBarButtonTopMargin = -1L + + // Only available in 19.15 and upwards. + var ytOutlineXWhite24 = -1L + var ytOutlinePictureInPictureWhite24 = -1L + var scrimOverlay = -1L + var modernMiniplayerClose = -1L + var modernMiniplayerExpand = -1L + var modernMiniplayerRewindButton = -1L + var modernMiniplayerForwardButton = -1L + var playerOverlays = -1L + + override fun execute(context: ResourceContext) { + floatyBarButtonTopMargin = ResourceMappingPatch[ + "dimen", + "floaty_bar_button_top_margin" + ] + + try { + ytOutlinePictureInPictureWhite24 = ResourceMappingPatch[ + "drawable", + "yt_outline_picture_in_picture_white_24" + ] + } catch (exception: PatchException) { + // Ignore, and assume the app is 19.14 or earlier. + return + } + + ytOutlineXWhite24 = ResourceMappingPatch[ + "drawable", + "yt_outline_x_white_24" + ] + + scrimOverlay = ResourceMappingPatch[ + "id", + "scrim_overlay" + ] + + modernMiniplayerClose = ResourceMappingPatch[ + "id", + "modern_miniplayer_close" + ] + + modernMiniplayerExpand = ResourceMappingPatch[ + "id", + "modern_miniplayer_expand" + ] + + modernMiniplayerRewindButton = ResourceMappingPatch[ + "id", + "modern_miniplayer_rewind_button" + ] + + modernMiniplayerForwardButton = ResourceMappingPatch[ + "id", + "modern_miniplayer_forward_button" + ] + + playerOverlays = ResourceMappingPatch[ + "layout", + "player_overlays" + ] + + // Resource id is not used during patching, but is used by integrations. + // Verify the resource is present while patching. + ResourceMappingPatch[ + "id", + "modern_miniplayer_subtitle_text" + ] + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerDimensionsCalculatorParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerDimensionsCalculatorParentFingerprint.kt new file mode 100644 index 000000000..bc156e7af --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerDimensionsCalculatorParentFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object MiniplayerDimensionsCalculatorParentFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("L"), + literalSupplier = { MiniplayerResourcePatch.floatyBarButtonTopMargin } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernAddViewListenerFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernAddViewListenerFingerprint.kt new file mode 100644 index 000000000..c4e947240 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernAddViewListenerFingerprint.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves using the class found in [MiniplayerModernViewParentFingerprint]. + */ +internal object MiniplayerModernAddViewListenerFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("Landroid/view/View;") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernCloseButtonFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernCloseButtonFingerprint.kt new file mode 100644 index 000000000..e4bbd9aad --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernCloseButtonFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves using the class found in [MiniplayerModernViewParentFingerprint]. + */ +internal object MiniplayerModernCloseButtonFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Landroid/widget/ImageView;", + parameters = listOf(), + literalSupplier = { MiniplayerResourcePatch.modernMiniplayerClose } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernConstructorFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernConstructorFingerprint.kt new file mode 100644 index 000000000..0afb5e526 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernConstructorFingerprint.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object MiniplayerModernConstructorFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, + parameters = listOf("L"), + literalSupplier = { 45623000L } // Magic number found in the constructor. +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernExpandButtonFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernExpandButtonFingerprint.kt new file mode 100644 index 000000000..35629e30d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernExpandButtonFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves using the class found in [MiniplayerModernViewParentFingerprint]. + */ +internal object MiniplayerModernExpandButtonFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Landroid/widget/ImageView;", + parameters = listOf(), + literalSupplier = { MiniplayerResourcePatch.modernMiniplayerExpand } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernExpandCloseDrawablesFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernExpandCloseDrawablesFingerprint.kt new file mode 100644 index 000000000..9ee56819e --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernExpandCloseDrawablesFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves using the class found in [MiniplayerModernViewParentFingerprint]. + */ +internal object MiniplayerModernExpandCloseDrawablesFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("L"), + literalSupplier = { MiniplayerResourcePatch.ytOutlinePictureInPictureWhite24 } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernForwardButtonFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernForwardButtonFingerprint.kt new file mode 100644 index 000000000..52053750c --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernForwardButtonFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves using the class found in [MiniplayerModernViewParentFingerprint]. + */ +internal object MiniplayerModernForwardButtonFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Landroid/widget/ImageView;", + parameters = listOf(), + literalSupplier = { MiniplayerResourcePatch.modernMiniplayerForwardButton } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernOverlayViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernOverlayViewFingerprint.kt new file mode 100644 index 000000000..02730f443 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernOverlayViewFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves using the class found in [MiniplayerModernViewParentFingerprint]. + */ +internal object MiniplayerModernOverlayViewFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", + parameters = listOf(), + literalSupplier = { MiniplayerResourcePatch.scrimOverlay } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernRewindButtonFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernRewindButtonFingerprint.kt new file mode 100644 index 000000000..71b9c054b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernRewindButtonFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves using the class found in [MiniplayerModernViewParentFingerprint]. + */ +internal object MiniplayerModernRewindButtonFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Landroid/widget/ImageView;", + parameters = listOf(), + literalSupplier = { MiniplayerResourcePatch.modernMiniplayerRewindButton } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernViewParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernViewParentFingerprint.kt new file mode 100644 index 000000000..a6baeec70 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerModernViewParentFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object MiniplayerModernViewParentFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Ljava/lang/String;", + parameters = listOf(), + strings = listOf("player_overlay_modern_mini_player_controls") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerOverrideFingerprint.kt similarity index 68% rename from src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerOverrideFingerprint.kt index 9e80c81a2..9d9bf5e15 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerOverrideFingerprint.kt @@ -1,10 +1,10 @@ -package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints +package app.revanced.patches.youtube.layout.miniplayer.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.AccessFlags -internal object MiniPlayerOverrideFingerprint : MethodFingerprint( +internal object MiniplayerOverrideFingerprint : MethodFingerprint( returnType = "L", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, parameters = listOf("L"), diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerOverrideNoContextFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerOverrideNoContextFingerprint.kt new file mode 100644 index 000000000..53900f993 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerOverrideNoContextFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal object MiniplayerOverrideNoContextFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + returnType = "Z", + opcodes = listOf(Opcode.IGET_BOOLEAN), // anchor to insert the instruction +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerResponseModelSizeCheckFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerResponseModelSizeCheckFingerprint.kt similarity index 56% rename from src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerResponseModelSizeCheckFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerResponseModelSizeCheckFingerprint.kt index 4ac508edc..b25e727cb 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerResponseModelSizeCheckFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/MiniplayerResponseModelSizeCheckFingerprint.kt @@ -1,15 +1,15 @@ -package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints +package app.revanced.patches.youtube.layout.miniplayer.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal object MiniPlayerResponseModelSizeCheckFingerprint : MethodFingerprint( - "L", - AccessFlags.PUBLIC or AccessFlags.FINAL, - listOf("Ljava/lang/Object;", "Ljava/lang/Object;"), - listOf( +internal object MiniplayerResponseModelSizeCheckFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "L", + parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"), + opcodes = listOf( Opcode.RETURN_OBJECT, Opcode.CHECK_CAST, Opcode.CHECK_CAST, diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/YouTubePlayerOverlaysLayoutFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/YouTubePlayerOverlaysLayoutFingerprint.kt new file mode 100644 index 000000000..c53a208e9 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/fingerprints/YouTubePlayerOverlaysLayoutFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.layout.miniplayer.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patches.youtube.layout.miniplayer.fingerprints.YouTubePlayerOverlaysLayoutFingerprint.YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME + +internal object YouTubePlayerOverlaysLayoutFingerprint : MethodFingerprint( + customFingerprint = { _, classDef -> + classDef.type == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME + } +) { + const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME = + "Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;" +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt index fd00bf39b..f64d7d76b 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatch.kt @@ -1,7 +1,5 @@ package app.revanced.patches.youtube.layout.player.overlay -import app.revanced.util.exception -import app.revanced.util.indexOfFirstWideLiteralInstructionValue import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction @@ -9,6 +7,8 @@ import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.annotation.CompatiblePackage import app.revanced.patcher.patch.annotation.Patch import app.revanced.patches.youtube.layout.player.overlay.fingerprints.CreatePlayerOverviewFingerprint +import app.revanced.util.exception +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @Patch( @@ -27,7 +27,7 @@ object CustomPlayerOverlayOpacityPatch : BytecodePatch(setOf(CreatePlayerOvervie CreatePlayerOverviewFingerprint.result?.let { result -> result.mutableMethod.apply { val viewRegisterIndex = - indexOfFirstWideLiteralInstructionValue(CustomPlayerOverlayOpacityResourcePatch.scrimOverlayId) + 3 + indexOfFirstWideLiteralInstructionValueOrThrow(CustomPlayerOverlayOpacityResourcePatch.scrimOverlayId) + 3 val viewRegister = getInstruction(viewRegisterIndex).registerA diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorBytecodePatch.kt index 98acf716c..08d3fa027 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorBytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorBytecodePatch.kt @@ -1,7 +1,5 @@ package app.revanced.patches.youtube.layout.seekbar -import app.revanced.util.exception -import app.revanced.util.indexOfFirstWideLiteralInstructionValue import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.getInstruction @@ -15,6 +13,8 @@ import app.revanced.patches.youtube.layout.seekbar.fingerprints.ShortsSeekbarCol import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch.lithoColorOverrideHook import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.util.exception +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -30,7 +30,7 @@ internal object SeekbarColorBytecodePatch : BytecodePatch( override fun execute(context: BytecodeContext) { fun MutableMethod.addColorChangeInstructions(resourceId: Long) { - val registerIndex = indexOfFirstWideLiteralInstructionValue(resourceId) + 2 + val registerIndex = indexOfFirstWideLiteralInstructionValueOrThrow(resourceId) + 2 val colorRegister = getInstruction(registerIndex).registerA addInstructions( registerIndex + 1, diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt index f09c7d694..8b41fc5df 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatch.kt @@ -13,18 +13,51 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.layout.tablet.fingerprints.GetFormFactorFingerprint import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch -import app.revanced.util.exception +import app.revanced.util.resultOrThrow @Patch( name = "Enable tablet layout", - description = "Adds an option to spoof the device form factor to a tablet which enables the tablet layout.", - dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class], - compatiblePackages = [CompatiblePackage("com.google.android.youtube")] + description = "Adds an option to enable tablet layout", + dependencies = [ + IntegrationsPatch::class, + SettingsPatch::class, + AddResourcesPatch::class, + ], + compatiblePackages = [ + CompatiblePackage( + "com.google.android.youtube", arrayOf( + "18.32.39", + "18.37.36", + "18.38.44", + "18.43.45", + "18.44.41", + "18.45.43", + "18.48.39", + "18.49.37", + "19.01.34", + "19.02.39", + "19.03.36", + "19.04.38", + "19.05.36", + "19.06.39", + "19.07.40", + "19.08.36", + "19.09.38", + "19.10.39", + "19.11.43", + "19.12.41", + "19.13.37", + "19.14.43", + "19.15.36", + "19.16.39" + ) + ) + ] ) @Suppress("unused") -object EnableTabletLayoutPatch : BytecodePatch( - setOf(GetFormFactorFingerprint) -) { +object EnableTabletLayoutPatch : BytecodePatch(setOf(GetFormFactorFingerprint)) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/TabletLayoutPatch;" + override fun execute(context: BytecodeContext) { AddResourcesPatch(this::class) @@ -32,7 +65,7 @@ object EnableTabletLayoutPatch : BytecodePatch( SwitchPreference("revanced_tablet_layout") ) - GetFormFactorFingerprint.result?.let { + GetFormFactorFingerprint.resultOrThrow().let { it.mutableMethod.apply { val returnIsLargeFormFactorIndex = getInstructions().lastIndex - 4 val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex) @@ -40,8 +73,8 @@ object EnableTabletLayoutPatch : BytecodePatch( addInstructionsWithLabels( 0, """ - invoke-static { }, Lapp/revanced/integrations/youtube/patches/EnableTabletLayoutPatch;->enableTabletLayout()Z - move-result v0 # Free register + invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->getTabletLayoutEnabled()Z + move-result v0 if-nez v0, :is_large_form_factor """, ExternalLabel( @@ -50,6 +83,6 @@ object EnableTabletLayoutPatch : BytecodePatch( ) ) } - } ?: GetFormFactorFingerprint.exception + } } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/fingerprints/GetFormFactorFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/fingerprints/GetFormFactorFingerprint.kt index c7cb6d1e7..8742fe5bc 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/fingerprints/GetFormFactorFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tablet/fingerprints/GetFormFactorFingerprint.kt @@ -21,5 +21,6 @@ internal object GetFormFactorFingerprint : MethodFingerprint( Opcode.INVOKE_STATIC, Opcode.MOVE_RESULT_OBJECT, Opcode.RETURN_OBJECT - ) + ), + strings = listOf("") ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch.kt index d3b4183aa..6b1f154bf 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/TabletMiniPlayerPatch.kt @@ -1,152 +1,11 @@ package app.revanced.patches.youtube.layout.tabletminiplayer import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.fingerprint.MethodFingerprint import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.PatchException -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.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerDimensionsCalculatorParentFingerprint -import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideFingerprint -import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerOverrideNoContextFingerprint -import app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints.MiniPlayerResponseModelSizeCheckFingerprint -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.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import app.revanced.patches.youtube.layout.miniplayer.MiniplayerPatch -@Patch( - name = "Tablet mini player", - description = "Adds an option to enable the tablet mini player layout.", - dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", arrayOf( - "18.32.39", - "18.37.36", - "18.38.44", - "18.43.45", - "18.44.41", - "18.45.43", - "18.48.39", - "18.49.37", - "19.01.34", - "19.02.39", - "19.03.36", - "19.04.38", - "19.05.36", - "19.06.39", - "19.07.40", - "19.08.36", - "19.09.38", - "19.10.39", - "19.11.43", - "19.12.41", - "19.13.37", - "19.14.43", - "19.15.36", - "19.16.39", - ) - ) - ] -) -@Suppress("unused") -object TabletMiniPlayerPatch : BytecodePatch( - setOf( - MiniPlayerDimensionsCalculatorParentFingerprint, - MiniPlayerResponseModelSizeCheckFingerprint, - MiniPlayerOverrideFingerprint - ) -) { +@Deprecated("This patch class has been renamed to Miniplayer.") +object TabletMiniPlayerPatch : BytecodePatch(dependencies = setOf(MiniplayerPatch::class)) { override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences( - SwitchPreference("revanced_tablet_miniplayer") - ) - - // First resolve the fingerprints via the parent fingerprint. - MiniPlayerDimensionsCalculatorParentFingerprint.result - ?: throw MiniPlayerDimensionsCalculatorParentFingerprint.exception - val miniPlayerClass = MiniPlayerDimensionsCalculatorParentFingerprint.result!!.classDef - - /* - * No context parameter method. - */ - MiniPlayerOverrideNoContextFingerprint.resolve(context, miniPlayerClass) - val (method, _, parameterRegister) = MiniPlayerOverrideNoContextFingerprint.addProxyCall() - - // Insert right before the return instruction. - val secondInsertIndex = method.implementation!!.instructions.size - 1 - method.insertOverride( - secondInsertIndex, parameterRegister - /** same register used to return **/ - ) - - /* - * Override every return instruction with the proxy call. - */ - MiniPlayerOverrideFingerprint.result?.let { result -> - result.mutableMethod.let { method -> - val appNameStringIndex = result.scanResult.stringsScanResult!!.matches.first().index + 2 - context.toMethodWalker(method).nextMethod(appNameStringIndex, true) - .getMethod() as MutableMethod - }.apply { - implementation!!.let { implementation -> - val returnIndices = implementation.instructions - .withIndex() - .filter { (_, instruction) -> instruction.opcode == Opcode.RETURN } - .map { (index, _) -> index } - - if (returnIndices.isEmpty()) throw PatchException("No return instructions found.") - - // This method clobbers register p0 to return the value, calculate to override. - val returnedRegister = implementation.registerCount - parameters.size - - // Hook the returned register on every return instruction. - returnIndices.forEach { index -> insertOverride(index, returnedRegister) } - } - } - - return@let - } ?: throw MiniPlayerOverrideFingerprint.exception - - /* - * Size check return value override. - */ - MiniPlayerResponseModelSizeCheckFingerprint.addProxyCall() - } - - // Helper methods. - private fun MethodFingerprint.addProxyCall(): Triple { - val (method, scanIndex, parameterRegister) = this.unwrap() - method.insertOverride(scanIndex, parameterRegister) - - return Triple(method, scanIndex, parameterRegister) - } - - private fun MutableMethod.insertOverride(index: Int, overrideRegister: Int) { - this.addInstructions( - index, - """ - invoke-static {v$overrideRegister}, Lapp/revanced/integrations/youtube/patches/TabletMiniPlayerOverridePatch;->getTabletMiniPlayerOverride(Z)Z - move-result v$overrideRegister - """ - ) - } - - private fun MethodFingerprint.unwrap(): Triple { - val result = this.result!! - val scanIndex = result.scanResult.patternScanResult!!.endIndex - val method = result.mutableMethod - val instructions = method.implementation!!.instructions - val parameterRegister = (instructions[scanIndex] as OneRegisterInstruction).registerA - - return Triple(method, scanIndex, parameterRegister) } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerDimensionsCalculatorParentFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerDimensionsCalculatorParentFingerprint.kt deleted file mode 100644 index 2e1612373..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerDimensionsCalculatorParentFingerprint.kt +++ /dev/null @@ -1,23 +0,0 @@ -package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -internal object MiniPlayerDimensionsCalculatorParentFingerprint : MethodFingerprint( - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - returnType = "V", - parameters = listOf("F"), - opcodes = listOf( - Opcode.CONST_HIGH16, - Opcode.ADD_FLOAT_2ADDR, - null, // Opcode.MUL_FLOAT or Opcode.MUL_FLOAT_2ADDR - Opcode.CONST_4, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT, - Opcode.FLOAT_TO_INT, - Opcode.INVOKE_INTERFACE, - Opcode.RETURN_VOID, - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideNoContextFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideNoContextFingerprint.kt deleted file mode 100644 index c6ea61f3f..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/tabletminiplayer/fingerprints/MiniPlayerOverrideNoContextFingerprint.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.youtube.layout.tabletminiplayer.fingerprints - -import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -internal object MiniPlayerOverrideNoContextFingerprint : MethodFingerprint( - "Z", AccessFlags.FINAL or AccessFlags.PRIVATE, - opcodes = listOf(Opcode.RETURN), // anchor to insert the instruction -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch.kt index 3e9bfd603..23d7a62fd 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemeBytecodePatch.kt @@ -16,7 +16,7 @@ import app.revanced.patches.youtube.layout.theme.fingerprints.UseGradientLoading import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.util.exception -import app.revanced.util.indexOfFirstWideLiteralInstructionValue +import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -123,7 +123,7 @@ object ThemeBytecodePatch : BytecodePatch( ) UseGradientLoadingScreenFingerprint.result?.mutableMethod?.apply { - val isEnabledIndex = indexOfFirstWideLiteralInstructionValue(GRADIENT_LOADING_SCREEN_AB_CONSTANT) + 3 + val isEnabledIndex = indexOfFirstWideLiteralInstructionValueOrThrow(GRADIENT_LOADING_SCREEN_AB_CONSTANT) + 3 val isEnabledRegister = getInstruction(isEnabledIndex - 1).registerA addInstructions( diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt new file mode 100644 index 000000000..9cad7fd4d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackPatch.kt @@ -0,0 +1,112 @@ +package app.revanced.patches.youtube.misc.backgroundplayback + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +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.patches.all.misc.resources.AddResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference +import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.KidsBackgroundPlaybackPolicyControllerFingerprint +import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackManagerFingerprint +import app.revanced.patches.youtube.misc.backgroundplayback.fingerprints.BackgroundPlaybackSettingsFingerprint +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 app.revanced.util.resultOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Patch( + name = "Remove background playback restrictions", + description = "Removes restrictions on background playback, including playing kids videos in the background.", + dependencies = [ + BackgroundPlaybackResourcePatch::class, + IntegrationsPatch::class, + PlayerTypeHookPatch::class, + VideoInformationPatch::class, + SettingsPatch::class, + AddResourcesPatch::class + ], + compatiblePackages = [ + CompatiblePackage( + "com.google.android.youtube", + [ + "18.48.39", + "18.49.37", + "19.01.34", + "19.02.39", + "19.03.36", + "19.04.38", + "19.05.36", + "19.06.39", + "19.07.40", + "19.08.36", + "19.09.38", + "19.10.39", + "19.11.43", + "19.12.41", + "19.13.37", + "19.14.43", + "19.15.36", + "19.16.39", + ] + ) + ] +) +@Suppress("unused") +object BackgroundPlaybackPatch : BytecodePatch( + setOf( + BackgroundPlaybackManagerFingerprint, + BackgroundPlaybackSettingsFingerprint, + KidsBackgroundPlaybackPolicyControllerFingerprint + ) +) { + private const val INTEGRATIONS_CLASS_DESCRIPTOR = + "Lapp/revanced/integrations/youtube/patches/BackgroundPlaybackPatch;" + + override fun execute(context: BytecodeContext) { + AddResourcesPatch(this::class) + + SettingsPatch.PreferenceScreen.MISC.addPreferences( + NonInteractivePreference("revanced_background_playback") + ) + + BackgroundPlaybackManagerFingerprint.resultOrThrow().mutableMethod.addInstructions( + 0, + """ + invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z + move-result v0 + return v0 + """ + ) + + // Enable background playback option in YouTube settings + BackgroundPlaybackSettingsFingerprint.resultOrThrow().mutableMethod.apply { + val booleanCalls = implementation!!.instructions.withIndex() + .filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" } + + val settingsBooleanIndex = booleanCalls.elementAt(1).index + val settingsBooleanMethod = + context.toMethodWalker(this).nextMethod(settingsBooleanIndex, true).getMethod() as MutableMethod + + settingsBooleanMethod.addInstructions( + 0, + """ + invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideBackgroundPlaybackAvailable()Z + move-result v0 + return v0 + """ + ) + } + + // Force allowing background play for videos labeled for kids. + KidsBackgroundPlaybackPolicyControllerFingerprint.resultOrThrow().mutableMethod.addInstruction( + 0, + "return-void" + ) + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackResourcePatch.kt similarity index 80% rename from src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackResourcePatch.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackResourcePatch.kt index d310d3abf..3be877a9a 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackResourcePatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/BackgroundPlaybackResourcePatch.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.youtube.misc.minimizedplayback +package app.revanced.patches.youtube.misc.backgroundplayback import app.revanced.patcher.data.ResourceContext import app.revanced.patcher.patch.ResourcePatch @@ -8,7 +8,7 @@ import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch @Patch( dependencies = [ResourceMappingPatch::class], ) -internal object MinimizedPlaybackResourcePatch : ResourcePatch() { +internal object BackgroundPlaybackResourcePatch : ResourcePatch() { internal var prefBackgroundAndOfflineCategoryId: Long = -1 override fun execute(context: ResourceContext) { diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/MinimizedPlaybackManagerFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/BackgroundPlaybackManagerFingerprint.kt similarity index 85% rename from src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/MinimizedPlaybackManagerFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/BackgroundPlaybackManagerFingerprint.kt index 2be2ec0d7..4695df5da 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/MinimizedPlaybackManagerFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/BackgroundPlaybackManagerFingerprint.kt @@ -1,11 +1,11 @@ -package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints +package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal object MinimizedPlaybackManagerFingerprint : MethodFingerprint( +internal object BackgroundPlaybackManagerFingerprint : MethodFingerprint( "Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/MinimizedPlaybackSettingsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/BackgroundPlaybackSettingsFingerprint.kt similarity index 61% rename from src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/MinimizedPlaybackSettingsFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/BackgroundPlaybackSettingsFingerprint.kt index 8f31e0173..c57d9173e 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/MinimizedPlaybackSettingsFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/BackgroundPlaybackSettingsFingerprint.kt @@ -1,12 +1,12 @@ -package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints +package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.misc.minimizedplayback.MinimizedPlaybackResourcePatch +import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -internal object MinimizedPlaybackSettingsFingerprint : LiteralValueFingerprint( +internal object BackgroundPlaybackSettingsFingerprint : LiteralValueFingerprint( returnType = "Ljava/lang/String;", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, parameters = listOf(), @@ -19,5 +19,5 @@ internal object MinimizedPlaybackSettingsFingerprint : LiteralValueFingerprint( Opcode.IF_NEZ, Opcode.GOTO ), - literalSupplier = { MinimizedPlaybackResourcePatch.prefBackgroundAndOfflineCategoryId } + literalSupplier = { BackgroundPlaybackResourcePatch.prefBackgroundAndOfflineCategoryId } ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/KidsMinimizedPlaybackPolicyControllerFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/KidsBackgroundPlaybackPolicyControllerFingerprint.kt similarity index 57% rename from src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/KidsMinimizedPlaybackPolicyControllerFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/KidsBackgroundPlaybackPolicyControllerFingerprint.kt index af992d609..7f33326c5 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/fingerprints/KidsMinimizedPlaybackPolicyControllerFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/backgroundplayback/fingerprints/KidsBackgroundPlaybackPolicyControllerFingerprint.kt @@ -1,12 +1,11 @@ -package app.revanced.patches.youtube.misc.minimizedplayback.fingerprints +package app.revanced.patches.youtube.misc.backgroundplayback.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction -internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerprint( +internal object KidsBackgroundPlaybackPolicyControllerFingerprint : LiteralValueFingerprint( returnType = "V", accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, parameters = listOf("I", "L", "L"), @@ -26,9 +25,5 @@ internal object KidsMinimizedPlaybackPolicyControllerFingerprint : MethodFingerp Opcode.INVOKE_VIRTUAL, Opcode.RETURN_VOID ), - customFingerprint = { methodDef, _ -> - methodDef.implementation!!.instructions.any { - ((it as? NarrowLiteralInstruction)?.narrowLiteral == 5) - } - } + literalSupplier = { 5 }, ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt index c008f6736..de6994840 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/SpoofClientPatch.kt @@ -286,9 +286,12 @@ object SpoofClientPatch : BytecodePatch( // Fix playback speed menu item if spoofing to iOS. CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let { - val shouldCreateMenuIndex = it.scanResult.patternScanResult!!.endIndex + val scanResult = it.scanResult.patternScanResult!! + if (scanResult.startIndex != 0) throw PatchException("Unexpected start index: ${scanResult.startIndex}") it.mutableMethod.apply { + // Find the conditional check if the playback speed menu item is not created. + val shouldCreateMenuIndex = indexOfFirstInstructionOrThrow(scanResult.endIndex) { opcode == Opcode.IF_EQZ } val shouldCreateMenuRegister = getInstruction(shouldCreateMenuIndex).registerA addInstructions( diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt index 2c0eaceb3..2437dfda6 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/UserAgentClientSpoofPatch.kt @@ -8,11 +8,14 @@ import app.revanced.patches.all.misc.transformation.IMethodCall import app.revanced.patches.all.misc.transformation.Instruction35cInfo import app.revanced.patches.all.misc.transformation.filterMapInstruction35c import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.ClassDef import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.iface.reference.StringReference object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch() { private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube" @@ -42,20 +45,31 @@ object UserAgentClientSpoofPatch : BaseTransformInstructionsPatch()?.toString() - // This can technically also match non-user agent string builder append methods, - // but no such occurrences are present in the app. + // Only replace string builder usage. if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) { return } + // Do not change the package name in methods that use resources, or for methods that use GmsCore. + // Changing these package names will result in playback limitations, + // particularly Android VR background audio only playback. + val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction { + val reference = getReference() + opcode == Opcode.CONST_STRING && + (reference?.string == "android.resource://" || reference?.string == "gcore_") + } + if (resourceOrGmsStringInstructionIndex >= 0) { + return + } + // Overwrite the result of context.getPackageName() with the original package name. replaceInstruction( instructionIndex + 1, - "const-string v$targetRegister, \"${ORIGINAL_PACKAGE_NAME}\"", + "const-string v$targetRegister, \"$ORIGINAL_PACKAGE_NAME\"", ) } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlaybackSpeedMenuItemFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlaybackSpeedMenuItemFingerprint.kt index 389977dd8..035771ce2 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlaybackSpeedMenuItemFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/fix/playback/fingerprints/CreatePlaybackSpeedMenuItemFingerprint.kt @@ -8,15 +8,27 @@ import com.android.tools.smali.dexlib2.Opcode internal object CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, returnType = "V", - parameters = listOf("[L", "F"), opcodes = listOf( - Opcode.IGET_OBJECT, + Opcode.IGET_OBJECT, // First instruction of the method Opcode.IGET_OBJECT, Opcode.IGET_OBJECT, Opcode.CONST_4, Opcode.IF_EQZ, Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT, // Return value controls the creation of the playback speed menu item. - Opcode.IF_EQZ, // If the return value is false, the playback speed menu item is not created. + null // MOVE_RESULT or MOVE_RESULT_OBJECT, Return value controls the creation of the playback speed menu item. ), + // 19.01 and earlier is missing the second parameter. + // Since this fingerprint is somewhat weak, work around by checking for both method parameter signatures. + customFingerprint = custom@{ methodDef, _ -> + // 19.01 and earlier parameters are: "[L" + // 19.02+ parameters are "[L", "F" + val parameterTypes = methodDef.parameterTypes + val firstParameter = parameterTypes.firstOrNull() + + if (firstParameter == null || !firstParameter.startsWith("[L")) { + return@custom false + } + + parameterTypes.size == 1 || (parameterTypes.size == 2 && parameterTypes[1] == "F") + } ) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch.kt index c3569590b..b354a6bc7 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/minimizedplayback/MinimizedPlaybackPatch.kt @@ -1,117 +1,11 @@ package app.revanced.patches.youtube.misc.minimizedplayback import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions 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.patches.all.misc.resources.AddResourcesPatch -import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference -import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch -import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.KidsMinimizedPlaybackPolicyControllerFingerprint -import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint -import app.revanced.patches.youtube.misc.minimizedplayback.fingerprints.MinimizedPlaybackSettingsFingerprint -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 app.revanced.util.exception -import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction -import com.android.tools.smali.dexlib2.iface.reference.MethodReference - -@Patch( - name = "Minimized playback", - description = "Unlocks options for picture-in-picture and background playback.", - dependencies = [ - MinimizedPlaybackResourcePatch::class, - IntegrationsPatch::class, - PlayerTypeHookPatch::class, - VideoInformationPatch::class, - SettingsPatch::class, - AddResourcesPatch::class - ], - compatiblePackages = [ - CompatiblePackage( - "com.google.android.youtube", - [ - "18.48.39", - "18.49.37", - "19.01.34", - "19.02.39", - "19.03.36", - "19.04.38", - "19.05.36", - "19.06.39", - "19.07.40", - "19.08.36", - "19.09.38", - "19.10.39", - "19.11.43", - "19.12.41", - "19.13.37", - "19.14.43", - "19.15.36", - "19.16.39", - ] - ) - ] -) -@Suppress("unused") -object MinimizedPlaybackPatch : BytecodePatch( - setOf( - MinimizedPlaybackManagerFingerprint, - MinimizedPlaybackSettingsFingerprint, - KidsMinimizedPlaybackPolicyControllerFingerprint - ) -) { - private const val INTEGRATIONS_CLASS_DESCRIPTOR = - "Lapp/revanced/integrations/youtube/patches/MinimizedPlaybackPatch;" +import app.revanced.patches.youtube.misc.backgroundplayback.BackgroundPlaybackPatch +@Deprecated("This patch class has been renamed to BackgroundPlaybackPatch.") +object MinimizedPlaybackPatch : BytecodePatch(dependencies = setOf(BackgroundPlaybackPatch::class)) { override fun execute(context: BytecodeContext) { - AddResourcesPatch(this::class) - - SettingsPatch.PreferenceScreen.MISC.addPreferences( - NonInteractivePreference("revanced_minimized_playback") - ) - - MinimizedPlaybackManagerFingerprint.result?.apply { - mutableMethod.addInstructions( - 0, - """ - invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->playbackIsNotShort()Z - move-result v0 - return v0 - """ - ) - } ?: throw MinimizedPlaybackManagerFingerprint.exception - - // Enable minimized playback option in YouTube settings - MinimizedPlaybackSettingsFingerprint.result?.apply { - val booleanCalls = method.implementation!!.instructions.withIndex() - .filter { ((it.value as? ReferenceInstruction)?.reference as? MethodReference)?.returnType == "Z" } - - val settingsBooleanIndex = booleanCalls.elementAt(1).index - val settingsBooleanMethod = - context.toMethodWalker(method).nextMethod(settingsBooleanIndex, true).getMethod() as MutableMethod - - settingsBooleanMethod.addInstructions( - 0, - """ - invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->overrideMinimizedPlaybackAvailable()Z - move-result v0 - return v0 - """ - ) - } ?: throw MinimizedPlaybackSettingsFingerprint.exception - - // Force allowing background play for videos labeled for kids. - // Some regions and YouTube accounts do not require this patch. - KidsMinimizedPlaybackPolicyControllerFingerprint.result?.apply { - mutableMethod.addInstruction( - 0, - "return-void" - ) - } ?: throw KidsMinimizedPlaybackPolicyControllerFingerprint.exception } -} +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/src/main/kotlin/app/revanced/util/BytecodeUtils.kt index acc989c37..7e5388d3f 100644 --- a/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -8,6 +8,7 @@ import app.revanced.patcher.patch.PatchException import app.revanced.patcher.util.proxy.mutableTypes.MutableClass import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.Instruction import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction @@ -99,6 +100,7 @@ fun Method.indexOfIdResourceOrThrow(resourceName: String): Int { * Find the index of the first wide literal instruction with the given value. * * @return the first literal instruction with the value, or -1 if not found. + * @see indexOfFirstWideLiteralInstructionValueOrThrow */ fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let { it.instructions.indexOfFirst { instruction -> @@ -106,6 +108,18 @@ fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementati } } ?: -1 +/** + * Find the index of the first wide literal instruction with the given value, + * or throw an exception if not found. + * + * @return the first literal instruction with the value, or throws [PatchException] if not found. + */ +fun Method.indexOfFirstWideLiteralInstructionValueOrThrow(literal: Long) : Int { + val index = indexOfFirstWideLiteralInstructionValue(literal) + if (index < 0) throw PatchException("Could not find literal value: $literal") + return index +} + /** * Check if the method contains a literal with the given value. * @@ -144,7 +158,9 @@ inline fun Instruction.getReference() = (this as? Refere * @return The index of the first [Instruction] that matches the predicate. */ // TODO: delete this on next major release, the overloaded method with an optional start index serves the same purposes. -@Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)")) +// Method is deprecated, but annotation is commented out otherwise during compilation usage of the replacement is +// incorrectly flagged as deprecated. +//@Deprecated("Use the overloaded method with an optional start index.", ReplaceWith("indexOfFirstInstruction(predicate)")) fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = indexOfFirstInstruction(0, predicate) /** @@ -179,6 +195,21 @@ fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, predicate: Instru return index } +/** + * @return The list of indices of the opcode in reverse order. + */ +fun Method.findOpcodeIndicesReversed(opcode: Opcode): List { + val indexes = implementation!!.instructions + .withIndex() + .filter { (_, instruction) -> instruction.opcode == opcode } + .map { (index, _) -> index } + .reversed() + + if (indexes.isEmpty()) throw PatchException("No ${opcode.name} instructions found in: $this") + + return indexes +} + /** * Return the resolved methods of [MethodFingerprint]s early. */ diff --git a/src/main/resources/addresources/values/arrays.xml b/src/main/resources/addresources/values/arrays.xml index 5fad2d884..f8f2b4218 100644 --- a/src/main/resources/addresources/values/arrays.xml +++ b/src/main/resources/addresources/values/arrays.xml @@ -16,6 +16,35 @@ 17.33.42 + + + @string/revanced_miniplayer_type_entry_1 + @string/revanced_miniplayer_type_entry_2 + @string/revanced_miniplayer_type_entry_3 + @string/revanced_miniplayer_type_entry_4 + @string/revanced_miniplayer_type_entry_5 + @string/revanced_miniplayer_type_entry_6 + + + + ORIGINAL + PHONE + TABLET + MODERN_1 + MODERN_2 + MODERN_3 + + + @string/revanced_miniplayer_type_entry_1 + @string/revanced_miniplayer_type_entry_2 + @string/revanced_miniplayer_type_entry_3 + + + ORIGINAL + PHONE + TABLET + + @string/revanced_start_page_entry_0 diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index dc0f814e7..2eb59f177 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -198,22 +198,19 @@ Hide chips shelf Chips shelf is hidden Chips shelf is shown + Hide attributes section + \'Featured places\', Games and Music sections are hidden + \'Featured places\', Games and Music sections are shown + Hide Chapters section + Chapters section is hidden + Chapters section is shown + Hide \'Explore the podcast\' section + \'Explore the podcast\' section is hidden + \'Explore the podcast\' section is shown Hide info cards section Info cards section is hidden Info cards section is shown - Hide chapters - Chapters are hidden - Chapters are shown - Hide game section - Game section is hidden - Game section is shown - Hide music section - Music section is hidden - Music section is shown - Hide podcast section - Podcast section is hidden - Podcast section is shown - Hide transcript section + Hide Transcript section Transcript section is hidden Transcript section is shown Video description @@ -502,15 +499,24 @@ Comments Hide or show comments section components - Hide preview comment - Preview comment is hidden - Preview comment is shown + Hide \'Comments by members\' header + \'Comments by members\' header is hidden + \'Comments by members\' header is shown Hide comments section - Comment section is hidden - Comment section is shown - Hide timestamp and emoji buttons - Comment timestamp and emoji buttons are hidden - Comment timestamp and emoji buttons are shown + Comments section is hidden + Comments section is shown + Hide \'Create a Short\' button + \'Create a Short\' button is hidden + \'Create a Short\' button is shown + Hide preview comment + Preview comment is hidden + Preview comment is shown + Hide thanks button + Thanks button is hidden + Thanks button is shown + Hide timestamp and emoji buttons + Timestamp and emoji buttons are hidden + Timestamp and emoji buttons are shown Hide crowdfunding box @@ -661,6 +667,7 @@ Player overlay opacity Opacity value between 0-100, where 0 is transparent + Player overlay opacity must be between 0-100 Return YouTube Dislike @@ -947,11 +954,29 @@ Tablet layout is enabled Tablet layout is disabled Community posts do not show up on tablet layouts - - - Enable tablet mini player - Mini player is enabled - Mini player is disabled + x + + Miniplayer + Change the style of the in app minimized player + Miniplayer type + Original + Phone + Tablet + Modern 1 + Modern 2 + Modern 3 + Hide expand and close buttons + Buttons are hidden\n(swipe miniplayer to expand or close) + Expand and close buttons are shown + Hide subtexts + Subtexts are hidden + Subtexts are shown + Hide skip forward and back buttons + Skip forward and back are hidden + Skip forward and back are shown + Overlay opacity + Opacity value between 0-100, where 0 is transparent + Miniplayer overlay opacity must be between 0-100 Enable gradient loading screen @@ -1032,9 +1057,9 @@ Opening links externally Opening links in app - - Minimized playback - This setting can be found in Settings -> Background + + Background playback + This setting can be found in Settings -> Background Remove tracking query parameter @@ -1101,7 +1126,7 @@ Client is not spoofed\n\nVideo playback may not work Turning off this setting may cause video playback issues. Spoof client to iOS - Client is currently spoofed to iOS\n\nSide effects include:\n• No HDR video\n• Watch history may not work\n• Live streams cannot play as audio only\n• Live streams not available on older devices + Client is currently spoofed to iOS\n\nSide effects include:\n• No HDR video\n• Watch history may not work\n• Higher video qualities may be missing\n• Live streams cannot play as audio only\n• Live streams not available on Android 8.0 Client is currently spoofed to Android VR\n\nSide effects include:\n• No HDR video\n• Kids videos do not playback\n• Paused videos can randomly resume Spoof client thumbnails not available (API timed out) Spoof client thumbnails temporarily not available: %s