diff --git a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/OpenChannelOfLiveAvatarPatch.java b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/OpenChannelOfLiveAvatarPatch.java index 7c6971af2..bce03fedf 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/OpenChannelOfLiveAvatarPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/youtube/patches/general/OpenChannelOfLiveAvatarPatch.java @@ -1,5 +1,9 @@ package app.revanced.extension.youtube.patches.general; +import androidx.annotation.Nullable; + +import java.util.concurrent.atomic.AtomicBoolean; + import app.revanced.extension.shared.utils.Logger; import app.revanced.extension.youtube.patches.general.requests.VideoDetailsRequest; import app.revanced.extension.youtube.settings.Settings; @@ -10,16 +14,16 @@ public final class OpenChannelOfLiveAvatarPatch { private static final boolean CHANGE_LIVE_RING_CLICK_ACTION = Settings.CHANGE_LIVE_RING_CLICK_ACTION.get(); - private static volatile String videoId = ""; - private static volatile boolean isCommentsPanelOpen = false; + private static final AtomicBoolean engagementPanelOpen = new AtomicBoolean(false); private static volatile boolean liveChannelAvatarClicked = false; + private static volatile String videoId = ""; - public static void commentsPanelClosed() { - isCommentsPanelOpen = false; + public static void showEngagementPanel(@Nullable Object object) { + engagementPanelOpen.set(object != null); } - public static void commentsPanelOpen() { - isCommentsPanelOpen = true; + public static void hideEngagementPanel() { + engagementPanelOpen.compareAndSet(true, false); } public static void liveChannelAvatarClicked() { @@ -34,13 +38,14 @@ public final class OpenChannelOfLiveAvatarPatch { if (!liveChannelAvatarClicked) { return false; } - if (isCommentsPanelOpen) { + if (engagementPanelOpen.get()) { return false; } VideoDetailsRequest request = VideoDetailsRequest.getRequestForVideoId(videoId); if (request != null) { String channelId = request.getInfo(); if (channelId != null) { + videoId = ""; liveChannelAvatarClicked = false; VideoUtils.openChannel(channelId); return true; @@ -60,7 +65,7 @@ public final class OpenChannelOfLiveAvatarPatch { if (!liveChannelAvatarClicked) { return; } - if (isCommentsPanelOpen) { + if (engagementPanelOpen.get()) { return; } if (newlyLoadedVideoId.isEmpty()) { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt index d24e75585..d4054ad43 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/FeedComponentsPatch.kt @@ -7,14 +7,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.litho.addLithoFilter import app.revanced.patches.shared.litho.emptyComponentLabel import app.revanced.patches.shared.mainactivity.onCreateMethod import app.revanced.patches.youtube.utils.bottomsheet.bottomSheetHookPatch import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE -import app.revanced.patches.youtube.utils.engagementPanelBuilderFingerprint +import app.revanced.patches.youtube.utils.engagement.engagementPanelHookPatch +import app.revanced.patches.youtube.utils.engagement.hookEngagementPanelState import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH import app.revanced.patches.youtube.utils.extension.Constants.FEED_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.extension.Constants.FEED_PATH @@ -41,19 +41,16 @@ import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.fingerprint.mutableClassOrThrow import app.revanced.util.getReference import app.revanced.util.getWalkerMethod -import app.revanced.util.indexOfFirstInstruction import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction 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.MethodReference import com.android.tools.smali.dexlib2.iface.reference.StringReference -import com.android.tools.smali.dexlib2.util.MethodUtil private const val CAROUSEL_SHELF_FILTER_CLASS_DESCRIPTOR = "$COMPONENTS_PATH/CarouselShelfFilter;" @@ -82,6 +79,7 @@ val feedComponentsPatch = bytecodePatch( sharedResourceIdPatch, settingsPatch, bottomSheetHookPatch, + engagementPanelHookPatch, versionCheckPatch, ) execute { @@ -176,38 +174,6 @@ val feedComponentsPatch = bytecodePatch( // region patch for hide relative video - fun Method.indexOfEngagementPanelBuilderInstruction(targetMethod: MutableMethod) = - indexOfFirstInstruction { - opcode == Opcode.INVOKE_DIRECT && - MethodUtil.methodSignaturesMatch( - targetMethod, - getReference()!! - ) - } - - engagementPanelBuilderFingerprint.matchOrThrow().let { - it.classDef.methods.filter { method -> - method.indexOfEngagementPanelBuilderInstruction(it.method) >= 0 - }.forEach { method -> - method.apply { - val index = indexOfEngagementPanelBuilderInstruction(it.method) - val register = getInstruction(index + 1).registerA - - addInstruction( - index + 2, - "invoke-static {v$register}, " + - "$RELATED_VIDEO_CLASS_DESCRIPTOR->showEngagementPanel(Ljava/lang/Object;)V" - ) - } - } - } - - engagementPanelUpdateFingerprint.methodOrThrow(engagementPanelBuilderFingerprint) - .addInstruction( - 0, - "invoke-static {}, $RELATED_VIDEO_CLASS_DESCRIPTOR->hideEngagementPanel()V" - ) - linearLayoutManagerItemCountsFingerprint.matchOrThrow().let { val methodWalker = it.getWalkerMethod(it.patternMatch!!.endIndex) @@ -224,6 +190,8 @@ val feedComponentsPatch = bytecodePatch( } } + hookEngagementPanelState(RELATED_VIDEO_CLASS_DESCRIPTOR) + // endregion // region patch for hide subscriptions channel section for tablet diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/Fingerprints.kt index f1e655d25..9ee3d8d9d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/feed/components/Fingerprints.kt @@ -11,12 +11,9 @@ import app.revanced.patches.youtube.utils.resourceid.filterBarHeight import app.revanced.patches.youtube.utils.resourceid.horizontalCardList import app.revanced.patches.youtube.utils.resourceid.relatedChipCloudMargin 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.reference.MethodReference internal val breakingNewsFingerprint = legacyFingerprint( name = "breakingNewsFingerprint", @@ -103,19 +100,6 @@ internal val elementParserParentFingerprint = legacyFingerprint( strings = listOf("Element tree missing id in debug mode.") ) -internal val engagementPanelUpdateFingerprint = legacyFingerprint( - name = "engagementPanelUpdateFingerprint", - returnType = "V", - accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, - parameters = listOf("L", "Z"), - customFingerprint = { method, _ -> - method.indexOfFirstInstruction { - opcode == Opcode.INVOKE_VIRTUAL && - getReference().toString() == "Ljava/util/ArrayDeque;->pop()Ljava/lang/Object;" - } >= 0 - } -) - internal val filterBarHeightFingerprint = legacyFingerprint( name = "filterBarHeightFingerprint", returnType = "V", diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/livering/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/livering/Fingerprints.kt index b77494757..873ef9df8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/livering/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/livering/Fingerprints.kt @@ -40,39 +40,3 @@ internal fun indexOfPlaybackStartDescriptorInstruction(method: Method) = reference?.returnType == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;" && reference.parameterTypes.isEmpty() } - -internal val engagementPanelCommentsClosedFingerprint = legacyFingerprint( - name = "engagementPanelCommentsClosedFingerprint", - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - parameters = emptyList(), - opcodes = listOf( - Opcode.IGET_OBJECT, - Opcode.INVOKE_STATIC, - Opcode.IGET_OBJECT, - Opcode.INVOKE_DIRECT, - ), - customFingerprint = { method, _ -> - method.indexOfFirstInstruction { - opcode == Opcode.INVOKE_INTERFACE && - getReference()?.name == "hasNext" - } >= 0 - } -) - -internal val engagementPanelCommentsOpenFingerprint = legacyFingerprint( - name = "engagementPanelCommentsOpenFingerprint", - returnType = "V", - accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, - parameters = listOf("L"), - opcodes = listOf( - Opcode.IGET_OBJECT, - Opcode.IF_NE, - Opcode.RETURN_VOID, - Opcode.IPUT_OBJECT, - Opcode.RETURN_VOID, - ), - customFingerprint = { method, _ -> - method.implementation!!.instructions.count() == 5 - } -) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/general/livering/OpenChannelOfLiveAvatarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/general/livering/OpenChannelOfLiveAvatarPatch.kt index 62e6ee695..7f7798982 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/general/livering/OpenChannelOfLiveAvatarPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/general/livering/OpenChannelOfLiveAvatarPatch.kt @@ -6,7 +6,8 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE -import app.revanced.patches.youtube.utils.engagementPanelTitleParentFingerprint +import app.revanced.patches.youtube.utils.engagement.engagementPanelHookPatch +import app.revanced.patches.youtube.utils.engagement.hookEngagementPanelState import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_PATH import app.revanced.patches.youtube.utils.patch.PatchList.CHANGE_LIVE_RING_CLICK_ACTION import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch @@ -31,30 +32,21 @@ val openChannelOfLiveAvatarPatch = bytecodePatch( compatibleWith(COMPATIBLE_PACKAGE) dependsOn( - playbackStartDescriptorPatch, - sharedResourceIdPatch, settingsPatch, + sharedResourceIdPatch, + playbackStartDescriptorPatch, + engagementPanelHookPatch, ) execute { - mapOf( - engagementPanelCommentsClosedFingerprint to "commentsPanelClosed", - engagementPanelCommentsOpenFingerprint to "commentsPanelOpen", - ).forEach { (fingerprint, methodName) -> - fingerprint - .methodOrThrow(engagementPanelTitleParentFingerprint) - .addInstruction( - 0, - "invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->$methodName()V" - ) - } - elementsImageFingerprint.methodOrThrow().addInstruction( 0, "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->liveChannelAvatarClicked()V" ) + hookEngagementPanelState(EXTENSION_CLASS_DESCRIPTOR) + clientSettingEndpointFingerprint.methodOrThrow().apply { val eqzIndex = indexOfFirstInstructionReversedOrThrow(Opcode.IF_EQZ) var freeIndex = indexOfFirstInstructionReversedOrThrow(eqzIndex, Opcode.NEW_INSTANCE) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/descriptions/DescriptionComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/descriptions/DescriptionComponentsPatch.kt index 80964a552..7cfda01cf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/player/descriptions/DescriptionComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/descriptions/DescriptionComponentsPatch.kt @@ -9,7 +9,6 @@ import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.shared.litho.addLithoFilter import app.revanced.patches.shared.litho.lithoFilterPatch import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE -import app.revanced.patches.youtube.utils.engagementPanelTitleParentFingerprint import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.patch.PatchList.DESCRIPTION_COMPONENTS diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/player/descriptions/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/player/descriptions/Fingerprints.kt index 94db27710..135a3a021 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/player/descriptions/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/player/descriptions/Fingerprints.kt @@ -16,6 +16,11 @@ internal val engagementPanelTitleFingerprint = legacyFingerprint( } ) +internal val engagementPanelTitleParentFingerprint = legacyFingerprint( + name = "engagementPanelTitleParentFingerprint", + strings = listOf("[EngagementPanelTitleHeader] Cannot remove action buttons from header as the child count is out of sync. Buttons to remove exceed current header child count.") +) + internal fun indexOfContentDescriptionInstruction(method: Method) = method.indexOfFirstInstructionReversed { opcode == Opcode.INVOKE_VIRTUAL && diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/Fingerprints.kt index 3ae137d26..89aa98555 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/Fingerprints.kt @@ -59,11 +59,6 @@ internal val engagementPanelBuilderFingerprint = legacyFingerprint( ) ) -internal val engagementPanelTitleParentFingerprint = legacyFingerprint( - name = "engagementPanelTitleParentFingerprint", - strings = listOf("[EngagementPanelTitleHeader] Cannot remove action buttons from header as the child count is out of sync. Buttons to remove exceed current header child count.") -) - internal val layoutConstructorFingerprint = legacyFingerprint( name = "layoutConstructorFingerprint", returnType = "V", diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/engagement/EngagementPanelHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/engagement/EngagementPanelHookPatch.kt new file mode 100644 index 000000000..e8607bd69 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/engagement/EngagementPanelHookPatch.kt @@ -0,0 +1,67 @@ +package app.revanced.patches.youtube.utils.engagement + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.youtube.utils.engagementPanelBuilderFingerprint +import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch +import app.revanced.util.fingerprint.matchOrThrow +import app.revanced.util.fingerprint.methodOrThrow +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionReversed +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil + +private lateinit var hideEngagementPanelMethod: MutableMethod +private var showEngagementPanelMethods = mutableListOf() + +val engagementPanelHookPatch = bytecodePatch( + description = "engagementPanelHookPatch" +) { + dependsOn(sharedResourceIdPatch) + + execute { + engagementPanelBuilderFingerprint.matchOrThrow().let { + it.classDef.methods.filter { method -> + method.indexOfEngagementPanelBuilderInstruction(it.method) >= 0 + }.forEach { method -> + showEngagementPanelMethods.add(method) + } + } + + hideEngagementPanelMethod = + engagementPanelUpdateFingerprint.methodOrThrow(engagementPanelBuilderFingerprint) + } +} + +private fun Method.indexOfEngagementPanelBuilderInstruction(targetMethod: MutableMethod) = + indexOfFirstInstructionReversed { + opcode == Opcode.INVOKE_DIRECT && + MethodUtil.methodSignaturesMatch( + targetMethod, + getReference()!! + ) + } + +internal fun hookEngagementPanelState(classDescriptor: String) { + showEngagementPanelMethods.forEach { method -> + method.apply { + val index = indexOfEngagementPanelBuilderInstruction(this) + val register = getInstruction(index + 1).registerA + + addInstruction( + index + 2, + "invoke-static {v$register}, $classDescriptor->showEngagementPanel(Ljava/lang/Object;)V" + ) + } + } + + hideEngagementPanelMethod.addInstruction( + 0, + "invoke-static {}, $classDescriptor->hideEngagementPanel()V" + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/utils/engagement/Fingerprints .kt b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/engagement/Fingerprints .kt new file mode 100644 index 000000000..855f89fd8 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/utils/engagement/Fingerprints .kt @@ -0,0 +1,22 @@ +package app.revanced.patches.youtube.utils.engagement + +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.reference.MethodReference + +internal val engagementPanelUpdateFingerprint = legacyFingerprint( + name = "engagementPanelUpdateFingerprint", + returnType = "V", + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + parameters = listOf("L", "Z"), + customFingerprint = { method, _ -> + method.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && + getReference().toString() == "Ljava/util/ArrayDeque;->pop()Ljava/lang/Object;" + } >= 0 + } +)