diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsPatch.java index a8458310d..a96999683 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsPatch.java @@ -57,11 +57,4 @@ public class PlayerControlsPatch { private static void fullscreenButtonVisibilityChanged(boolean isVisible) { // Code added during patching. } - - /** - * Injection point. - */ - public static String getPlayerTopControlsLayoutResourceName(String original) { - return "default"; - } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsVisibilityHookPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsVisibilityHookPatch.java new file mode 100644 index 000000000..75f37a570 --- /dev/null +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/PlayerControlsVisibilityHookPatch.java @@ -0,0 +1,18 @@ +package app.revanced.extension.youtube.patches; + +import androidx.annotation.Nullable; + +import app.revanced.extension.youtube.shared.PlayerControlsVisibility; + +@SuppressWarnings("unused") +public class PlayerControlsVisibilityHookPatch { + + /** + * Injection point. + */ + public static void setPlayerControlsVisibility(@Nullable Enum youTubePlayerControlsVisibility) { + if (youTubePlayerControlsVisibility == null) return; + + PlayerControlsVisibility.setFromString(youTubePlayerControlsVisibility.name()); + } +} diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch.java index 25ad35d64..64628661a 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofAppVersionPatch.java @@ -9,7 +9,7 @@ public class SpoofAppVersionPatch { private static final String SPOOF_APP_VERSION_TARGET = Settings.SPOOF_APP_VERSION_TARGET.get(); /** - * Injection point + * injection point. */ public static String getYouTubeVersionOverride(String version) { if (SPOOF_APP_VERSION_ENABLED) return SPOOF_APP_VERSION_TARGET; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java index 4e3405860..965b16364 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/SeekbarColorPatch.java @@ -126,7 +126,7 @@ public final class SeekbarColorPatch { } /** - * Injection point + * injection point. */ public static boolean useLotteLaunchSplashScreen(boolean original) { // This method is only used for development purposes to force the old style launch screen. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerControlsVisibility.kt b/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerControlsVisibility.kt new file mode 100644 index 000000000..5585bc157 --- /dev/null +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/shared/PlayerControlsVisibility.kt @@ -0,0 +1,48 @@ +package app.revanced.extension.youtube.shared + +import app.revanced.extension.shared.Logger +import app.revanced.extension.youtube.Event + +/** + * PlayerControls visibility state. + */ +enum class PlayerControlsVisibility { + PLAYER_CONTROLS_VISIBILITY_UNKNOWN, + PLAYER_CONTROLS_VISIBILITY_WILL_HIDE, + PLAYER_CONTROLS_VISIBILITY_HIDDEN, + PLAYER_CONTROLS_VISIBILITY_WILL_SHOW, + PLAYER_CONTROLS_VISIBILITY_SHOWN; + + companion object { + + private val nameToPlayerControlsVisibility = PlayerControlsVisibility.entries.associateBy { it.name } + + @JvmStatic + fun setFromString(enumName: String) { + val newType = nameToPlayerControlsVisibility[enumName] + if (newType == null) { + Logger.printException { "Unknown PlayerControlsVisibility encountered: $enumName" } + } else { + current = newType + } + } + + @JvmStatic + var current + get() = currentPlayerControlsVisibility + private set(type) { + if (currentPlayerControlsVisibility != type) { + Logger.printDebug { "Changed to: $type" } + + currentPlayerControlsVisibility = type + onChange(type) + } + } + + @Volatile // Read/write from different threads. + private var currentPlayerControlsVisibility = PLAYER_CONTROLS_VISIBILITY_UNKNOWN + + @JvmStatic + val onChange = Event() + } +} \ No newline at end of file diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/SegmentPlaybackController.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/SegmentPlaybackController.java index 300488390..c53c12460 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/SegmentPlaybackController.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/SegmentPlaybackController.java @@ -13,7 +13,6 @@ import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RoundRectShape; import android.text.TextUtils; -import android.util.DisplayMetrics; import android.util.Range; import android.view.Gravity; import android.view.ViewGroup; @@ -877,7 +876,7 @@ public class SegmentPlaybackController { } /** - * Injection point + * injection point. */ @SuppressWarnings("unused") public static void setSponsorBarRect(Object self) { @@ -909,7 +908,7 @@ public class SegmentPlaybackController { } /** - * Injection point + * injection point. */ @SuppressWarnings("unused") public static void setSponsorBarThickness(int thickness) { diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java index 77ae650c0..3d4f1d888 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/CreateSegmentButton.java @@ -18,7 +18,7 @@ public class CreateSegmentButton { } /** - * injection point + * injection point. */ public static void initialize(View controlsView) { try { @@ -37,14 +37,21 @@ public class CreateSegmentButton { } /** - * Injection point + * injection point. + */ + public static void setVisibilityNegatedImmediate() { + if (instance != null) instance.setVisibilityNegatedImmediate(); + } + + /** + * injection point. */ public static void setVisibilityImmediate(boolean visible) { if (instance != null) instance.setVisibilityImmediate(visible); } /** - * Injection point + * injection point. */ public static void setVisibility(boolean visible, boolean animated) { if (instance != null) instance.setVisibility(visible, animated); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/VotingButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/VotingButton.java index cc9850253..e8f793d05 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/VotingButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/sponsorblock/ui/VotingButton.java @@ -20,7 +20,7 @@ public class VotingButton { } /** - * injection point + * injection point. */ public static void initialize(View controlsView) { try { @@ -39,14 +39,21 @@ public class VotingButton { } /** - * Injection point + * injection point. + */ + public static void setVisibilityNegatedImmediate() { + if (instance != null) instance.setVisibilityNegatedImmediate(); + } + + /** + * injection point. */ public static void setVisibilityImmediate(boolean visible) { if (instance != null) instance.setVisibilityImmediate(visible); } /** - * Injection point + * injection point. */ public static void setVisibility(boolean visible, boolean animated) { if (instance != null) instance.setVisibility(visible, animated); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlButton.java index 5568a99b9..9b9d46cce 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlButton.java @@ -35,15 +35,22 @@ public class CopyVideoUrlButton { } } + /**` + * injection point. + */ + public static void setVisibilityNegatedImmediate() { + if (instance != null) instance.setVisibilityNegatedImmediate(); + } + /** - * injection point + * injection point. */ public static void setVisibilityImmediate(boolean visible) { if (instance != null) instance.setVisibilityImmediate(visible); } /** - * injection point + * injection point. */ public static void setVisibility(boolean visible, boolean animated) { if (instance != null) instance.setVisibility(visible, animated); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlTimestampButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlTimestampButton.java index a8f4e7c12..55ec75543 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlTimestampButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/CopyVideoUrlTimestampButton.java @@ -36,14 +36,21 @@ public class CopyVideoUrlTimestampButton { } /** - * injection point + * injection point. + */ + public static void setVisibilityNegatedImmediate() { + if (instance != null) instance.setVisibilityNegatedImmediate(); + } + + /** + * injection point. */ public static void setVisibilityImmediate(boolean visible) { if (instance != null) instance.setVisibilityImmediate(visible); } /** - * injection point + * injection point. */ public static void setVisibility(boolean visible, boolean animated) { if (instance != null) instance.setVisibility(visible, animated); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/ExternalDownloadButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/ExternalDownloadButton.java index ae3304ecc..af158ca9e 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/ExternalDownloadButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/ExternalDownloadButton.java @@ -34,14 +34,21 @@ public class ExternalDownloadButton { } /** - * injection point + * injection point. + */ + public static void setVisibilityNegatedImmediate() { + if (instance != null) instance.setVisibilityNegatedImmediate(); + } + + /** + * injection point. */ public static void setVisibilityImmediate(boolean visible) { if (instance != null) instance.setVisibilityImmediate(visible); } /** - * Injection point + * injection point. */ public static void setVisibility(boolean visible, boolean animated) { if (instance != null) instance.setVisibility(visible, animated); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton.java index 60795851c..57cb5912b 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlaybackSpeedDialogButton.java @@ -69,6 +69,13 @@ public class PlaybackSpeedDialogButton { } } + /** + * injection point. + */ + public static void setVisibilityNegatedImmediate() { + if (instance != null) instance.setVisibilityNegatedImmediate(); + } + /** * Injection point. */ diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlayerControlButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlayerControlButton.java index 548ee86c0..64551d327 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlayerControlButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/PlayerControlButton.java @@ -11,6 +11,7 @@ import java.lang.ref.WeakReference; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; +import app.revanced.extension.youtube.shared.PlayerControlsVisibility; import app.revanced.extension.youtube.shared.PlayerType; import kotlin.Unit; @@ -40,8 +41,6 @@ public class PlayerControlButton { fadeOutAnimation = Utils.getResourceAnimation("fade_out"); fadeOutAnimation.setDuration(fadeOutDuration); - // Animation for the fast fade out after tapping the overlay. - // Currently not used but should be. fadeOutImmediate = Utils.getResourceAnimation("abc_fade_out"); fadeOutImmediate.setDuration(Utils.getResourceInteger("fade_duration_fast")); } @@ -114,6 +113,33 @@ public class PlayerControlButton { }); } + public void setVisibilityNegatedImmediate() { + if (PlayerControlsVisibility.getCurrent() != PlayerControlsVisibility.PLAYER_CONTROLS_VISIBILITY_HIDDEN) { + return; + } + + final boolean buttonEnabled = enabledStatus.buttonEnabled(); + if (!buttonEnabled) { + return; + } + + View container = containerRef.get(); + if (container == null) { + return; + } + + isVisible = false; + + container.clearAnimation(); + container.startAnimation(fadeOutImmediate); + container.setVisibility(View.GONE); + + View placeholder = placeHolderRef.get(); + if (placeholder != null) { + container.setVisibility(View.VISIBLE); + } + } + public void setVisibilityImmediate(boolean visible) { if (visible) { // Fix button flickering, by pushing this call to the back of diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java index 6b43abf51..109eaadd2 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/videoplayer/VideoQualityDialogButton.java @@ -116,6 +116,13 @@ public class VideoQualityDialogButton { } } + /** + * injection point. + */ + public static void setVisibilityNegatedImmediate() { + if (instance != null) instance.setVisibilityNegatedImmediate(); + } + /** * Injection point. */ diff --git a/patches/api/patches.api b/patches/api/patches.api index 835beeb59..aae1502c1 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -1536,6 +1536,10 @@ public final class app/revanced/patches/youtube/misc/navigation/NavigationBarHoo public static final fun setHookNavigationButtonCreated (Lkotlin/jvm/functions/Function1;)V } +public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatchKt { + public static final fun getPlayerControlsOverlayVisibilityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatchKt { public static final fun getAddBottomControl ()Lkotlin/jvm/functions/Function1; public static final fun getPlayerControlsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt index 95dbd54ec..da2137135 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/Fingerprints.kt @@ -2,14 +2,63 @@ package app.revanced.patches.youtube.misc.playercontrols import app.revanced.patcher.fingerprint import app.revanced.util.containsLiteralInstruction +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import app.revanced.util.indexOfFirstInstructionReversed import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags +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 fun indexOfFocusableInTouchModeInstruction(method: Method) = + method.indexOfFirstInstruction { + getReference()?.name == "setFocusableInTouchMode" + } + +internal fun indexOfTranslationInstruction(method: Method) = + method.indexOfFirstInstructionReversed { + getReference()?.name == "setTranslationY" + } + +internal val playerControlsVisibilityEntityModelFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("L") + parameters() + opcodes( + Opcode.IGET, + Opcode.INVOKE_STATIC + ) + custom { method, _ -> + method.name == "getPlayerControlsVisibility" + } +} + +internal val youtubeControlsOverlayFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + parameters() + custom { method, _ -> + indexOfFocusableInTouchModeInstruction(method) >= 0 && + method.containsLiteralInstruction(inset_overlay_view_layout_id) && + method.containsLiteralInstruction(scrim_overlay_id) + + } +} + +internal val motionEventFingerprint = fingerprint { + returns("V") + parameters("Landroid/view/MotionEvent;") + custom { method, _ -> + indexOfTranslationInstruction(method) >= 0 + } +} internal val playerTopControlsInflateFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters() - literal { controlsLayoutStub } + literal { controls_layout_stub_id } } internal val playerControlsExtensionHookListenersExistFingerprint = fingerprint { @@ -35,7 +84,7 @@ internal val playerControlsExtensionHookFingerprint = fingerprint { internal val playerBottomControlsInflateFingerprint = fingerprint { returns("Ljava/lang/Object;") parameters() - literal { bottomUiContainerResourceId } + literal { bottom_ui_container_stub_id } } internal val overlayViewInflateFingerprint = fingerprint { @@ -43,8 +92,8 @@ internal val overlayViewInflateFingerprint = fingerprint { returns("V") parameters("Landroid/view/View;") custom { methodDef, _ -> - methodDef.containsLiteralInstruction(fullscreenButton) && - methodDef.containsLiteralInstruction(heatseekerViewstub) + methodDef.containsLiteralInstruction(fullscreen_button_id) && + methodDef.containsLiteralInstruction(heatseeker_viewstub_id) } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatch.kt new file mode 100644 index 000000000..e10725f76 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsOverlayVisibilityPatch.kt @@ -0,0 +1,42 @@ +package app.revanced.patches.youtube.misc.playercontrols + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.util.indexOfFirstInstructionOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +private const val EXTENSION_PLAYER_CONTROLS_VISIBILITY_HOOK_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/PlayerControlsVisibilityHookPatch;" + +val PlayerControlsOverlayVisibilityPatch = bytecodePatch { + dependsOn(sharedExtensionPatch) + + execute { + playerControlsVisibilityEntityModelFingerprint.let { + it.method.apply { + val startIndex = it.patternMatch!!.startIndex + val iGetReference = getInstruction(startIndex).reference + val staticReference = getInstruction(startIndex + 1).reference + + it.classDef.methods.find { method -> method.name == "" }?.apply { + val targetIndex = indexOfFirstInstructionOrThrow(Opcode.IPUT_OBJECT) + val targetRegister = getInstruction(targetIndex).registerA + + addInstructions( + targetIndex + 1, + """ + iget v$targetRegister, v$targetRegister, $iGetReference + invoke-static { v$targetRegister }, $staticReference + move-result-object v$targetRegister + invoke-static { v$targetRegister }, $EXTENSION_PLAYER_CONTROLS_VISIBILITY_HOOK_CLASS_DESCRIPTOR->setPlayerControlsVisibility(Ljava/lang/Enum;)V + """ + ) + } + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt index 7eb292bfd..e5949586d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/playercontrols/PlayerControlsPatch.kt @@ -1,7 +1,6 @@ package app.revanced.patches.youtube.misc.playercontrols import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -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 @@ -40,13 +39,17 @@ internal lateinit var addTopControl: (String) -> Unit lateinit var addBottomControl: (String) -> Unit private set -internal var bottomUiContainerResourceId = -1L +internal var bottom_ui_container_stub_id = -1L private set -internal var controlsLayoutStub = -1L +internal var controls_layout_stub_id = -1L private set -internal var heatseekerViewstub = -1L +internal var heatseeker_viewstub_id = -1L private set -internal var fullscreenButton = -1L +internal var fullscreen_button_id = -1L + private set +internal var inset_overlay_view_layout_id = -1L + private set +internal var scrim_overlay_id = -1L private set val playerControlsResourcePatch = resourcePatch { @@ -65,10 +68,12 @@ val playerControlsResourcePatch = resourcePatch { execute { val targetResourceName = "youtube_controls_bottom_ui_container.xml" - bottomUiContainerResourceId = resourceMappings["id", "bottom_ui_container_stub"] - controlsLayoutStub = resourceMappings["id", "controls_layout_stub"] - heatseekerViewstub = resourceMappings["id", "heatseeker_viewstub"] - fullscreenButton = resourceMappings["id", "fullscreen_button"] + bottom_ui_container_stub_id = resourceMappings["id", "bottom_ui_container_stub"] + controls_layout_stub_id = resourceMappings["id", "controls_layout_stub"] + heatseeker_viewstub_id = resourceMappings["id", "heatseeker_viewstub"] + fullscreen_button_id = resourceMappings["id", "fullscreen_button"] + inset_overlay_view_layout_id = resourceMappings["id", "inset_overlay_view_layout"] + scrim_overlay_id = resourceMappings["id", "scrim_overlay"] bottomTargetDocument = document("res/layout/$targetResourceName") @@ -198,6 +203,13 @@ fun injectVisibilityCheckCall(descriptor: String) { visibilityImmediateInsertIndex++, "invoke-static { p0 }, $descriptor->setVisibilityImmediate(Z)V", ) + + // Patch works without this hook, but it is needed to use the correct fade out animation + // duration when tapping the overlay to dismiss. + visibilityNegatedImmediateMethod.addInstruction( + visibilityNegatedImmediateInsertIndex++, + "invoke-static { }, $descriptor->setVisibilityNegatedImmediate()V", + ) } internal const val EXTENSION_CLASS_DESCRIPTOR = @@ -220,12 +232,16 @@ private lateinit var visibilityImmediateCallbacksExistMethod : MutableMethod private lateinit var visibilityImmediateMethod: MutableMethod private var visibilityImmediateInsertIndex: Int = 0 +private lateinit var visibilityNegatedImmediateMethod: MutableMethod +private var visibilityNegatedImmediateInsertIndex: Int = 0 + val playerControlsPatch = bytecodePatch( description = "Manages the code for the player controls of the YouTube player.", ) { dependsOn( playerControlsResourcePatch, sharedExtensionPatch, + PlayerControlsOverlayVisibilityPatch ) execute { @@ -258,7 +274,7 @@ val playerControlsPatch = bytecodePatch( // Hook the fullscreen close button. Used to fix visibility // when seeking and other situations. overlayViewInflateFingerprint.method.apply { - val resourceIndex = indexOfFirstLiteralInstructionReversedOrThrow(fullscreenButton) + val resourceIndex = indexOfFirstLiteralInstructionReversedOrThrow(fullscreen_button_id) val index = indexOfFirstInstructionOrThrow(resourceIndex) { opcode == Opcode.CHECK_CAST && @@ -277,6 +293,11 @@ val playerControlsPatch = bytecodePatch( visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistFingerprint.method visibilityImmediateMethod = playerControlsExtensionHookFingerprint.method + motionEventFingerprint.match(youtubeControlsOverlayFingerprint.originalClassDef).method.apply { + visibilityNegatedImmediateMethod = this + visibilityNegatedImmediateInsertIndex = indexOfTranslationInstruction(this) + 1 + } + // A/B test for a slightly different bottom overlay controls, // that uses layout file youtube_video_exploder_controls_bottom_ui_container.xml // The change to support this is simple and only requires adding buttons to both layout files, @@ -299,12 +320,9 @@ val playerControlsPatch = bytecodePatch( val index = indexOfFirstInstructionOrThrow(Opcode.MOVE_RESULT_OBJECT) val register = getInstruction(index).registerA - addInstructions( + addInstruction( index + 1, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getPlayerTopControlsLayoutResourceName(Ljava/lang/String;)Ljava/lang/String; - move-result-object v$register - """, + "const-string v$register, \"default\"" ) } }