feat(YouTube/Overlay buttons): restore Tap and hold to toggle pause after repeat states features

This commit is contained in:
inotia00 2024-05-02 03:32:50 +09:00
parent a66f3eebae
commit 16ba530024
5 changed files with 113 additions and 19 deletions

View File

@ -5,32 +5,59 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.player.overlaybuttons.fingerprints.OfflineVideoEndpointFingerprint import app.revanced.patches.youtube.player.overlaybuttons.fingerprints.OfflineVideoEndpointFingerprint
import app.revanced.patches.youtube.player.overlaybuttons.fingerprints.PiPPlaybackFingerprint import app.revanced.patches.youtube.player.overlaybuttons.fingerprints.PiPPlaybackFingerprint
import app.revanced.patches.youtube.player.overlaybuttons.fingerprints.PlayerButtonConstructorFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH
import app.revanced.patches.youtube.utils.integrations.Constants.PATCH_STATUS_CLASS_DESCRIPTOR import app.revanced.patches.youtube.utils.integrations.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.integrations.Constants.UTILS_PATH
import app.revanced.patches.youtube.utils.mainactivity.MainActivityResolvePatch import app.revanced.patches.youtube.utils.mainactivity.MainActivityResolvePatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.util.addFieldAndInstructions
import app.revanced.util.getReference
import app.revanced.util.getTargetIndexWithReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.resultOrThrow import app.revanced.util.resultOrThrow
import app.revanced.util.updatePatchStatus import app.revanced.util.updatePatchStatus
import com.android.tools.smali.dexlib2.Opcode
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.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
@Patch(dependencies = [MainActivityResolvePatch::class]) @Patch(
dependencies = [
MainActivityResolvePatch::class,
SharedResourceIdPatch::class,
VideoInformationPatch::class
]
)
object OverlayButtonsBytecodePatch : BytecodePatch( object OverlayButtonsBytecodePatch : BytecodePatch(
setOf( setOf(
OfflineVideoEndpointFingerprint, OfflineVideoEndpointFingerprint,
PiPPlaybackFingerprint PiPPlaybackFingerprint,
PlayerButtonConstructorFingerprint
) )
) { ) {
private const val INTEGRATIONS_CLASS_DESCRIPTOR = private const val INTEGRATIONS_ALWAYS_REPEAT_CLASS_DESCRIPTOR =
"$UTILS_PATH/AlwaysRepeatPatch;"
private const val INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR =
"$INTEGRATIONS_PATH/utils/VideoUtils;" "$INTEGRATIONS_PATH/utils/VideoUtils;"
override fun execute(context: BytecodeContext) { override fun execute(context: BytecodeContext) {
// region patch for hook download button
OfflineVideoEndpointFingerprint.resultOrThrow().mutableMethod.apply { OfflineVideoEndpointFingerprint.resultOrThrow().mutableMethod.apply {
addInstructionsWithLabels( addInstructionsWithLabels(
0, """ 0, """
invoke-static/range {p3 .. p3}, $INTEGRATIONS_CLASS_DESCRIPTOR->inAppDownloadButtonOnClick(Ljava/lang/String;)Z invoke-static/range {p3 .. p3}, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->inAppDownloadButtonOnClick(Ljava/lang/String;)Z
move-result v0 move-result v0
if-eqz v0, :show_native_downloader if-eqz v0, :show_native_downloader
return-void return-void
@ -45,13 +72,75 @@ object OverlayButtonsBytecodePatch : BytecodePatch(
addInstructions( addInstructions(
insertIndex, """ insertIndex, """
invoke-static {v$insertRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->getExternalDownloaderLaunchedState(Z)Z invoke-static {v$insertRegister}, $INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR->getExternalDownloaderLaunchedState(Z)Z
move-result v$insertRegister move-result v$insertRegister
""" """
) )
} }
} }
// endregion
// region patch for always repeat and pause
PlayerButtonConstructorFingerprint.resultOrThrow().mutableMethod.apply {
val registerResolver = implementation!!.registerCount - parameters.size - 1 + 6 // p6
val invokerObjectIndex = indexOfFirstInstruction {
opcode == Opcode.IPUT_OBJECT
&& getReference<FieldReference>()?.definingClass == definingClass
&& (this as TwoRegisterInstruction).registerA == registerResolver
}
val invokerObjectReference = getInstruction<ReferenceInstruction>(invokerObjectIndex).reference
val onClickListenerReferenceIndex = getTargetIndexWithReference("<init>(Ljava/lang/Object;I[B)V")
val onClickListenerReference = getInstruction<ReferenceInstruction>(onClickListenerReferenceIndex).reference
val onClickListenerClass = context.findClass((onClickListenerReference as MethodReference).definingClass)!!.mutableClass
var invokeInterfaceReference = ""
onClickListenerClass.methods.find { method -> method.name == "onClick" }
?.apply {
val invokeInterfaceIndex = getTargetIndexWithReference(invokerObjectReference.toString()) + 1
if (getInstruction(invokeInterfaceIndex).opcode != Opcode.INVOKE_INTERFACE)
throw PatchException("Opcode does not match")
invokeInterfaceReference = getInstruction<ReferenceInstruction>(invokeInterfaceIndex).reference.toString()
} ?: throw PatchException("Could not find onClick method")
val alwaysRepeatMutableClass = context.findClass(INTEGRATIONS_ALWAYS_REPEAT_CLASS_DESCRIPTOR)!!.mutableClass
val smaliInstructions =
"""
if-eqz v0, :ignore
iget-object v1, v0, $invokerObjectReference
if-eqz v1, :ignore
invoke-interface {v1}, $invokeInterfaceReference
:ignore
return-void
"""
alwaysRepeatMutableClass.addFieldAndInstructions(
context,
"pauseVideo",
"pauseButtonClass",
definingClass,
smaliInstructions,
true
)
}
VideoInformationPatch.videoEndMethod.apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $INTEGRATIONS_ALWAYS_REPEAT_CLASS_DESCRIPTOR->alwaysRepeat()Z
move-result v0
if-eqz v0, :end
return-void
""", ExternalLabel("end", getInstruction(0))
)
}
// endregion
context.updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "OverlayButtons") context.updatePatchStatus(PATCH_STATUS_CLASS_DESCRIPTOR, "OverlayButtons")
} }

View File

@ -0,0 +1,12 @@
package app.revanced.patches.youtube.player.overlaybuttons.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.SingleLoopEduSnackBarText
import app.revanced.util.fingerprint.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object PlayerButtonConstructorFingerprint : LiteralValueFingerprint(
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
literalSupplier = { SingleLoopEduSnackBarText }
)

View File

@ -85,6 +85,7 @@ object SharedResourceIdPatch : ResourcePatch() {
var ScrimOverlay = -1L var ScrimOverlay = -1L
var Scrubbing = -1L var Scrubbing = -1L
var SeekUndoEduOverlayStub = -1L var SeekUndoEduOverlayStub = -1L
var SingleLoopEduSnackBarText = -1L
var SlidingDialogAnimation = -1L var SlidingDialogAnimation = -1L
var SubtitleMenuSettingsFooterInfo = -1L var SubtitleMenuSettingsFooterInfo = -1L
var SuggestedAction = -1L var SuggestedAction = -1L
@ -172,6 +173,7 @@ object SharedResourceIdPatch : ResourcePatch() {
ScrimOverlay = getId(ID, "scrim_overlay") ScrimOverlay = getId(ID, "scrim_overlay")
Scrubbing = getId(DIMEN, "vertical_touch_offset_to_enter_fine_scrubbing") Scrubbing = getId(DIMEN, "vertical_touch_offset_to_enter_fine_scrubbing")
SeekUndoEduOverlayStub = getId(ID, "seek_undo_edu_overlay_stub") SeekUndoEduOverlayStub = getId(ID, "seek_undo_edu_overlay_stub")
SingleLoopEduSnackBarText = getId(STRING, "single_loop_edu_snackbar_text")
SlidingDialogAnimation = getId(STYLE, "SlidingDialogAnimation") SlidingDialogAnimation = getId(STYLE, "SlidingDialogAnimation")
SubtitleMenuSettingsFooterInfo = getId(STRING, "subtitle_menu_settings_footer_info") SubtitleMenuSettingsFooterInfo = getId(STRING, "subtitle_menu_settings_footer_info")
SuggestedAction = getId(LAYOUT, "suggested_action") SuggestedAction = getId(LAYOUT, "suggested_action")

View File

@ -2,7 +2,6 @@ package app.revanced.patches.youtube.video.information
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.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.extensions.or import app.revanced.patcher.extensions.or
@ -12,7 +11,6 @@ import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patcher.util.smali.toInstructions import app.revanced.patcher.util.smali.toInstructions
import app.revanced.patches.youtube.utils.fingerprints.OrganicPlaybackContextModelFingerprint import app.revanced.patches.youtube.utils.fingerprints.OrganicPlaybackContextModelFingerprint
import app.revanced.patches.youtube.utils.fingerprints.VideoEndFingerprint import app.revanced.patches.youtube.utils.fingerprints.VideoEndFingerprint
@ -178,17 +176,6 @@ object VideoInformationPatch : BytecodePatch(
) )
videoEndMethod = getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex + 1) videoEndMethod = getWalkerMethod(context, it.scanResult.patternScanResult!!.startIndex + 1)
videoEndMethod.apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $INTEGRATIONS_CLASS_DESCRIPTOR->videoEnded()Z
move-result v0
if-eqz v0, :end
return-void
""", ExternalLabel("end", getInstruction(0))
)
}
} }
} }

