mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-12 05:07:41 +02:00
feat(YouTube): Support version 19.34.42
This commit is contained in:
@ -14,6 +14,7 @@ import app.revanced.patches.music.utils.settings.settingsPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.Utils.trimIndentMultiline
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.getAdaptiveIconResourceFile
|
||||
import app.revanced.util.getResourceGroup
|
||||
import app.revanced.util.underBarOrThrow
|
||||
import org.w3c.dom.Element
|
||||
@ -248,38 +249,23 @@ val customBrandingIconPatch = resourcePatch(
|
||||
return@execute
|
||||
}
|
||||
|
||||
fun getAdaptiveIconResourceFile(tag: String): String {
|
||||
document("res/mipmap-anydpi/ic_launcher_release.xml").use { document ->
|
||||
val adaptiveIcon = document
|
||||
.getElementsByTagName("adaptive-icon")
|
||||
.item(0) as Element
|
||||
|
||||
val childNodes = adaptiveIcon.childNodes
|
||||
for (i in 0 until childNodes.length) {
|
||||
val node = childNodes.item(i)
|
||||
if (node is Element && node.tagName == tag && node.hasAttribute("android:drawable")) {
|
||||
return node.getAttribute("android:drawable").split("/")[1]
|
||||
}
|
||||
}
|
||||
throw PatchException("Element not found: $tag")
|
||||
}
|
||||
}
|
||||
|
||||
mapOf(
|
||||
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("background"),
|
||||
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("foreground")
|
||||
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher_release.xml", "background"),
|
||||
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher_release.xml", "foreground")
|
||||
).forEach { (oldIconResourceFile, newIconResourceFile) ->
|
||||
mipmapDirectories.forEach {
|
||||
val mipmapDirectory = resourceDirectory.resolve(it)
|
||||
Files.move(
|
||||
mipmapDirectory
|
||||
.resolve("$oldIconResourceFile.png")
|
||||
.toPath(),
|
||||
mipmapDirectory
|
||||
.resolve("$newIconResourceFile.png")
|
||||
.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
if (oldIconResourceFile != newIconResourceFile) {
|
||||
mipmapDirectories.forEach {
|
||||
val mipmapDirectory = resourceDirectory.resolve(it)
|
||||
Files.move(
|
||||
mipmapDirectory
|
||||
.resolve("$oldIconResourceFile.png")
|
||||
.toPath(),
|
||||
mipmapDirectory
|
||||
.resolve("$newIconResourceFile.png")
|
||||
.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,10 +112,6 @@ private val settingsBytecodePatch = bytecodePatch(
|
||||
|
||||
// endregion
|
||||
|
||||
injectOnCreateMethodCall(
|
||||
EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR,
|
||||
"setDeviceInformation"
|
||||
)
|
||||
injectOnCreateMethodCall(
|
||||
EXTENSION_INITIALIZATION_CLASS_DESCRIPTOR,
|
||||
"onCreate"
|
||||
|
@ -147,18 +147,22 @@ val feedComponentsPatch = bytecodePatch(
|
||||
// region patch for hide floating button
|
||||
|
||||
onCreateMethod.apply {
|
||||
val fabIndex = indexOfFirstInstructionOrThrow {
|
||||
val stringIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.CONST_STRING &&
|
||||
getReference<StringReference>()?.string == "fab"
|
||||
}
|
||||
val fabRegister = getInstruction<OneRegisterInstruction>(fabIndex).registerA
|
||||
val jumpIndex = indexOfFirstInstructionOrThrow(fabIndex + 1, Opcode.CONST_STRING)
|
||||
val stringRegister = getInstruction<OneRegisterInstruction>(stringIndex).registerA
|
||||
val insertIndex = indexOfFirstInstructionOrThrow(stringIndex) {
|
||||
opcode == Opcode.INVOKE_DIRECT &&
|
||||
getReference<MethodReference>()?.name == "<init>"
|
||||
}
|
||||
val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex, Opcode.CONST_STRING)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
fabIndex, """
|
||||
invoke-static {}, $FEED_CLASS_DESCRIPTOR->hideFloatingButton()Z
|
||||
move-result v$fabRegister
|
||||
if-nez v$fabRegister, :hide
|
||||
insertIndex, """
|
||||
invoke-static {v$stringRegister}, $FEED_CLASS_DESCRIPTOR->hideFloatingButton(Ljava/lang/String;)Ljava/lang/String;
|
||||
move-result-object v$stringRegister
|
||||
if-eqz v$stringRegister, :hide
|
||||
""", ExternalLabel("hide", getInstruction(jumpIndex))
|
||||
)
|
||||
}
|
||||
|
@ -2,20 +2,18 @@
|
||||
|
||||
package app.revanced.patches.youtube.general.miniplayer
|
||||
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
|
||||
import app.revanced.patches.youtube.utils.resourceid.floatyBarTopMargin
|
||||
import app.revanced.patches.youtube.utils.resourceid.miniplayerMaxSize
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerForwardButton
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerRewindButton
|
||||
import app.revanced.patches.youtube.utils.resourceid.scrimOverlay
|
||||
import app.revanced.patches.youtube.utils.resourceid.ytOutlinePictureInPictureWhite
|
||||
import app.revanced.util.containsLiteralInstruction
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint(
|
||||
name = "miniplayerDimensionsCalculatorParentFingerprint",
|
||||
@ -25,6 +23,9 @@ internal val miniplayerDimensionsCalculatorParentFingerprint = legacyFingerprint
|
||||
literals = listOf(floatyBarTopMargin),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernAddViewListenerFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
@ -32,6 +33,9 @@ internal val miniplayerModernAddViewListenerFingerprint = legacyFingerprint(
|
||||
parameters = listOf("Landroid/view/View;")
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernCloseButtonFingerprint",
|
||||
returnType = "Landroid/widget/ImageView;",
|
||||
@ -40,41 +44,33 @@ internal val miniplayerModernCloseButtonFingerprint = legacyFingerprint(
|
||||
literals = listOf(modernMiniPlayerClose),
|
||||
)
|
||||
|
||||
private var constructorMethodCount = 0
|
||||
|
||||
internal fun isMultiConstructorMethod() = constructorMethodCount > 1
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_KEY = 45622882L
|
||||
// In later targets this feature flag does nothing and is dead code.
|
||||
internal const val MINIPLAYER_MODERN_FEATURE_LEGACY_KEY = 45630429L
|
||||
internal const val MINIPLAYER_DOUBLE_TAP_FEATURE_KEY = 45628823L
|
||||
internal const val MINIPLAYER_DRAG_DROP_FEATURE_KEY = 45628752L
|
||||
internal const val MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY = 45658112L
|
||||
internal const val MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY = 45652224L
|
||||
internal const val MINIPLAYER_INITIAL_SIZE_FEATURE_KEY = 45640023L
|
||||
internal const val MINIPLAYER_DISABLED_FEATURE_KEY = 45657015L
|
||||
|
||||
internal val miniplayerModernConstructorFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernConstructorFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("L"),
|
||||
literals = listOf(45623000L),
|
||||
customFingerprint = custom@{ method, classDef ->
|
||||
classDef.methods.forEach {
|
||||
if (MethodUtil.isConstructor(it)) constructorMethodCount += 1
|
||||
}
|
||||
|
||||
if (!is_19_25_or_greater)
|
||||
return@custom true
|
||||
|
||||
// Double tap action (Used in YouTube 19.25.39+).
|
||||
method.containsLiteralInstruction(45628823L)
|
||||
&& method.containsLiteralInstruction(45630429L)
|
||||
}
|
||||
)
|
||||
|
||||
internal val miniplayerModernDragAndDropFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernDragAndDropFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("L"),
|
||||
literals = listOf(45628752L),
|
||||
)
|
||||
|
||||
internal val miniplayerModernEnabledFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernEnabledFingerprint",
|
||||
literals = listOf(45622882L),
|
||||
internal val miniplayerOnCloseHandlerFingerprint = legacyFingerprint(
|
||||
name = "miniplayerOnCloseHandlerFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Z",
|
||||
literals = listOf(MINIPLAYER_DISABLED_FEATURE_KEY),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernExpandButtonFingerprint",
|
||||
returnType = "Landroid/widget/ImageView;",
|
||||
@ -83,6 +79,9 @@ internal val miniplayerModernExpandButtonFingerprint = legacyFingerprint(
|
||||
literals = listOf(modernMiniPlayerExpand),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernExpandCloseDrawablesFingerprint",
|
||||
returnType = "V",
|
||||
@ -91,6 +90,9 @@ internal val miniplayerModernExpandCloseDrawablesFingerprint = legacyFingerprint
|
||||
literals = listOf(ytOutlinePictureInPictureWhite),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernForwardButtonFingerprint",
|
||||
returnType = "Landroid/widget/ImageView;",
|
||||
@ -99,6 +101,9 @@ internal val miniplayerModernForwardButtonFingerprint = legacyFingerprint(
|
||||
literals = listOf(modernMiniPlayerForwardButton),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernOverlayViewFingerprint",
|
||||
returnType = "V",
|
||||
@ -107,6 +112,9 @@ internal val miniplayerModernOverlayViewFingerprint = legacyFingerprint(
|
||||
literals = listOf(scrimOverlay),
|
||||
)
|
||||
|
||||
/**
|
||||
* Matches using the class found in [miniplayerModernViewParentFingerprint].
|
||||
*/
|
||||
internal val miniplayerModernRewindButtonFingerprint = legacyFingerprint(
|
||||
name = "miniplayerModernRewindButtonFingerprint",
|
||||
returnType = "Landroid/widget/ImageView;",
|
||||
@ -123,11 +131,16 @@ internal val miniplayerModernViewParentFingerprint = legacyFingerprint(
|
||||
strings = listOf("player_overlay_modern_mini_player_controls")
|
||||
)
|
||||
|
||||
internal val miniplayerMinimumSizeFingerprint = legacyFingerprint(
|
||||
name = "miniplayerMinimumSizeFingerprint",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
literals = listOf(192L, 128L, miniplayerMaxSize),
|
||||
)
|
||||
|
||||
internal val miniplayerOverrideFingerprint = legacyFingerprint(
|
||||
name = "miniplayerOverrideFingerprint",
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("L"),
|
||||
strings = listOf("appName")
|
||||
)
|
||||
|
||||
@ -144,6 +157,7 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
|
||||
returnType = "L",
|
||||
parameters = listOf("Ljava/lang/Object;", "Ljava/lang/Object;"),
|
||||
opcodes = listOf(
|
||||
Opcode.RETURN_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.INVOKE_STATIC,
|
||||
@ -152,12 +166,12 @@ internal val miniplayerResponseModelSizeCheckFingerprint = legacyFingerprint(
|
||||
)
|
||||
)
|
||||
|
||||
internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
|
||||
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"
|
||||
|
||||
internal val youTubePlayerOverlaysLayoutFingerprint = legacyFingerprint(
|
||||
name = "youTubePlayerOverlaysLayoutFingerprint",
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.type == YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME
|
||||
}
|
||||
)
|
||||
|
||||
internal const val YOUTUBE_PLAYER_OVERLAYS_LAYOUT_CLASS_NAME =
|
||||
"Lcom/google/android/apps/youtube/app/common/player/overlay/YouTubePlayerOverlaysLayout;"
|
||||
|
@ -1,22 +1,22 @@
|
||||
package app.revanced.patches.youtube.general.miniplayer
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
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.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_PATH
|
||||
import app.revanced.patches.youtube.utils.patch.PatchList.MINIPLAYER
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_15_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_23_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_26_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_29_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_36_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_43_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerClose
|
||||
import app.revanced.patches.youtube.utils.resourceid.modernMiniPlayerExpand
|
||||
@ -28,6 +28,7 @@ import app.revanced.patches.youtube.utils.resourceid.ytOutlinePictureInPictureWh
|
||||
import app.revanced.patches.youtube.utils.resourceid.ytOutlineXWhite
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||
import app.revanced.util.findInstructionIndicesReversedOrThrow
|
||||
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
|
||||
import app.revanced.util.fingerprint.matchOrThrow
|
||||
@ -41,14 +42,13 @@ 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.NarrowLiteralInstruction
|
||||
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
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"$GENERAL_PATH/MiniplayerPatch;"
|
||||
@ -83,7 +83,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
"""
|
||||
invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z
|
||||
move-result v$register
|
||||
"""
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@ -105,39 +105,34 @@ val miniplayerPatch = bytecodePatch(
|
||||
* Adds an override to specify which modern miniplayer is used.
|
||||
*/
|
||||
fun MutableMethod.insertModernMiniplayerTypeOverride(iPutIndex: Int) {
|
||||
val targetInstruction = getInstruction<TwoRegisterInstruction>(iPutIndex)
|
||||
val targetReference = (targetInstruction as ReferenceInstruction).reference
|
||||
val register = getInstruction<TwoRegisterInstruction>(iPutIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
iPutIndex + 1, """
|
||||
invoke-static { v${targetInstruction.registerA} }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
|
||||
move-result v${targetInstruction.registerA}
|
||||
# Original instruction
|
||||
iput v${targetInstruction.registerA}, v${targetInstruction.registerB}, $targetReference
|
||||
"""
|
||||
addInstructionsAtControlFlowLabel(
|
||||
iPutIndex,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverrideType(I)I
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
removeInstruction(iPutIndex)
|
||||
}
|
||||
|
||||
fun Pair<String, Fingerprint>.hookInflatedView(
|
||||
fun MutableMethod.hookInflatedView(
|
||||
literalValue: Long,
|
||||
hookedClassType: String,
|
||||
extensionMethodName: String,
|
||||
) {
|
||||
methodOrThrow(miniplayerModernViewParentFingerprint).apply {
|
||||
val imageViewIndex = indexOfFirstInstructionOrThrow(
|
||||
indexOfFirstLiteralInstructionOrThrow(literalValue)
|
||||
) {
|
||||
opcode == Opcode.CHECK_CAST &&
|
||||
getReference<TypeReference>()?.type == hookedClassType
|
||||
}
|
||||
|
||||
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
|
||||
addInstruction(
|
||||
imageViewIndex + 1,
|
||||
"invoke-static { v$register }, $extensionMethodName"
|
||||
)
|
||||
val imageViewIndex = indexOfFirstInstructionOrThrow(
|
||||
indexOfFirstLiteralInstructionOrThrow(literalValue)
|
||||
) {
|
||||
opcode == Opcode.CHECK_CAST &&
|
||||
getReference<TypeReference>()?.type == hookedClassType
|
||||
}
|
||||
|
||||
val register = getInstruction<OneRegisterInstruction>(imageViewIndex).registerA
|
||||
addInstruction(
|
||||
imageViewIndex + 1,
|
||||
"invoke-static { v$register }, $extensionMethodName"
|
||||
)
|
||||
}
|
||||
|
||||
// Modern mini player is only present and functional in 19.15+.
|
||||
@ -184,7 +179,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
if (isPatchingOldVersion) {
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_LEGACY"
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_14"
|
||||
addPreference(settingArray, MINIPLAYER)
|
||||
|
||||
// Return here, as patch below is only intended for new versions of the app.
|
||||
@ -197,14 +192,14 @@ val miniplayerPatch = bytecodePatch(
|
||||
|
||||
miniplayerModernConstructorFingerprint.mutableClassOrThrow().methods.forEach {
|
||||
it.apply {
|
||||
if (MethodUtil.isConstructor(it)) {
|
||||
if (AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
|
||||
val iPutIndex = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.IPUT &&
|
||||
getReference<FieldReference>()?.type == "I"
|
||||
this.opcode == Opcode.IPUT &&
|
||||
this.getReference<FieldReference>()?.type == "I"
|
||||
}
|
||||
|
||||
insertModernMiniplayerTypeOverride(iPutIndex)
|
||||
} else if (isMultiConstructorMethod()) {
|
||||
} else {
|
||||
findReturnIndicesReversed().forEach { index ->
|
||||
insertModernMiniplayerOverride(
|
||||
index
|
||||
@ -214,27 +209,96 @@ val miniplayerPatch = bytecodePatch(
|
||||
}
|
||||
}
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
miniplayerModernEnabledFingerprint.injectLiteralInstructionBooleanCall(
|
||||
45622882L,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z"
|
||||
if (is_19_23_or_greater) {
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDragAndDrop(Z)Z"
|
||||
)
|
||||
settingArray += "SETTINGS: MINIPLAYER_DRAG_AND_DROP"
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Enable double tap action.
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
45628823L,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDoubleTapAction()Z"
|
||||
)
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
45630429L,
|
||||
MINIPLAYER_MODERN_FEATURE_LEGACY_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->getModernMiniplayerOverride(Z)Z"
|
||||
)
|
||||
settingArray += "SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION"
|
||||
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
MINIPLAYER_MODERN_FEATURE_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->getModernFeatureFlagsActiveOverride(Z)Z"
|
||||
)
|
||||
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
MINIPLAYER_DOUBLE_TAP_FEATURE_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDoubleTapAction(Z)Z"
|
||||
)
|
||||
|
||||
if (!is_19_29_or_greater) {
|
||||
settingArray += "SETTINGS: MINIPLAYER_DOUBLE_TAP_ACTION"
|
||||
}
|
||||
}
|
||||
|
||||
if (is_19_26_or_greater) {
|
||||
miniplayerModernConstructorFingerprint.methodOrThrow().apply {
|
||||
val literalIndex = indexOfFirstLiteralInstructionOrThrow(
|
||||
MINIPLAYER_INITIAL_SIZE_FEATURE_KEY,
|
||||
)
|
||||
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.LONG_TO_INT)
|
||||
|
||||
val register = getInstruction<OneRegisterInstruction>(targetIndex).registerA
|
||||
|
||||
addInstructions(
|
||||
targetIndex + 1,
|
||||
"""
|
||||
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setMiniplayerDefaultSize(I)I
|
||||
move-result v$register
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
// Override a minimum size constant.
|
||||
miniplayerMinimumSizeFingerprint.methodOrThrow().apply {
|
||||
val index = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.CONST_16 &&
|
||||
(this as NarrowLiteralInstruction).narrowLiteral == 192
|
||||
}
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
// Smaller sizes can be used, but the miniplayer will always start in size 170 if set any smaller.
|
||||
// The 170 initial limit probably could be patched to allow even smaller initial sizes,
|
||||
// but 170 is already half the horizontal space and smaller does not seem useful.
|
||||
replaceInstruction(index, "const/16 v$register, 170")
|
||||
}
|
||||
|
||||
settingArray += "SETTINGS: MINIPLAYER_WIDTH_DIP"
|
||||
} else {
|
||||
settingArray += "SETTINGS: MINIPLAYER_REWIND_FORWARD"
|
||||
}
|
||||
|
||||
if (is_19_36_or_greater) {
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->setRoundedCorners(Z)Z"
|
||||
)
|
||||
|
||||
settingArray += "SETTINGS: MINIPLAYER_ROUNDED_CONERS"
|
||||
}
|
||||
|
||||
if (is_19_43_or_greater) {
|
||||
miniplayerOnCloseHandlerFingerprint.injectLiteralInstructionBooleanCall(
|
||||
MINIPLAYER_DISABLED_FEATURE_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->getMiniplayerOnCloseHandler(Z)Z"
|
||||
)
|
||||
|
||||
miniplayerModernConstructorFingerprint.injectLiteralInstructionBooleanCall(
|
||||
MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->setHorizontalDrag(Z)Z"
|
||||
)
|
||||
|
||||
settingArray += "SETTINGS: MINIPLAYER_HORIZONTAL_DRAG"
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_43"
|
||||
} else {
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_19_16"
|
||||
}
|
||||
|
||||
// endregion
|
||||
@ -291,7 +355,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
"adjustMiniplayerOpacity"
|
||||
)
|
||||
).forEach { (fingerprint, literalValue, methodName) ->
|
||||
fingerprint.hookInflatedView(
|
||||
fingerprint.methodOrThrow(miniplayerModernViewParentFingerprint).hookInflatedView(
|
||||
literalValue,
|
||||
"Landroid/widget/ImageView;",
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->$methodName(Landroid/widget/ImageView;)V"
|
||||
@ -300,23 +364,19 @@ val miniplayerPatch = bytecodePatch(
|
||||
|
||||
miniplayerModernAddViewListenerFingerprint.methodOrThrow(
|
||||
miniplayerModernViewParentFingerprint
|
||||
).apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->hideMiniplayerSubTexts(Landroid/view/View;)Z
|
||||
move-result v0
|
||||
if-nez v0, :hidden
|
||||
""",
|
||||
ExternalLabel("hidden", getInstruction(implementation!!.instructions.lastIndex))
|
||||
)
|
||||
}
|
||||
|
||||
).addInstruction(
|
||||
0,
|
||||
"invoke-static { p1 }, $EXTENSION_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 extension.
|
||||
//
|
||||
// NOTE: Modern 2 uses the same video UI as the regular player except resized to smaller.
|
||||
// This patch code could be used to hide other player overlays that do not use Litho.
|
||||
youTubePlayerOverlaysLayoutFingerprint.matchOrThrow().let {
|
||||
it.method.apply {
|
||||
it.classDef.methods.add(
|
||||
@ -352,18 +412,6 @@ val miniplayerPatch = bytecodePatch(
|
||||
|
||||
// endregion
|
||||
|
||||
// region Enable drag and drop.
|
||||
|
||||
if (is_19_23_or_greater) {
|
||||
miniplayerModernDragAndDropFingerprint.injectLiteralInstructionBooleanCall(
|
||||
45628752L,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->enableMiniplayerDragAndDrop()Z"
|
||||
)
|
||||
settingArray += "SETTINGS: MINIPLAYER_DRAG_AND_DROP"
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
settingArray += "SETTINGS: MINIPLAYER_TYPE_MODERN"
|
||||
|
||||
// region add settings
|
||||
|
@ -1,17 +1,69 @@
|
||||
package app.revanced.patches.youtube.general.spoofappversion
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.shared.spoof.appversion.baseSpoofAppVersionPatch
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.youtube.utils.indexOfGetDrawableInstruction
|
||||
import app.revanced.patches.youtube.utils.patch.PatchList.SPOOF_APP_VERSION
|
||||
import app.revanced.patches.youtube.utils.playservice.is_18_34_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_18_39_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_18_49_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_23_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.utils.toolBarButtonFingerprint
|
||||
import app.revanced.util.appendAppVersion
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private val spoofAppVersionBytecodePatch = bytecodePatch(
|
||||
description = "spoofAppVersionBytecodePatch"
|
||||
) {
|
||||
|
||||
dependsOn(versionCheckPatch)
|
||||
|
||||
execute {
|
||||
if (!is_19_23_or_greater) {
|
||||
return@execute
|
||||
}
|
||||
|
||||
/**
|
||||
* When spoofing the app version to YouTube 19.20.xx or earlier via Spoof app version on YouTube 19.23.xx+, the Library tab will crash.
|
||||
* As a temporary workaround, do not set an image in the toolbar when the enum name is UNKNOWN.
|
||||
*/
|
||||
toolBarButtonFingerprint.methodOrThrow().apply {
|
||||
val getDrawableIndex = indexOfGetDrawableInstruction(this)
|
||||
val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) {
|
||||
opcode == Opcode.INVOKE_INTERFACE &&
|
||||
getReference<MethodReference>()?.returnType == "I"
|
||||
}
|
||||
val insertIndex = enumOrdinalIndex + 2
|
||||
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
|
||||
val jumpIndex = indexOfFirstInstructionOrThrow(insertIndex) {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
getReference<MethodReference>()?.name == "setImageDrawable"
|
||||
} + 1
|
||||
|
||||
addInstructionsWithLabels(
|
||||
insertIndex, """
|
||||
if-eqz v$insertRegister, :ignore
|
||||
""", ExternalLabel("ignore", getInstruction(jumpIndex))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
val spoofAppVersionPatch = resourcePatch(
|
||||
@ -22,6 +74,7 @@ val spoofAppVersionPatch = resourcePatch(
|
||||
|
||||
dependsOn(
|
||||
baseSpoofAppVersionPatch("$GENERAL_CLASS_DESCRIPTOR->getVersionOverride(Ljava/lang/String;)Ljava/lang/String;"),
|
||||
spoofAppVersionBytecodePatch,
|
||||
settingsPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
@ -297,6 +297,7 @@ val toolBarComponentsPatch = bytecodePatch(
|
||||
|
||||
// endregion
|
||||
|
||||
/*
|
||||
// region patch for hide voice search button
|
||||
|
||||
if (is_19_28_or_greater) {
|
||||
@ -311,6 +312,7 @@ val toolBarComponentsPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
// endregion
|
||||
*/
|
||||
|
||||
// region patch for hide voice search button
|
||||
|
||||
|
@ -5,6 +5,8 @@ import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.patch.PatchList.CUSTOM_BRANDING_ICON_FOR_YOUTUBE
|
||||
import app.revanced.patches.youtube.utils.playservice.is_19_34_or_greater
|
||||
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusIcon
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
@ -12,9 +14,16 @@ import app.revanced.util.Utils.trimIndentMultiline
|
||||
import app.revanced.util.copyFile
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.getAdaptiveIconResourceFile
|
||||
import app.revanced.util.getResourceGroup
|
||||
import app.revanced.util.underBarOrThrow
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
private const val ADAPTIVE_ICON_BACKGROUND_FILE_NAME =
|
||||
"adaptiveproduct_youtube_background_color_108"
|
||||
private const val ADAPTIVE_ICON_FOREGROUND_FILE_NAME =
|
||||
"adaptiveproduct_youtube_foreground_color_108"
|
||||
private const val DEFAULT_ICON = "revancify_blue"
|
||||
|
||||
private val availableIcon = mapOf(
|
||||
@ -39,8 +48,8 @@ private val drawableDirectories = sizeArray.map { "drawable-$it" }
|
||||
private val mipmapDirectories = sizeArray.map { "mipmap-$it" }
|
||||
|
||||
private val launcherIconResourceFileNames = arrayOf(
|
||||
"adaptiveproduct_youtube_background_color_108",
|
||||
"adaptiveproduct_youtube_foreground_color_108",
|
||||
ADAPTIVE_ICON_BACKGROUND_FILE_NAME,
|
||||
ADAPTIVE_ICON_FOREGROUND_FILE_NAME,
|
||||
"ic_launcher",
|
||||
"ic_launcher_round"
|
||||
).map { "$it.png" }.toTypedArray()
|
||||
@ -83,7 +92,10 @@ val customBrandingIconPatch = resourcePatch(
|
||||
) {
|
||||
compatibleWith(COMPATIBLE_PACKAGE)
|
||||
|
||||
dependsOn(settingsPatch)
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
|
||||
val appIconOption = stringOption(
|
||||
@ -181,5 +193,32 @@ val customBrandingIconPatch = resourcePatch(
|
||||
|
||||
updatePatchStatusIcon(appIcon)
|
||||
}
|
||||
|
||||
if (!is_19_34_or_greater) {
|
||||
return@execute
|
||||
}
|
||||
if (appIcon == "youtube") {
|
||||
return@execute
|
||||
}
|
||||
|
||||
mapOf(
|
||||
ADAPTIVE_ICON_BACKGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher.xml", "background"),
|
||||
ADAPTIVE_ICON_FOREGROUND_FILE_NAME to getAdaptiveIconResourceFile("res/mipmap-anydpi/ic_launcher.xml", "foreground")
|
||||
).forEach { (oldIconResourceFile, newIconResourceFile) ->
|
||||
if (oldIconResourceFile != newIconResourceFile) {
|
||||
mipmapDirectories.forEach {
|
||||
val mipmapDirectory = get("res").resolve(it)
|
||||
Files.copy(
|
||||
mipmapDirectory
|
||||
.resolve("$oldIconResourceFile.png")
|
||||
.toPath(),
|
||||
mipmapDirectory
|
||||
.resolve("$newIconResourceFile.png")
|
||||
.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -386,6 +386,8 @@ val playerComponentsPatch = bytecodePatch(
|
||||
findMethodOrThrow(syntheticReference) {
|
||||
name == "onClick"
|
||||
}.hookInitVideoPanel(0)
|
||||
} else {
|
||||
println("WARNING: target Opcode not found in ${fingerprint.first}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import app.revanced.patches.youtube.utils.resourceid.fadeDurationFast
|
||||
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarColorizedBarPlayedColorDark
|
||||
import app.revanced.patches.youtube.utils.resourceid.inlineTimeBarPlayedNotHighlightedColor
|
||||
import app.revanced.patches.youtube.utils.resourceid.insetOverlayViewLayout
|
||||
import app.revanced.patches.youtube.utils.resourceid.menuItemView
|
||||
import app.revanced.patches.youtube.utils.resourceid.scrimOverlay
|
||||
import app.revanced.patches.youtube.utils.resourceid.seekUndoEduOverlayStub
|
||||
import app.revanced.patches.youtube.utils.resourceid.totalTime
|
||||
@ -12,9 +13,13 @@ import app.revanced.patches.youtube.utils.resourceid.varispeedUnavailableTitle
|
||||
import app.revanced.patches.youtube.utils.resourceid.videoQualityBottomSheet
|
||||
import app.revanced.patches.youtube.utils.sponsorblock.sponsorBlockBytecodePatch
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal val engagementPanelBuilderFingerprint = legacyFingerprint(
|
||||
name = "engagementPanelBuilderFingerprint",
|
||||
@ -164,6 +169,23 @@ internal val seekbarOnDrawFingerprint = legacyFingerprint(
|
||||
customFingerprint = { method, _ -> method.name == "onDraw" }
|
||||
)
|
||||
|
||||
internal fun indexOfGetDrawableInstruction(method: Method) =
|
||||
method.indexOfFirstInstruction {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
getReference<MethodReference>()?.toString() == "Landroid/content/res/Resources;->getDrawable(I)Landroid/graphics/drawable/Drawable;"
|
||||
}
|
||||
|
||||
internal val toolBarButtonFingerprint = legacyFingerprint(
|
||||
name = "toolBarButtonFingerprint",
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Landroid/view/MenuItem;"),
|
||||
literals = listOf(menuItemView),
|
||||
customFingerprint = { method, _ ->
|
||||
indexOfGetDrawableInstruction(method) >= 0
|
||||
}
|
||||
)
|
||||
|
||||
internal val totalTimeFingerprint = legacyFingerprint(
|
||||
name = "totalTimeFingerprint",
|
||||
returnType = "V",
|
||||
|
@ -14,7 +14,8 @@ internal object Constants {
|
||||
"18.38.44", // This is the last version with no delay in applying video quality on the server side.
|
||||
"18.48.39", // This is the last version that do not use Rolling Number.
|
||||
"19.05.36", // This is the last version with the least YouTube experimental flag.
|
||||
"19.16.39", // This is the latest version supported by the RVX patch.
|
||||
"19.16.39", // This is the last version where the 'Restore old seekbar thumbnails' setting works.
|
||||
"19.34.42", // This is the latest version supported by the RVX patch.
|
||||
)
|
||||
)
|
||||
}
|
@ -134,8 +134,8 @@ private val playerControlsBytecodePatch = bytecodePatch(
|
||||
|
||||
changeVisibilityMethod =
|
||||
findMethodOrThrow(EXTENSION_PLAYER_CONTROLS_CLASS_DESCRIPTOR) {
|
||||
name == "changeVisibility"
|
||||
&& parameters == listOf("Z", "Z")
|
||||
name == "changeVisibility" &&
|
||||
parameters == listOf("Z", "Z")
|
||||
}
|
||||
|
||||
changeVisibilityNegatedImmediatelyMethod =
|
||||
|
@ -10,12 +10,6 @@ import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
internal fun indexOfLayoutDirectionInstruction(method: Method) =
|
||||
method.indexOfFirstInstruction {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
getReference<MethodReference>().toString() == "Landroid/view/View;->setLayoutDirection(I)V"
|
||||
}
|
||||
|
||||
internal val browseIdClassFingerprint = legacyFingerprint(
|
||||
name = "browseIdClassFingerprint",
|
||||
returnType = "Ljava/lang/Object;",
|
||||
@ -45,18 +39,26 @@ internal val reelWatchPagerFingerprint = legacyFingerprint(
|
||||
literals = listOf(reelWatchPlayer),
|
||||
)
|
||||
|
||||
internal fun indexOfStringIsEmptyInstruction(method: Method) =
|
||||
method.indexOfFirstInstruction {
|
||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||
getReference<MethodReference>().toString() == "Ljava/lang/String;->isEmpty()Z"
|
||||
}
|
||||
|
||||
internal val searchQueryClassFingerprint = legacyFingerprint(
|
||||
name = "searchQueryClassFingerprint",
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("L", "Ljava/util/Map;"),
|
||||
opcodes = listOf(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
),
|
||||
strings = listOf("force_enable_sticky_browsy_bars"),
|
||||
customFingerprint = { method, _ ->
|
||||
indexOfStringIsEmptyInstruction(method) >= 0
|
||||
}
|
||||
)
|
||||
|
||||
internal val videoStateFingerprint = legacyFingerprint(
|
||||
|
@ -135,32 +135,30 @@ val playerTypeHookPatch = bytecodePatch(
|
||||
|
||||
// region patch for hook search bar
|
||||
|
||||
searchQueryClassFingerprint.matchOrThrow().let {
|
||||
it.method.apply {
|
||||
val searchQueryIndex = it.patternMatch!!.startIndex + 1
|
||||
val searchQueryFieldReference = getInstruction<ReferenceInstruction>(searchQueryIndex).reference
|
||||
val searchQueryClass = (searchQueryFieldReference as FieldReference).definingClass
|
||||
searchQueryClassFingerprint.methodOrThrow().apply {
|
||||
val searchQueryIndex = indexOfStringIsEmptyInstruction(this) - 1
|
||||
val searchQueryFieldReference = getInstruction<ReferenceInstruction>(searchQueryIndex).reference
|
||||
val searchQueryClass = (searchQueryFieldReference as FieldReference).definingClass
|
||||
|
||||
findMethodOrThrow(searchQueryClass).apply {
|
||||
val smaliInstructions =
|
||||
findMethodOrThrow(searchQueryClass).apply {
|
||||
val smaliInstructions =
|
||||
"""
|
||||
if-eqz v0, :ignore
|
||||
iget-object v0, v0, $searchQueryFieldReference
|
||||
if-eqz v0, :ignore
|
||||
return-object v0
|
||||
:ignore
|
||||
const-string v0, ""
|
||||
return-object v0
|
||||
"""
|
||||
if-eqz v0, :ignore
|
||||
iget-object v0, v0, $searchQueryFieldReference
|
||||
if-eqz v0, :ignore
|
||||
return-object v0
|
||||
:ignore
|
||||
const-string v0, ""
|
||||
return-object v0
|
||||
"""
|
||||
|
||||
addStaticFieldToExtension(
|
||||
EXTENSION_ROOT_VIEW_HOOK_CLASS_DESCRIPTOR,
|
||||
"getSearchQuery",
|
||||
"searchQueryClass",
|
||||
definingClass,
|
||||
smaliInstructions
|
||||
)
|
||||
}
|
||||
addStaticFieldToExtension(
|
||||
EXTENSION_ROOT_VIEW_HOOK_CLASS_DESCRIPTOR,
|
||||
"getSearchQuery",
|
||||
"searchQueryClass",
|
||||
definingClass,
|
||||
smaliInstructions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,10 +23,22 @@ var is_19_23_or_greater = false
|
||||
private set
|
||||
var is_19_25_or_greater = false
|
||||
private set
|
||||
var is_19_26_or_greater = false
|
||||
private set
|
||||
var is_19_28_or_greater = false
|
||||
private set
|
||||
var is_19_29_or_greater = false
|
||||
private set
|
||||
var is_19_32_or_greater = false
|
||||
private set
|
||||
var is_19_34_or_greater = false
|
||||
private set
|
||||
var is_19_36_or_greater = false
|
||||
private set
|
||||
var is_19_41_or_greater = false
|
||||
private set
|
||||
var is_19_43_or_greater = false
|
||||
private set
|
||||
var is_19_44_or_greater = false
|
||||
private set
|
||||
|
||||
@ -53,8 +65,14 @@ val versionCheckPatch = resourcePatch(
|
||||
is_19_15_or_greater = 241602000 <= playStoreServicesVersion
|
||||
is_19_23_or_greater = 242402000 <= playStoreServicesVersion
|
||||
is_19_25_or_greater = 242599000 <= playStoreServicesVersion
|
||||
is_19_26_or_greater = 242705000 <= playStoreServicesVersion
|
||||
is_19_28_or_greater = 242905000 <= playStoreServicesVersion
|
||||
is_19_29_or_greater = 243005000 <= playStoreServicesVersion
|
||||
is_19_32_or_greater = 243305000 <= playStoreServicesVersion
|
||||
is_19_34_or_greater = 243499000 <= playStoreServicesVersion
|
||||
is_19_36_or_greater = 243705000 <= playStoreServicesVersion
|
||||
is_19_41_or_greater = 244305000 <= playStoreServicesVersion
|
||||
is_19_43_or_greater = 244405000 <= playStoreServicesVersion
|
||||
is_19_44_or_greater = 244505000 <= playStoreServicesVersion
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +120,8 @@ var menuItemView = -1L
|
||||
private set
|
||||
var metaPanel = -1L
|
||||
private set
|
||||
var miniplayerMaxSize = -1L
|
||||
private set
|
||||
var modernMiniPlayerClose = -1L
|
||||
private set
|
||||
var modernMiniPlayerExpand = -1L
|
||||
@ -444,6 +446,10 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
ID,
|
||||
"metapanel"
|
||||
]
|
||||
miniplayerMaxSize = resourceMappings[
|
||||
DIMEN,
|
||||
"miniplayer_max_size",
|
||||
]
|
||||
modernMiniPlayerClose = resourceMappings[
|
||||
ID,
|
||||
"modern_miniplayer_close"
|
||||
|
@ -1,26 +1,10 @@
|
||||
package app.revanced.patches.youtube.utils.toolbar
|
||||
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
|
||||
import app.revanced.patches.youtube.utils.resourceid.menuItemView
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val toolBarButtonFingerprint = legacyFingerprint(
|
||||
name = "toolBarButtonFingerprint",
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Landroid/view/MenuItem;"),
|
||||
opcodes = listOf(
|
||||
Opcode.INVOKE_INTERFACE,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.INVOKE_VIRTUAL
|
||||
),
|
||||
literals = listOf(menuItemView),
|
||||
)
|
||||
internal val toolBarPatchFingerprint = legacyFingerprint(
|
||||
name = "toolBarPatchFingerprint",
|
||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC,
|
||||
|
@ -6,12 +6,18 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
|
||||
import app.revanced.patches.youtube.utils.indexOfGetDrawableInstruction
|
||||
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
|
||||
import app.revanced.util.fingerprint.matchOrThrow
|
||||
import app.revanced.patches.youtube.utils.toolBarButtonFingerprint
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
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.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"$UTILS_PATH/ToolBarPatch;"
|
||||
@ -24,30 +30,35 @@ val toolBarHookPatch = bytecodePatch(
|
||||
dependsOn(sharedResourceIdPatch)
|
||||
|
||||
execute {
|
||||
toolBarButtonFingerprint.matchOrThrow().let {
|
||||
it.method.apply {
|
||||
val replaceIndex = it.patternMatch!!.startIndex
|
||||
val freeIndex = it.patternMatch!!.endIndex - 1
|
||||
|
||||
val replaceReference = getInstruction<ReferenceInstruction>(replaceIndex).reference
|
||||
val replaceRegister =
|
||||
getInstruction<FiveRegisterInstruction>(replaceIndex).registerC
|
||||
val enumRegister = getInstruction<FiveRegisterInstruction>(replaceIndex).registerD
|
||||
val freeRegister = getInstruction<TwoRegisterInstruction>(freeIndex).registerA
|
||||
|
||||
val imageViewIndex = replaceIndex + 2
|
||||
val imageViewReference =
|
||||
getInstruction<ReferenceInstruction>(imageViewIndex).reference
|
||||
|
||||
addInstructions(
|
||||
replaceIndex + 1, """
|
||||
iget-object v$freeRegister, p0, $imageViewReference
|
||||
invoke-static {v$enumRegister, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->hookToolBar(Ljava/lang/Enum;Landroid/widget/ImageView;)V
|
||||
invoke-interface {v$replaceRegister, v$enumRegister}, $replaceReference
|
||||
"""
|
||||
)
|
||||
removeInstruction(replaceIndex)
|
||||
toolBarButtonFingerprint.methodOrThrow().apply {
|
||||
val getDrawableIndex = indexOfGetDrawableInstruction(this)
|
||||
val enumOrdinalIndex = indexOfFirstInstructionReversedOrThrow(getDrawableIndex) {
|
||||
opcode == Opcode.INVOKE_INTERFACE &&
|
||||
getReference<MethodReference>()?.returnType == "I"
|
||||
}
|
||||
val freeIndex = getDrawableIndex - 1
|
||||
|
||||
val replaceReference = getInstruction<ReferenceInstruction>(enumOrdinalIndex).reference
|
||||
val replaceRegister =
|
||||
getInstruction<FiveRegisterInstruction>(enumOrdinalIndex).registerC
|
||||
val enumRegister = getInstruction<FiveRegisterInstruction>(enumOrdinalIndex).registerD
|
||||
val freeRegister = getInstruction<TwoRegisterInstruction>(freeIndex).registerA
|
||||
|
||||
val imageViewIndex = indexOfFirstInstructionReversedOrThrow(enumOrdinalIndex) {
|
||||
opcode == Opcode.IGET_OBJECT &&
|
||||
getReference<FieldReference>()?.type == "Landroid/widget/ImageView;"
|
||||
}
|
||||
val imageViewReference =
|
||||
getInstruction<ReferenceInstruction>(imageViewIndex).reference
|
||||
|
||||
addInstructions(
|
||||
enumOrdinalIndex + 1, """
|
||||
iget-object v$freeRegister, p0, $imageViewReference
|
||||
invoke-static {v$enumRegister, v$freeRegister}, $EXTENSION_CLASS_DESCRIPTOR->hookToolBar(Ljava/lang/Enum;Landroid/widget/ImageView;)V
|
||||
invoke-interface {v$replaceRegister, v$enumRegister}, $replaceReference
|
||||
"""
|
||||
)
|
||||
removeInstruction(enumOrdinalIndex)
|
||||
}
|
||||
|
||||
toolbarMethod = toolBarPatchFingerprint.methodOrThrow()
|
||||
|
@ -5,7 +5,6 @@ import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatchContext
|
||||
import app.revanced.patcher.util.Document
|
||||
import org.w3c.dom.Attr
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
@ -44,26 +43,6 @@ fun Node.cloneNodes(parent: Node) {
|
||||
parent.removeChild(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sequence for all child nodes.
|
||||
*/
|
||||
fun NodeList.asSequence() = (0 until this.length).asSequence().map { this.item(it) }
|
||||
|
||||
/**
|
||||
* Returns a sequence for all child nodes.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun Node.childElementsSequence() =
|
||||
this.childNodes.asSequence().filter { it.nodeType == Node.ELEMENT_NODE } as Sequence<Element>
|
||||
|
||||
/**
|
||||
* Performs the given [action] on each child element.
|
||||
*/
|
||||
inline fun Node.forEachChildElement(action: (Element) -> Unit) =
|
||||
childElementsSequence().forEach {
|
||||
action(it)
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively traverse the DOM tree starting from the given root node.
|
||||
*
|
||||
@ -74,20 +53,29 @@ fun Node.doRecursively(action: (Node) -> Unit) {
|
||||
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
|
||||
}
|
||||
|
||||
fun String.startsWithAny(vararg prefixes: String): Boolean {
|
||||
for (prefix in prefixes)
|
||||
if (this.startsWith(prefix))
|
||||
return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun List<String>.getResourceGroup(fileNames: Array<String>) = map { directory ->
|
||||
ResourceGroup(
|
||||
directory, *fileNames
|
||||
)
|
||||
}
|
||||
|
||||
fun ResourcePatchContext.getAdaptiveIconResourceFile(path: String, tag: String): String {
|
||||
document(path).use { document ->
|
||||
val adaptiveIcon = document
|
||||
.getElementsByTagName("adaptive-icon")
|
||||
.item(0) as Element
|
||||
|
||||
val childNodes = adaptiveIcon.childNodes
|
||||
for (i in 0 until childNodes.length) {
|
||||
val node = childNodes.item(i)
|
||||
if (node is Element && node.tagName == tag && node.hasAttribute("android:drawable")) {
|
||||
return node.getAttribute("android:drawable").split("/")[1]
|
||||
}
|
||||
}
|
||||
throw PatchException("Element not found: $tag")
|
||||
}
|
||||
}
|
||||
|
||||
fun ResourcePatchContext.appendAppVersion(appVersion: String) {
|
||||
addEntryValues(
|
||||
"revanced_spoof_app_version_target_entries",
|
||||
@ -227,14 +215,6 @@ fun ResourcePatchContext.removeStringsElements(
|
||||
}
|
||||
}
|
||||
|
||||
fun Node.insertFirst(node: Node) {
|
||||
if (hasChildNodes()) {
|
||||
insertBefore(node, firstChild)
|
||||
} else {
|
||||
appendChild(node)
|
||||
}
|
||||
}
|
||||
|
||||
fun Node.insertNode(tagName: String, targetNode: Node, block: Element.() -> Unit) {
|
||||
val child = ownerDocument.createElement(tagName)
|
||||
child.block()
|
||||
@ -294,21 +274,6 @@ internal fun inputStreamFromBundledResource(
|
||||
*/
|
||||
class ResourceGroup(val resourceDirectoryName: String, vararg val resources: String)
|
||||
|
||||
/**
|
||||
* Iterate through the children of a node by its tag.
|
||||
* @param resource The xml resource.
|
||||
* @param targetTag The target xml node.
|
||||
* @param callback The callback to call when iterating over the nodes.
|
||||
*/
|
||||
fun ResourcePatchContext.iterateXmlNodeChildren(
|
||||
resource: String,
|
||||
targetTag: String,
|
||||
callback: (node: Node) -> Unit,
|
||||
) = document(classLoader.getResourceAsStream(resource)!!).use { document ->
|
||||
val stringsNode = document.getElementsByTagName(targetTag).item(0).childNodes
|
||||
for (i in 1 until stringsNode.length - 1) callback(stringsNode.item(i))
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy resources from the current class loader to the resource directory.
|
||||
* @param resourceDirectory The directory of the resource.
|
||||
@ -383,12 +348,3 @@ internal fun NodeList.findElementByAttributeValue(attributeName: String, value:
|
||||
internal fun NodeList.findElementByAttributeValueOrThrow(attributeName: String, value: String) =
|
||||
findElementByAttributeValue(attributeName, value)
|
||||
?: throw PatchException("Could not find: $attributeName $value")
|
||||
|
||||
internal fun Element.copyAttributesFrom(oldContainer: Element) {
|
||||
// Copy attributes from the old element to the new element
|
||||
val attributes = oldContainer.attributes
|
||||
for (i in 0 until attributes.length) {
|
||||
val attr = attributes.item(i) as Attr
|
||||
setAttribute(attr.name, attr.value)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user