feat(YouTube - Shorts components): add Custom actions setting (YouTube 19.05.36+)

This commit is contained in:
inotia00
2024-12-15 15:03:05 +09:00
parent 169dc9acba
commit dad6b3d5e4
11 changed files with 747 additions and 74 deletions

View File

@ -5,16 +5,17 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.youtube.utils.bottomSheetMenuItemBuilderFingerprint
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.FEED_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.indexOfSpannedCharSequenceInstruction
import app.revanced.patches.youtube.utils.patch.PatchList.HIDE_FEED_FLYOUT_MENU
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.fingerprint.matchOrNull
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
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.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@ -33,27 +34,16 @@ val feedFlyoutMenuPatch = bytecodePatch(
// region patch for phone
val bottomSheetMenuItemBuilderMatch =
bottomSheetMenuItemBuilderLegacyFingerprint.matchOrNull()
?: bottomSheetMenuItemBuilderFingerprint.matchOrThrow()
bottomSheetMenuItemBuilderFingerprint.methodOrThrow().apply {
val insertIndex = indexOfSpannedCharSequenceInstruction(this) + 2
val insertRegister = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
bottomSheetMenuItemBuilderMatch.let {
it.method.apply {
val targetIndex = it.patternMatch!!.endIndex
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
val targetParameter =
getInstruction<ReferenceInstruction>(targetIndex - 1).reference
if (!targetParameter.toString().endsWith("Ljava/lang/CharSequence;"))
throw PatchException("Method signature parameter did not match: $targetParameter")
addInstructions(
targetIndex + 1, """
invoke-static {v$targetRegister}, $FEED_CLASS_DESCRIPTOR->hideFlyoutMenu(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$targetRegister
"""
)
}
addInstructions(
insertIndex, """
invoke-static {v$insertRegister}, $FEED_CLASS_DESCRIPTOR->hideFlyoutMenu(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
move-result-object v$insertRegister
"""
)
}
// endregion

View File

@ -6,39 +6,6 @@ import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
/**
* Compatible with YouTube v19.11.43~
*/
internal val bottomSheetMenuItemBuilderFingerprint = legacyFingerprint(
name = "bottomSheetMenuItemBuilderFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "L",
parameters = listOf("L"),
opcodes = listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT
),
strings = listOf("Text missing for BottomSheetMenuItem with iconType: ")
)
/**
* Compatible with ~YouTube v19.10.39
*/
internal val bottomSheetMenuItemBuilderLegacyFingerprint = legacyFingerprint(
name = "bottomSheetMenuItemBuilderLegacyFingerprint",
returnType = "L",
parameters = listOf("L"),
opcodes = listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT
),
strings = listOf("ElementTransformer, ElementPresenter and InteractionLogger cannot be null")
)
internal val contextualMenuItemBuilderFingerprint = legacyFingerprint(
name = "contextualMenuItemBuilderFingerprint",
returnType = "V",

View File

@ -1,7 +1,6 @@
package app.revanced.patches.youtube.shorts.components
import app.revanced.patches.youtube.utils.resourceid.badgeLabel
import app.revanced.patches.youtube.utils.resourceid.bottomBarContainer
import app.revanced.patches.youtube.utils.resourceid.metaPanel
import app.revanced.patches.youtube.utils.resourceid.reelDynRemix
import app.revanced.patches.youtube.utils.resourceid.reelDynShare
@ -19,14 +18,18 @@ import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
// multiFingerprint
internal val bottomBarContainerHeightFingerprint = legacyFingerprint(
name = "bottomBarContainerHeightFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("Landroid/view/View;", "Landroid/os/Bundle;"),
strings = listOf("r_pfvc"),
literals = listOf(bottomBarContainer),
internal val bottomSheetMenuListBuilderFingerprint = legacyFingerprint(
name = "bottomSheetMenuListBuilderFingerprint",
returnType = "L",
accessFlags = AccessFlags.PROTECTED or AccessFlags.FINAL,
parameters = emptyList(),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
),
strings = listOf("Bottom Sheet Menu is empty. No menu items were supported."),
)
internal val reelEnumConstructorFingerprint = legacyFingerprint(

View File

@ -6,6 +6,7 @@ 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.removeInstructions
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
@ -14,11 +15,13 @@ import app.revanced.patches.shared.litho.addLithoFilter
import app.revanced.patches.shared.litho.lithoFilterPatch
import app.revanced.patches.shared.textcomponent.hookSpannableString
import app.revanced.patches.shared.textcomponent.textComponentPatch
import app.revanced.patches.youtube.utils.bottomSheetMenuItemBuilderFingerprint
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH
import app.revanced.patches.youtube.utils.extension.Constants.SHORTS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.extension.Constants.SHORTS_PATH
import app.revanced.patches.youtube.utils.extension.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.indexOfSpannedCharSequenceInstruction
import app.revanced.patches.youtube.utils.lottie.LOTTIE_ANIMATION_VIEW_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.lottie.lottieAnimationViewHookPatch
import app.revanced.patches.youtube.utils.navigation.addBottomBarContainerHook
@ -26,9 +29,12 @@ import app.revanced.patches.youtube.utils.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.utils.patch.PatchList.SHORTS_COMPONENTS
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.playservice.is_18_31_or_greater
import app.revanced.patches.youtube.utils.playservice.is_18_49_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_28_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
import app.revanced.patches.youtube.utils.recyclerview.bottomSheetRecyclerViewHook
import app.revanced.patches.youtube.utils.recyclerview.bottomSheetRecyclerViewPatch
import app.revanced.patches.youtube.utils.resourceid.bottomBarContainer
import app.revanced.patches.youtube.utils.resourceid.metaPanel
import app.revanced.patches.youtube.utils.resourceid.reelDynRemix
@ -49,9 +55,13 @@ import app.revanced.patches.youtube.utils.settings.ResourceUtils.getContext
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.patches.youtube.video.information.hookShortsVideoInformation
import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.patches.youtube.video.videoid.hookPlayerResponseVideoId
import app.revanced.patches.youtube.video.videoid.videoIdPatch
import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
import app.revanced.util.ResourceGroup
import app.revanced.util.cloneMutable
import app.revanced.util.copyResources
import app.revanced.util.findMethodOrThrow
import app.revanced.util.findMutableMethodOf
import app.revanced.util.fingerprint.injectLiteralInstructionBooleanCall
import app.revanced.util.fingerprint.matchOrThrow
@ -73,6 +83,7 @@ import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
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.RegisterRangeInstruction
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
@ -130,6 +141,153 @@ private val shortsAnimationPatch = bytecodePatch(
}
}
private const val SHORTS_PLAYER_FLYOUT_MENU_FILTER_CLASS_DESCRIPTOR =
"$COMPONENTS_PATH/ShortsCustomActionsFilter;"
private const val EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR =
"$SHORTS_PATH/CustomActionsPatch;"
private val shortsCustomActionsPatch = bytecodePatch(
description = "shortsCustomActionsPatch"
) {
dependsOn(
bottomSheetRecyclerViewPatch,
lithoFilterPatch,
playerTypeHookPatch,
videoIdPatch,
videoInformationPatch,
versionCheckPatch,
)
execute {
if (!is_18_49_or_greater) {
return@execute
}
bottomSheetMenuListBuilderFingerprint.matchOrThrow().let {
it.method.apply {
val addListIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "add"
}
val addListReference = getInstruction<ReferenceInstruction>(addListIndex).reference
val getObjectIndex = indexOfFirstInstructionReversedOrThrow(addListIndex) {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.returnType == "Ljava/lang/Object;"
}
val getObjectReference = getInstruction<ReferenceInstruction>(getObjectIndex).reference as MethodReference
val bottomSheetMenuInitializeIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_STATIC_RANGE &&
reference?.returnType == "V" &&
reference.parameterTypes[1] == "Ljava/lang/Object;"
}
val bottomSheetMenuObjectRegister = getInstruction<RegisterRangeInstruction>(bottomSheetMenuInitializeIndex).startRegister
val bottomSheetMenuObject = (getInstruction<ReferenceInstruction>(bottomSheetMenuInitializeIndex).reference as MethodReference).parameterTypes[0]!!
val bottomSheetMenuListIndex = it.patternMatch!!.startIndex
val bottomSheetMenuListField = (getInstruction<ReferenceInstruction>(bottomSheetMenuListIndex).reference as FieldReference)
val bottomSheetMenuClass = bottomSheetMenuListField.definingClass
val bottomSheetMenuList = bottomSheetMenuListField.type
val bottomSheetMenuClassRegister = getInstruction<TwoRegisterInstruction>(bottomSheetMenuListIndex).registerB
val bottomSheetMenuListRegister = getInstruction<TwoRegisterInstruction>(bottomSheetMenuListIndex).registerA
addInstruction(
bottomSheetMenuListIndex + 1,
"invoke-static {v$bottomSheetMenuClassRegister, v$bottomSheetMenuListRegister}, " +
"$EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->addFlyoutMenu(Ljava/lang/Object;Ljava/lang/Object;)V"
)
addInstruction(
bottomSheetMenuInitializeIndex + 1,
"invoke-static {v$bottomSheetMenuObjectRegister}, " +
"$EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->setFlyoutMenuObject(Ljava/lang/Object;)V"
)
val addFlyoutMenuMethod = findMethodOrThrow(EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR) {
name == "addFlyoutMenu" &&
accessFlags == AccessFlags.PRIVATE or AccessFlags.STATIC
}
val customActionClass = with(addFlyoutMenuMethod) {
val thirdParameter = parameters[2]
addInstructions(
3, """
check-cast p0, $bottomSheetMenuClass
check-cast v0, $bottomSheetMenuObject
invoke-virtual {p0, v0, p2}, $bottomSheetMenuClass->buildFlyoutMenu(${bottomSheetMenuObject}${thirdParameter})${getObjectReference.definingClass}
move-result-object v0
invoke-virtual {v0}, $getObjectReference
move-result-object v0
check-cast p1, $bottomSheetMenuList
invoke-virtual {p1, v0}, $addListReference
return-void
"""
)
thirdParameter
}
val bottomSheetMenuItemBuilderMethod = bottomSheetMenuItemBuilderFingerprint
.methodOrThrow()
val newParameter = bottomSheetMenuItemBuilderMethod.parameters + listOf(customActionClass)
it.classDef.methods.add(
bottomSheetMenuItemBuilderMethod
.cloneMutable(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
name = "buildFlyoutMenu",
registerCount = bottomSheetMenuItemBuilderMethod.implementation!!.registerCount + 1,
parameters = newParameter,
).apply {
val drawableIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_DIRECT &&
getReference<MethodReference>()?.returnType == "Landroid/graphics/drawable/Drawable;"
}
val drawableRegister = getInstruction<OneRegisterInstruction>(drawableIndex + 1).registerA
addInstructions(
drawableIndex + 2, """
invoke-virtual {p2}, $customActionClass->getDrawable()Landroid/graphics/drawable/Drawable;
move-result-object v$drawableRegister
"""
)
val charSequenceIndex = indexOfSpannedCharSequenceInstruction(this)
val charSequenceRegister = getInstruction<OneRegisterInstruction>(charSequenceIndex + 1).registerA
val insertIndex = charSequenceIndex + 2
if (getInstruction<ReferenceInstruction>(insertIndex).reference.toString().startsWith("Lapp/revanced")) {
removeInstructions(insertIndex, 2)
}
addInstructions(
insertIndex, """
invoke-virtual {p2}, $customActionClass->getLabel()Ljava/lang/String;
move-result-object v$charSequenceRegister
"""
)
}
)
}
}
bottomSheetRecyclerViewHook("$EXTENSION_CUSTOM_ACTIONS_CLASS_DESCRIPTOR->onFlyoutMenuCreate(Landroid/support/v7/widget/RecyclerView;)V")
hookPlayerResponseVideoId("$SHORTS_PLAYER_FLYOUT_MENU_FILTER_CLASS_DESCRIPTOR->newPlayerResponseVideoId(Ljava/lang/String;Z)V")
hookShortsVideoInformation("$SHORTS_PLAYER_FLYOUT_MENU_FILTER_CLASS_DESCRIPTOR->newShortsVideoStarted(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JZ)V")
addLithoFilter(SHORTS_PLAYER_FLYOUT_MENU_FILTER_CLASS_DESCRIPTOR)
}
}
private val shortsNavigationBarPatch = bytecodePatch(
description = "shortsNavigationBarPatch"
) {
@ -344,6 +502,7 @@ val shortsComponentPatch = bytecodePatch(
dependsOn(
shortsAnimationPatch,
shortsCustomActionsPatch,
shortsNavigationBarPatch,
shortsRepeatPatch,
shortsTimeStampPatch,
@ -412,6 +571,10 @@ val shortsComponentPatch = bytecodePatch(
settingArray += "SETTINGS: SHORTS_TIME_STAMP"
}
if (is_18_49_or_greater) {
settingArray += "SETTINGS: SHORTS_CUSTOM_ACTIONS"
}
// region patch for hide comments button (non-litho)
shortsButtonFingerprint.hideButton(rightComment, "hideShortsCommentsButton", false)

View File

@ -21,6 +21,31 @@ 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 bottomSheetMenuItemBuilderFingerprint = legacyFingerprint(
name = "bottomSheetMenuItemBuilderFingerprint",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "L",
parameters = listOf("L"),
opcodes = listOf(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT
),
strings = listOf("Text missing for BottomSheetMenuItem."),
customFingerprint = { method, _ ->
indexOfSpannedCharSequenceInstruction(method) >= 0
}
)
fun indexOfSpannedCharSequenceInstruction(method: Method) =
method.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_STATIC &&
reference?.parameterTypes?.size == 1 &&
reference.returnType == "Ljava/lang/CharSequence;"
}
internal val engagementPanelBuilderFingerprint = legacyFingerprint(
name = "engagementPanelBuilderFingerprint",
returnType = "L",