View File

@ -9,6 +9,7 @@
<string name="revanced_extended_restart_message">Refresh and restart</string> <string name="revanced_extended_restart_message">Refresh and restart</string>
<string name="revanced_playback_speed_normal">Normal</string> <string name="revanced_playback_speed_normal">Normal</string>
<!-- PreferenceScreen: Ads --> <!-- PreferenceScreen: Ads -->
<string name="revanced_preference_screen_ads_title">Ads</string> <string name="revanced_preference_screen_ads_title">Ads</string>
@ -762,7 +763,8 @@ Limitation: Video title disappears when clicked."</string>
<!-- PreferenceScreen: Player, PreferenceCategory: Player, PreferenceScreen: Player buttons, PreferenceCategory: Overlay buttons --> <!-- PreferenceScreen: Player, PreferenceCategory: Player, PreferenceScreen: Player buttons, PreferenceCategory: Overlay buttons -->
<string name="revanced_preference_category_overlay_buttons">Overlay buttons</string> <string name="revanced_preference_category_overlay_buttons">Overlay buttons</string>
<string name="revanced_overlay_button_always_repeat_title">Show always repeat button</string> <string name="revanced_overlay_button_always_repeat_title">Show always repeat button</string>
<string name="revanced_overlay_button_always_repeat_summary">Tap to toggle always repeat states.</string> <string name="revanced_overlay_button_always_repeat_summary">"Tap to toggle always repeat states.
Tap and hold to toggle pause after repeat states."</string>
<string name="revanced_overlay_button_copy_video_url_title">Show copy video URL button</string> <string name="revanced_overlay_button_copy_video_url_title">Show copy video URL button</string>
<string name="revanced_overlay_button_copy_video_url_summary">"Tap to copy video URL. <string name="revanced_overlay_button_copy_video_url_summary">"Tap to copy video URL.
Tap and hold to copy video URL with timestamp."</string> Tap and hold to copy video URL with timestamp."</string>
@ -775,6 +777,8 @@ Tap and hold to copy video timestamp."</string>
<string name="revanced_overlay_button_speed_dialog_summary">"Tap to open speed dialog. <string name="revanced_overlay_button_speed_dialog_summary">"Tap to open speed dialog.
Tap and hold to set playback speed to 1.0x."</string> Tap and hold to set playback speed to 1.0x."</string>
<string name="revanced_overlay_button_speed_dialog_reset">Playback speed reseted (1.0x).</string> <string name="revanced_overlay_button_speed_dialog_reset">Playback speed reseted (1.0x).</string>
<string name="revanced_overlay_button_not_allowed_warning">Tap and hold to change button state.</string>
<string name="revanced_external_downloader_package_name_title">External downloader package name</string> <string name="revanced_external_downloader_package_name_title">External downloader package name</string>
<string name="revanced_external_downloader_package_name_summary">Package name of your installed external downloader app, such as NewPipe or YTDLnis.</string> <string name="revanced_external_downloader_package_name_summary">Package name of your installed external downloader app, such as NewPipe or YTDLnis.</string>
<string name="revanced_external_downloader_dialog_title">External downloader</string> <string name="revanced_external_downloader_dialog_title">External downloader</string>