diff --git a/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/fingerprints/SpeedOverlayHookFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/fingerprints/SpeedOverlayHookFingerprint.kt new file mode 100644 index 000000000..0a409a32b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/fingerprints/SpeedOverlayHookFingerprint.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.player.speedoverlay.fingerprints + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +object SpeedOverlayHookFingerprint : MethodFingerprint( + returnType = "V", + opcodes = listOf( + Opcode.CMPL_FLOAT, + Opcode.IF_GEZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL + ), + strings = listOf("Failed to easy seek haptics vibrate."), + customFingerprint = { methodDef, _ -> methodDef.name == "run" } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/fingerprints/YouTubeTextViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/fingerprints/YouTubeTextViewFingerprint.kt new file mode 100644 index 000000000..be6d57f2d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/fingerprints/YouTubeTextViewFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.player.speedoverlay.fingerprints + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import com.android.tools.smali.dexlib2.Opcode + +object YouTubeTextViewFingerprint : MethodFingerprint( + returnType = "V", + opcodes = listOf(Opcode.INVOKE_SUPER), + customFingerprint = { methodDef, _ -> + methodDef.definingClass.endsWith("/YouTubeTextView;") + && methodDef.name == "setText" + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/patch/HideSpeedOverlayPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/patch/HideSpeedOverlayPatch.kt deleted file mode 100644 index 7b0374698..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/patch/HideSpeedOverlayPatch.kt +++ /dev/null @@ -1,55 +0,0 @@ -package app.revanced.patches.youtube.player.speedoverlay.patch - -import app.revanced.extensions.exception -import app.revanced.patcher.annotation.Description -import app.revanced.patcher.annotation.Name -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.annotations.DependsOn -import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patches.youtube.player.speedoverlay.fingerprints.SpeedOverlayConfigFingerprint -import app.revanced.patches.youtube.utils.annotations.YouTubeCompatibility -import app.revanced.patches.youtube.utils.settings.resource.patch.SettingsPatch -import app.revanced.util.integrations.Constants.PLAYER -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - -@Patch -@Name("Hide speed overlay") -@Description("Hide speed overlay in player.") -@DependsOn([SettingsPatch::class]) -@YouTubeCompatibility -class HideSpeedOverlayPatch : BytecodePatch( - listOf(SpeedOverlayConfigFingerprint) -) { - override fun execute(context: BytecodeContext) { - - SpeedOverlayConfigFingerprint.result?.let { - it.mutableMethod.apply { - val insertIndex = implementation!!.instructions.size - 1 - val targetRegister = getInstruction(insertIndex).registerA - - addInstructions( - insertIndex, """ - invoke-static {v$targetRegister}, $PLAYER->hideSpeedOverlay(Z)Z - move-result v$targetRegister - """ - ) - } - } ?: throw SpeedOverlayConfigFingerprint.exception - - /** - * Add settings - */ - SettingsPatch.addPreference( - arrayOf( - "PREFERENCE: PLAYER_SETTINGS", - "SETTINGS: HIDE_SPEED_OVERLAY" - ) - ) - - SettingsPatch.updatePatchStatus("hide-speed-overlay") - - } -} diff --git a/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/patch/SpeedOverlayPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/patch/SpeedOverlayPatch.kt new file mode 100644 index 000000000..1d39e76ee --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/player/speedoverlay/patch/SpeedOverlayPatch.kt @@ -0,0 +1,111 @@ +package app.revanced.patches.youtube.player.speedoverlay.patch + +import app.revanced.extensions.exception +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Name +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotations.DependsOn +import app.revanced.patcher.patch.annotations.Patch +import app.revanced.patches.youtube.player.speedoverlay.fingerprints.SpeedOverlayConfigFingerprint +import app.revanced.patches.youtube.player.speedoverlay.fingerprints.SpeedOverlayHookFingerprint +import app.revanced.patches.youtube.player.speedoverlay.fingerprints.YouTubeTextViewFingerprint +import app.revanced.patches.youtube.utils.annotations.YouTubeCompatibility +import app.revanced.patches.youtube.utils.resourceid.patch.SharedResourceIdPatch +import app.revanced.patches.youtube.utils.resourceid.patch.SharedResourceIdPatch.Companion.SpeedOverlayText +import app.revanced.patches.youtube.utils.settings.resource.patch.SettingsPatch +import app.revanced.util.integrations.Constants.UTILS_PATH +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.formats.Instruction35c + +@Patch +@Name("Custom speed overlay") +@Description("Customize 'Play at 2x speed' while holding down.") +@DependsOn( + [ + SettingsPatch::class, + SharedResourceIdPatch::class + ] +) +@YouTubeCompatibility +class SpeedOverlayPatch : BytecodePatch( + listOf( + SpeedOverlayConfigFingerprint, + SpeedOverlayHookFingerprint, + YouTubeTextViewFingerprint + ) +) { + override fun execute(context: BytecodeContext) { + + SpeedOverlayConfigFingerprint.result?.let { + it.mutableMethod.apply { + val insertIndex = implementation!!.instructions.size - 1 + val targetRegister = getInstruction(insertIndex).registerA + + addInstructions( + insertIndex, """ + invoke-static {v$targetRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->disableSpeedOverlay(Z)Z + move-result v$targetRegister + """ + ) + } + } ?: throw SpeedOverlayConfigFingerprint.exception + + SpeedOverlayHookFingerprint.result?.let { + it.mutableMethod.apply { + val insertIndex = implementation!!.instructions.indexOfFirst { instruction -> + instruction.opcode == Opcode.CMPL_FLOAT + } + 3 + val insertRegister = getInstruction(insertIndex).registerD + + addInstructions( + insertIndex, """ + invoke-static {v$insertRegister}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSpeed(F)F + move-result v$insertRegister + """ + ) + } + } ?: throw SpeedOverlayHookFingerprint.exception + + YouTubeTextViewFingerprint.result?.let { + it.mutableMethod.apply { + val targetIndex = it.scanResult.patternScanResult!!.startIndex + val targetInstruction = getInstruction(targetIndex) + val targetReference = getInstruction(targetIndex).reference + + addInstructions( + targetIndex + 1, """ + const v0, $SpeedOverlayText + invoke-static {v${targetInstruction.registerC}, v${targetInstruction.registerD}, v0}, $INTEGRATIONS_CLASS_DESCRIPTOR->getSpeedText(Landroid/widget/TextView;Ljava/lang/CharSequence;I)Ljava/lang/CharSequence; + move-result-object v${targetInstruction.registerD} + invoke-super {v${targetInstruction.registerC}, v${targetInstruction.registerD}, v${targetInstruction.registerE}}, $targetReference + """ + ) + removeInstruction(targetIndex) + } + } ?: throw YouTubeTextViewFingerprint.exception + + /** + * Add settings + */ + SettingsPatch.addPreference( + arrayOf( + "PREFERENCE: PLAYER_SETTINGS", + "SETTINGS: CUSTOM_SPEED_OVERLAY" + ) + ) + + SettingsPatch.updatePatchStatus("custom-speed-overlay") + + } + + private companion object { + const val INTEGRATIONS_CLASS_DESCRIPTOR = + "$UTILS_PATH/SpeedOverlayPatch;" + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/patch/SharedResourceIdPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/patch/SharedResourceIdPatch.kt index c45981603..2c719a6c3 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/patch/SharedResourceIdPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/utils/resourceid/patch/SharedResourceIdPatch.kt @@ -73,6 +73,7 @@ class SharedResourceIdPatch : ResourcePatch { var ScrimOverlay: Long = -1 var Scrubbing: Long = -1 var SettingsBooleanTimeRangeDialog: Long = -1 + var SpeedOverlayText: Long = -1 var SubtitleMenuSettingsFooterInfo: Long = -1 var SuggestedAction: Long = -1 var ToolBarPaddingHome: Long = -1 @@ -154,6 +155,7 @@ class SharedResourceIdPatch : ResourcePatch { ScrimOverlay = find(ID, "scrim_overlay") Scrubbing = find(DIMEN, "vertical_touch_offset_to_enter_fine_scrubbing") SettingsBooleanTimeRangeDialog = find(LAYOUT, "setting_boolean_time_range_dialog") + SpeedOverlayText = find(ID, "speedmaster_edu_text") SubtitleMenuSettingsFooterInfo = find(STRING, "subtitle_menu_settings_footer_info") SuggestedAction = find(LAYOUT, "suggested_action") ToolBarPaddingHome = find(DIMEN, "toolbar_padding_home_action_up") diff --git a/src/main/resources/youtube/settings/host/values/strings.xml b/src/main/resources/youtube/settings/host/values/strings.xml index 9cbc28134..148d48f9a 100644 --- a/src/main/resources/youtube/settings/host/values/strings.xml +++ b/src/main/resources/youtube/settings/host/values/strings.xml @@ -81,6 +81,8 @@ Landscape mode when entering fullscreen is enabled Landscape mode when entering fullscreen is disabled Disable landscape mode + "Disable 'Playing at 2x speed' while holding down" + Disable speed overlay "Disable CronetEngine's QUIC protocol" Disable QUIC protocol Shorts player is enabled at app startup @@ -97,6 +99,9 @@ Custom seekbar color is disabled Custom seekbar color is enabled Enable custom seekbar color + "Edit speed that changes while holding down +Only available to some users who can use the speed overlay" + Edit custom speed overlay Debug logs are disabled Debug logs are enabled Enable debug logging @@ -503,9 +508,6 @@ Snack bar is shown Snack bar is hidden Hide snack bar - Speed overlay are shown - Speed overlay are hidden - Hide speed overlay Subscriptions button is shown Subscriptions button is hidden Hide subscriptions button @@ -628,6 +630,7 @@ Since these setting is quite outdated, it may not be valid" Shorts player Known issue: Title disappears when clicked Show fullscreen title + Playing at %sx speed "Spoofing the client version to the old version This will change the appearance of the app, but unknown side effects may occur diff --git a/src/main/resources/youtube/settings/xml/revanced_prefs.xml b/src/main/resources/youtube/settings/xml/revanced_prefs.xml index de9b2ea6f..0afa855c6 100644 --- a/src/main/resources/youtube/settings/xml/revanced_prefs.xml +++ b/src/main/resources/youtube/settings/xml/revanced_prefs.xml @@ -317,6 +317,7 @@ + @@ -325,7 +326,6 @@ - @@ -470,6 +470,10 @@ PREFERENCE: PLAYER_SETTINGS --> + + @@ -506,9 +510,6 @@ - -