feat(YouTube - Fullscreen components): Add Exit fullscreen mode setting

This commit is contained in:
inotia00
2025-01-03 21:52:52 +09:00
parent a596959d71
commit 428047b725
9 changed files with 273 additions and 0 deletions

View File

@ -15,9 +15,12 @@ import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PAC
import app.revanced.patches.youtube.utils.extension.Constants.COMPONENTS_PATH
import app.revanced.patches.youtube.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.extension.Constants.PLAYER_PATH
import app.revanced.patches.youtube.utils.fullscreen.fullscreenButtonHookPatch
import app.revanced.patches.youtube.utils.layoutConstructorFingerprint
import app.revanced.patches.youtube.utils.mainactivity.mainActivityResolvePatch
import app.revanced.patches.youtube.utils.patch.PatchList.FULLSCREEN_COMPONENTS
import app.revanced.patches.youtube.utils.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.utils.playservice.is_18_42_or_greater
import app.revanced.patches.youtube.utils.playservice.is_19_41_or_greater
import app.revanced.patches.youtube.utils.playservice.versionCheckPatch
@ -28,7 +31,10 @@ 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.patches.youtube.utils.youtubeControlsOverlayFingerprint
import app.revanced.patches.youtube.video.information.videoEndMethod
import app.revanced.patches.youtube.video.information.videoInformationPatch
import app.revanced.util.Utils.printWarn
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.fingerprint.mutableClassOrThrow
@ -48,6 +54,9 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val FILTER_CLASS_DESCRIPTOR =
"$COMPONENTS_PATH/QuickActionFilter;"
private const val EXTENSION_EXIT_FULLSCREEN_CLASS_DESCRIPTOR =
"$PLAYER_PATH/ExitFullscreenPatch;"
@Suppress("unused")
val fullscreenComponentsPatch = bytecodePatch(
FULLSCREEN_COMPONENTS.title,
@ -57,8 +66,11 @@ val fullscreenComponentsPatch = bytecodePatch(
dependsOn(
settingsPatch,
playerTypeHookPatch,
lithoFilterPatch,
mainActivityResolvePatch,
fullscreenButtonHookPatch,
videoInformationPatch,
sharedResourceIdPatch,
versionCheckPatch,
)
@ -106,6 +118,17 @@ val fullscreenComponentsPatch = bytecodePatch(
// endregion
// region patch for exit fullscreen
videoEndMethod.apply {
addInstructionsAtControlFlowLabel(
implementation!!.instructions.lastIndex,
"invoke-static {}, $EXTENSION_EXIT_FULLSCREEN_CLASS_DESCRIPTOR->endOfVideoReached()V",
)
}
// endregion
// region patch for hide autoplay preview
layoutConstructorFingerprint.methodOrThrow().apply {

View File

@ -0,0 +1,35 @@
package app.revanced.patches.youtube.utils.fullscreen
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
private const val NEXT_GEN_WATCH_LAYOUT_CLASS_DESCRIPTOR =
"Lcom/google/android/apps/youtube/app/watch/nextgenwatch/ui/NextGenWatchLayout;"
internal val nextGenWatchLayoutConstructorFingerprint = legacyFingerprint(
name = "nextGenWatchLayoutConstructorFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
parameters = listOf("Landroid/content/Context;", "Landroid/util/AttributeSet;", "I"),
opcodes = listOf(Opcode.CHECK_CAST),
customFingerprint = { method, _ ->
method.definingClass == NEXT_GEN_WATCH_LAYOUT_CLASS_DESCRIPTOR
},
)
internal val nextGenWatchLayoutFullscreenModeFingerprint = legacyFingerprint(
name = "nextGenWatchLayoutFullscreenModeFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("I"),
opcodes = listOf(Opcode.INVOKE_DIRECT),
customFingerprint = { method, _ ->
method.definingClass == NEXT_GEN_WATCH_LAYOUT_CLASS_DESCRIPTOR &&
method.containsLiteralInstruction(32)
},
)

View File

@ -0,0 +1,135 @@
package app.revanced.patches.youtube.utils.fullscreen
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
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.patches.youtube.utils.extension.Constants.EXTENSION_PATH
import app.revanced.patches.youtube.utils.extension.sharedExtensionPatch
import app.revanced.util.addStaticFieldToExtension
import app.revanced.util.findMethodOrThrow
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.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
private const val EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR =
"$EXTENSION_PATH/utils/VideoUtils;"
internal lateinit var enterFullscreenMethod: MutableMethod
val fullscreenButtonHookPatch = bytecodePatch(
description = "fullscreenButtonHookPatch"
) {
dependsOn(sharedExtensionPatch)
execute {
val (referenceClass, fullscreenActionClass) = with (nextGenWatchLayoutFullscreenModeFingerprint.methodOrThrow()) {
val targetIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.INVOKE_DIRECT &&
getReference<MethodReference>()?.parameterTypes?.size == 2
}
val targetReference = getInstruction<ReferenceInstruction>(targetIndex).reference as MethodReference
Pair(targetReference.definingClass, targetReference.parameterTypes[1].toString())
}
val (enterFullscreenReference, exitFullscreenReference, opcodeName) =
with (findMethodOrThrow(referenceClass) { parameters == listOf("I") }) {
val enterFullscreenIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.returnType == "V" &&
reference.definingClass == fullscreenActionClass &&
reference.parameterTypes.size == 0
}
val exitFullscreenIndex = indexOfFirstInstructionReversedOrThrow {
val reference = getReference<MethodReference>()
reference?.returnType == "V" &&
reference.definingClass == fullscreenActionClass &&
reference.parameterTypes.size == 0
}
val enterFullscreenReference =
getInstruction<ReferenceInstruction>(enterFullscreenIndex).reference
val exitFullscreenReference =
getInstruction<ReferenceInstruction>(exitFullscreenIndex).reference
val opcode = getInstruction(enterFullscreenIndex).opcode
val enterFullscreenClass = (enterFullscreenReference as MethodReference).definingClass
enterFullscreenMethod = if (opcode == Opcode.INVOKE_INTERFACE) {
classes.find { classDef -> classDef.interfaces.contains(enterFullscreenClass) }
?.let { classDef ->
proxy(classDef)
.mutableClass
.methods
.find { method -> method.name == enterFullscreenReference.name }
} ?: throw PatchException("No matching classes: $enterFullscreenClass")
} else {
findMethodOrThrow(enterFullscreenClass) {
name == enterFullscreenReference.name
}
}
Triple(
enterFullscreenReference,
exitFullscreenReference,
opcode.name
)
}
nextGenWatchLayoutConstructorFingerprint.methodOrThrow().apply {
val targetIndex = indexOfFirstInstructionReversedOrThrow {
opcode == Opcode.CHECK_CAST &&
getReference<TypeReference>()?.type == fullscreenActionClass
}
val targetRegister = getInstruction<OneRegisterInstruction>(targetIndex).registerA
addInstruction(
targetIndex + 1,
"sput-object v$targetRegister, $EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR->fullscreenActionClass:$fullscreenActionClass"
)
val enterFullscreenModeSmaliInstructions =
"""
if-eqz v0, :ignore
$opcodeName {v0}, $enterFullscreenReference
:ignore
return-void
"""
val exitFullscreenModeSmaliInstructions =
"""
if-eqz v0, :ignore
$opcodeName {v0}, $exitFullscreenReference
:ignore
return-void
"""
addStaticFieldToExtension(
EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR,
"enterFullscreenMode",
"fullscreenActionClass",
fullscreenActionClass,
enterFullscreenModeSmaliInstructions,
false
)
addStaticFieldToExtension(
EXTENSION_VIDEO_UTILS_CLASS_DESCRIPTOR,
"exitFullscreenMode",
"fullscreenActionClass",
fullscreenActionClass,
exitFullscreenModeSmaliInstructions,
false
)
}
}
}