mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2024-11-10 01:01:56 +01:00
feat(YouTube - Miniplayer): Rename Tablet mini player
and allow selecting the style of the in-app miniplayer (#3302)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
parent
64ac233782
commit
5511736b0c
31 changed files with 805 additions and 220 deletions
|
@ -1528,6 +1528,12 @@ public final class app/revanced/patches/youtube/layout/hide/time/HideTimestampPa
|
||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
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 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 static final field INSTANCE Lapp/revanced/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch;
|
||||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||||
|
@ -1894,6 +1900,7 @@ public final class app/revanced/patches/yuka/misc/unlockpremium/UnlockPremiumPat
|
||||||
public final class app/revanced/util/BytecodeUtilsKt {
|
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 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 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 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;ILkotlin/jvm/functions/Function1;)I
|
||||||
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I
|
public static final fun indexOfFirstInstruction (Lcom/android/tools/smali/dexlib2/iface/Method;Lkotlin/jvm/functions/Function1;)I
|
||||||
|
@ -1901,6 +1908,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 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 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 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 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 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
|
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package app.revanced.patches.shared.misc.mapping
|
package app.revanced.patches.shared.misc.mapping
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -51,9 +52,10 @@ object ResourceMappingPatch : ResourcePatch() {
|
||||||
threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
|
threadPoolExecutor.also { it.shutdown() }.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun get(type: String, name: String) = resourceMappings.first {
|
operator fun get(type: String, name: String) =
|
||||||
|
resourceMappings.firstOrNull {
|
||||||
it.type == type && it.name == name
|
it.type == type && it.name == name
|
||||||
}.id
|
}?.id ?: throw PatchException("Could not find resource type: $type name: $name")
|
||||||
|
|
||||||
data class ResourceElement(val type: String, val name: String, val id: Long)
|
data class ResourceElement(val type: String, val name: String, val id: Long)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<FieldReference>()?.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<OneRegisterInstruction>(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<OneRegisterInstruction>(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<TwoRegisterInstruction>(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<TypeReference>()?.type == hookedClassType
|
||||||
|
}
|
||||||
|
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
|
||||||
|
addInstruction(
|
||||||
|
imageViewIndex + 1,
|
||||||
|
"invoke-static { v$register }, $integrationsMethodName"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 }
|
||||||
|
)
|
|
@ -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;")
|
||||||
|
)
|
|
@ -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 }
|
||||||
|
)
|
|
@ -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.
|
||||||
|
)
|
|
@ -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 }
|
||||||
|
)
|
|
@ -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 }
|
||||||
|
)
|
|
@ -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 }
|
||||||
|
)
|
|
@ -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 }
|
||||||
|
)
|
|
@ -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 }
|
||||||
|
)
|
|
@ -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")
|
||||||
|
)
|
|
@ -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.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
internal object MiniPlayerOverrideFingerprint : MethodFingerprint(
|
internal object MiniplayerOverrideFingerprint : MethodFingerprint(
|
||||||
returnType = "L",
|
returnType = "L",
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
parameters = listOf("L"),
|
parameters = listOf("L"),
|
|
@ -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
|
||||||
|
)
|
|
@ -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.extensions.or
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal object MiniPlayerResponseModelSizeCheckFingerprint : MethodFingerprint(
|
internal object MiniplayerResponseModelSizeCheckFingerprint : MethodFingerprint(
|
||||||
"L",
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
returnType = "L",
|
||||||
listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
|
parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
|
||||||
listOf(
|
opcodes = listOf(
|
||||||
Opcode.RETURN_OBJECT,
|
Opcode.RETURN_OBJECT,
|
||||||
Opcode.CHECK_CAST,
|
Opcode.CHECK_CAST,
|
||||||
Opcode.CHECK_CAST,
|
Opcode.CHECK_CAST,
|
|
@ -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;"
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
package app.revanced.patches.youtube.layout.player.overlay
|
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.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
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.CompatiblePackage
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patches.youtube.layout.player.overlay.fingerprints.CreatePlayerOverviewFingerprint
|
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
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
|
@ -27,7 +27,7 @@ object CustomPlayerOverlayOpacityPatch : BytecodePatch(setOf(CreatePlayerOvervie
|
||||||
CreatePlayerOverviewFingerprint.result?.let { result ->
|
CreatePlayerOverviewFingerprint.result?.let { result ->
|
||||||
result.mutableMethod.apply {
|
result.mutableMethod.apply {
|
||||||
val viewRegisterIndex =
|
val viewRegisterIndex =
|
||||||
indexOfFirstWideLiteralInstructionValue(CustomPlayerOverlayOpacityResourcePatch.scrimOverlayId) + 3
|
indexOfFirstWideLiteralInstructionValueOrThrow(CustomPlayerOverlayOpacityResourcePatch.scrimOverlayId) + 3
|
||||||
val viewRegister =
|
val viewRegister =
|
||||||
getInstruction<OneRegisterInstruction>(viewRegisterIndex).registerA
|
getInstruction<OneRegisterInstruction>(viewRegisterIndex).registerA
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package app.revanced.patches.youtube.layout.seekbar
|
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.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
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
|
||||||
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch.lithoColorOverrideHook
|
import app.revanced.patches.youtube.layout.theme.LithoColorHookPatch.lithoColorOverrideHook
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
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.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ internal object SeekbarColorBytecodePatch : BytecodePatch(
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
|
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
|
||||||
val registerIndex = indexOfFirstWideLiteralInstructionValue(resourceId) + 2
|
val registerIndex = indexOfFirstWideLiteralInstructionValueOrThrow(resourceId) + 2
|
||||||
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
|
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
|
||||||
addInstructions(
|
addInstructions(
|
||||||
registerIndex + 1,
|
registerIndex + 1,
|
||||||
|
|
|
@ -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.layout.tablet.fingerprints.GetFormFactorFingerprint
|
||||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.resultOrThrow
|
||||||
|
|
||||||
@Patch(
|
@Patch(
|
||||||
name = "Enable tablet layout",
|
name = "Enable tablet layout",
|
||||||
description = "Adds an option to spoof the device form factor to a tablet which enables the tablet layout.",
|
description = "Adds an option to enable tablet layout",
|
||||||
dependencies = [IntegrationsPatch::class, SettingsPatch::class, AddResourcesPatch::class],
|
dependencies = [
|
||||||
compatiblePackages = [CompatiblePackage("com.google.android.youtube")]
|
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")
|
@Suppress("unused")
|
||||||
object EnableTabletLayoutPatch : BytecodePatch(
|
object EnableTabletLayoutPatch : BytecodePatch(setOf(GetFormFactorFingerprint)) {
|
||||||
setOf(GetFormFactorFingerprint)
|
private const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/TabletLayoutPatch;"
|
||||||
) {
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
AddResourcesPatch(this::class)
|
AddResourcesPatch(this::class)
|
||||||
|
|
||||||
|
@ -32,7 +65,7 @@ object EnableTabletLayoutPatch : BytecodePatch(
|
||||||
SwitchPreference("revanced_tablet_layout")
|
SwitchPreference("revanced_tablet_layout")
|
||||||
)
|
)
|
||||||
|
|
||||||
GetFormFactorFingerprint.result?.let {
|
GetFormFactorFingerprint.resultOrThrow().let {
|
||||||
it.mutableMethod.apply {
|
it.mutableMethod.apply {
|
||||||
val returnIsLargeFormFactorIndex = getInstructions().lastIndex - 4
|
val returnIsLargeFormFactorIndex = getInstructions().lastIndex - 4
|
||||||
val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex)
|
val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex)
|
||||||
|
@ -40,8 +73,8 @@ object EnableTabletLayoutPatch : BytecodePatch(
|
||||||
addInstructionsWithLabels(
|
addInstructionsWithLabels(
|
||||||
0,
|
0,
|
||||||
"""
|
"""
|
||||||
invoke-static { }, Lapp/revanced/integrations/youtube/patches/EnableTabletLayoutPatch;->enableTabletLayout()Z
|
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->getTabletLayoutEnabled()Z
|
||||||
move-result v0 # Free register
|
move-result v0
|
||||||
if-nez v0, :is_large_form_factor
|
if-nez v0, :is_large_form_factor
|
||||||
""",
|
""",
|
||||||
ExternalLabel(
|
ExternalLabel(
|
||||||
|
@ -50,6 +83,6 @@ object EnableTabletLayoutPatch : BytecodePatch(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} ?: GetFormFactorFingerprint.exception
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,5 +21,6 @@ internal object GetFormFactorFingerprint : MethodFingerprint(
|
||||||
Opcode.INVOKE_STATIC,
|
Opcode.INVOKE_STATIC,
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
Opcode.RETURN_OBJECT
|
Opcode.RETURN_OBJECT
|
||||||
)
|
),
|
||||||
|
strings = listOf("")
|
||||||
)
|
)
|
|
@ -1,152 +1,11 @@
|
||||||
package app.revanced.patches.youtube.layout.tabletminiplayer
|
package app.revanced.patches.youtube.layout.tabletminiplayer
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
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.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patches.youtube.layout.miniplayer.MiniplayerPatch
|
||||||
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
|
|
||||||
|
|
||||||
@Patch(
|
@Deprecated("This patch class has been renamed to Miniplayer.")
|
||||||
name = "Tablet mini player",
|
object TabletMiniPlayerPatch : BytecodePatch(dependencies = setOf(MiniplayerPatch::class)) {
|
||||||
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
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
override fun execute(context: BytecodeContext) {
|
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<MutableMethod, Int, Int> {
|
|
||||||
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<MutableMethod, Int, Int> {
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
|
||||||
)
|
|
||||||
)
|
|
|
@ -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
|
|
||||||
)
|
|
|
@ -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.integrations.IntegrationsPatch
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import app.revanced.util.exception
|
import app.revanced.util.exception
|
||||||
import app.revanced.util.indexOfFirstWideLiteralInstructionValue
|
import app.revanced.util.indexOfFirstWideLiteralInstructionValueOrThrow
|
||||||
import app.revanced.util.resultOrThrow
|
import app.revanced.util.resultOrThrow
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ object ThemeBytecodePatch : BytecodePatch(
|
||||||
)
|
)
|
||||||
|
|
||||||
UseGradientLoadingScreenFingerprint.result?.mutableMethod?.apply {
|
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<OneRegisterInstruction>(isEnabledIndex - 1).registerA
|
val isEnabledRegister = getInstruction<OneRegisterInstruction>(isEnabledIndex - 1).registerA
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
|
|
|
@ -286,9 +286,12 @@ object SpoofClientPatch : BytecodePatch(
|
||||||
// Fix playback speed menu item if spoofing to iOS.
|
// Fix playback speed menu item if spoofing to iOS.
|
||||||
|
|
||||||
CreatePlaybackSpeedMenuItemFingerprint.resultOrThrow().let {
|
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 {
|
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<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
|
val shouldCreateMenuRegister = getInstruction<OneRegisterInstruction>(shouldCreateMenuIndex).registerA
|
||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
|
|
|
@ -8,15 +8,27 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||||
internal object CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint(
|
internal object CreatePlaybackSpeedMenuItemFingerprint : MethodFingerprint(
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
returnType = "V",
|
returnType = "V",
|
||||||
parameters = listOf("[L", "F"),
|
|
||||||
opcodes = listOf(
|
opcodes = listOf(
|
||||||
Opcode.IGET_OBJECT,
|
Opcode.IGET_OBJECT, // First instruction of the method
|
||||||
Opcode.IGET_OBJECT,
|
Opcode.IGET_OBJECT,
|
||||||
Opcode.IGET_OBJECT,
|
Opcode.IGET_OBJECT,
|
||||||
Opcode.CONST_4,
|
Opcode.CONST_4,
|
||||||
Opcode.IF_EQZ,
|
Opcode.IF_EQZ,
|
||||||
Opcode.INVOKE_INTERFACE,
|
Opcode.INVOKE_INTERFACE,
|
||||||
Opcode.MOVE_RESULT, // Return value controls the creation of the playback speed menu item.
|
null // MOVE_RESULT or MOVE_RESULT_OBJECT, 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.
|
|
||||||
),
|
),
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -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.MutableClass
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
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.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
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.
|
* 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.
|
* @return the first literal instruction with the value, or -1 if not found.
|
||||||
|
* @see indexOfFirstWideLiteralInstructionValueOrThrow
|
||||||
*/
|
*/
|
||||||
fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let {
|
fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementation?.let {
|
||||||
it.instructions.indexOfFirst { instruction ->
|
it.instructions.indexOfFirst { instruction ->
|
||||||
|
@ -106,6 +108,18 @@ fun Method.indexOfFirstWideLiteralInstructionValue(literal: Long) = implementati
|
||||||
}
|
}
|
||||||
} ?: -1
|
} ?: -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.
|
* Check if the method contains a literal with the given value.
|
||||||
*
|
*
|
||||||
|
@ -144,7 +158,9 @@ inline fun <reified T : Reference> Instruction.getReference() = (this as? Refere
|
||||||
* @return The index of the first [Instruction] that matches the predicate.
|
* @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.
|
// 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)
|
fun Method.indexOfFirstInstruction(predicate: Instruction.() -> Boolean) = indexOfFirstInstruction(0, predicate)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -179,6 +195,21 @@ fun Method.indexOfFirstInstructionOrThrow(startIndex: Int = 0, predicate: Instru
|
||||||
return index
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The list of indices of the opcode in reverse order.
|
||||||
|
*/
|
||||||
|
fun Method.findOpcodeIndicesReversed(opcode: Opcode): List<Int> {
|
||||||
|
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.
|
* Return the resolved methods of [MethodFingerprint]s early.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -16,6 +16,35 @@
|
||||||
<item>17.33.42</item>
|
<item>17.33.42</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</patch>
|
</patch>
|
||||||
|
<patch id="layout.miniplayer.MiniplayerPatch">
|
||||||
|
<string-array name="revanced_miniplayer_type_19_15_entries">
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_1</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_2</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_3</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_4</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_5</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_6</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_miniplayer_type_19_15_entry_values">
|
||||||
|
<!-- Enum names from Integrations. -->
|
||||||
|
<item>ORIGINAL</item>
|
||||||
|
<item>PHONE</item>
|
||||||
|
<item>TABLET</item>
|
||||||
|
<item>MODERN_1</item>
|
||||||
|
<item>MODERN_2</item>
|
||||||
|
<item>MODERN_3</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_miniplayer_type_legacy_entries">
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_1</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_2</item>
|
||||||
|
<item>@string/revanced_miniplayer_type_entry_3</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="revanced_miniplayer_type_legacy_entry_values">
|
||||||
|
<item>ORIGINAL</item>
|
||||||
|
<item>PHONE</item>
|
||||||
|
<item>TABLET</item>
|
||||||
|
</string-array>
|
||||||
|
</patch>
|
||||||
<patch id="layout.startpage.ChangeStartPagePatch">
|
<patch id="layout.startpage.ChangeStartPagePatch">
|
||||||
<string-array name="revanced_start_page_entries">
|
<string-array name="revanced_start_page_entries">
|
||||||
<item>@string/revanced_start_page_entry_0</item>
|
<item>@string/revanced_start_page_entry_0</item>
|
||||||
|
|
|
@ -661,6 +661,7 @@
|
||||||
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
|
<patch id="layout.player.overlay.CustomPlayerOverlayOpacityResourcePatch">
|
||||||
<string name="revanced_player_overlay_opacity_title">Player overlay opacity</string>
|
<string name="revanced_player_overlay_opacity_title">Player overlay opacity</string>
|
||||||
<string name="revanced_player_overlay_opacity_summary">Opacity value between 0-100, where 0 is transparent</string>
|
<string name="revanced_player_overlay_opacity_summary">Opacity value between 0-100, where 0 is transparent</string>
|
||||||
|
<string name="revanced_player_overlay_opacity_invalid_toast">Player overlay opacity must be between 0-100</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
|
<patch id="layout.returnyoutubedislike.ReturnYouTubeDislikeResourcePatch">
|
||||||
<string name="revanced_ryd_settings_title">Return YouTube Dislike</string>
|
<string name="revanced_ryd_settings_title">Return YouTube Dislike</string>
|
||||||
|
@ -947,11 +948,29 @@
|
||||||
<string name="revanced_tablet_layout_summary_on">Tablet layout is enabled</string>
|
<string name="revanced_tablet_layout_summary_on">Tablet layout is enabled</string>
|
||||||
<string name="revanced_tablet_layout_summary_off">Tablet layout is disabled</string>
|
<string name="revanced_tablet_layout_summary_off">Tablet layout is disabled</string>
|
||||||
<string name="revanced_tablet_layout_user_dialog_message">Community posts do not show up on tablet layouts</string>
|
<string name="revanced_tablet_layout_user_dialog_message">Community posts do not show up on tablet layouts</string>
|
||||||
</patch>
|
</patch>x
|
||||||
<patch id="layout.tabletminiplayer.TabletMiniPlayerPatch">
|
<patch id="layout.miniplayer.MiniplayerPatch">
|
||||||
<string name="revanced_tablet_miniplayer_title">Enable tablet mini player</string>
|
<string name="revanced_miniplayer_screen_title">Miniplayer</string>
|
||||||
<string name="revanced_tablet_miniplayer_summary_on">Mini player is enabled</string>
|
<string name="revanced_miniplayer_screen_summary">Change the style of the in app minimized player</string>
|
||||||
<string name="revanced_tablet_miniplayer_summary_off">Mini player is disabled</string>
|
<string name="revanced_miniplayer_type_title">Miniplayer type</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_1">Original</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_2">Phone</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_3">Tablet</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_4">Modern 1</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_5">Modern 2</string>
|
||||||
|
<string name="revanced_miniplayer_type_entry_6">Modern 3</string>
|
||||||
|
<string name="revanced_miniplayer_hide_expand_close_title">Hide expand and close buttons</string>
|
||||||
|
<string name="revanced_miniplayer_hide_expand_close_summary_on">Buttons are hidden\n(swipe miniplayer to expand or close)</string>
|
||||||
|
<string name="revanced_miniplayer_hide_expand_close_summary_off">Expand and close buttons are shown</string>
|
||||||
|
<string name="revanced_miniplayer_hide_subtext_title">Hide subtexts</string>
|
||||||
|
<string name="revanced_miniplayer_hide_subtext_summary_on">Subtexts are hidden</string>
|
||||||
|
<string name="revanced_miniplayer_hide_subtext_summary_off">Subtexts are shown</string>
|
||||||
|
<string name="revanced_miniplayer_hide_rewind_forward_title">Hide skip forward and back buttons</string>
|
||||||
|
<string name="revanced_miniplayer_hide_rewind_forward_summary_on">Skip forward and back are hidden</string>
|
||||||
|
<string name="revanced_miniplayer_hide_rewind_forward_summary_off">Skip forward and back are shown</string>
|
||||||
|
<string name="revanced_miniplayer_opacity_title">Overlay opacity</string>
|
||||||
|
<string name="revanced_miniplayer_opacity_summary">Opacity value between 0-100, where 0 is transparent</string>
|
||||||
|
<string name="revanced_miniplayer_opacity_invalid_toast">Miniplayer overlay opacity must be between 0-100</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="layout.theme.ThemeBytecodePatch">
|
<patch id="layout.theme.ThemeBytecodePatch">
|
||||||
<string name="revanced_gradient_loading_screen_title">Enable gradient loading screen</string>
|
<string name="revanced_gradient_loading_screen_title">Enable gradient loading screen</string>
|
||||||
|
|
Loading…
Reference in a new issue