diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenVideosFullscreen.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenVideosFullscreen.java new file mode 100644 index 000000000..a15d7f641 --- /dev/null +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/OpenVideosFullscreen.java @@ -0,0 +1,14 @@ +package app.revanced.extension.youtube.patches; + +import app.revanced.extension.youtube.settings.Settings; + +@SuppressWarnings("unused") +public class OpenVideosFullscreen { + + /** + * Injection point. + */ + public static boolean openVideoFullscreenPortrait(boolean original) { + return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get(); + } +} diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ThemePatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ThemePatch.java index 0f8fb3304..0a8dfda9b 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ThemePatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/theme/ThemePatch.java @@ -55,7 +55,7 @@ public class ThemePatch { /** * Injection point. */ - public static boolean gradientLoadingScreenEnabled() { + public static boolean gradientLoadingScreenEnabled(boolean original) { return GRADIENT_LOADING_SCREEN_ENABLED; } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java index fb3c199c9..5f256fb96 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -140,6 +140,7 @@ public class Settings extends BaseSettings { public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE); public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE); public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true); + public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE); // Miniplayer public static final EnumSetting MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.ORIGINAL, true); private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4); diff --git a/patches/api/patches.api b/patches/api/patches.api index 5e6a8c8fe..36a925df8 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -1164,6 +1164,10 @@ public final class app/revanced/patches/youtube/layout/player/background/PlayerC public static final fun getPlayerControlsBackgroundPatch ()Lapp/revanced/patcher/patch/ResourcePatch; } +public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenKt { + public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatchKt { public static final fun getCustomPlayerOverlayOpacityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt index 2112c1b40..57d40a6c5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/miniplayer/MiniplayerPatch.kt @@ -240,14 +240,14 @@ val miniplayerPatch = bytecodePatch( ), ) - fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) { + fun MutableMethod.insertMiniplayerBooleanOverride(index: Int, methodName: String) { val register = getInstruction(index).registerA addInstructions( index, """ - invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z - move-result v$register - """, + invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z + move-result v$register + """ ) } @@ -257,29 +257,25 @@ val miniplayerPatch = bytecodePatch( * Adds an override to force legacy tablet miniplayer to be used or not used. */ fun MutableMethod.insertLegacyTabletMiniplayerOverride(index: Int) { - insertBooleanOverride(index, "getLegacyTabletMiniplayerOverride") + insertMiniplayerBooleanOverride(index, "getLegacyTabletMiniplayerOverride") } /** * Adds an override to force modern miniplayer to be used or not used. */ fun MutableMethod.insertModernMiniplayerOverride(index: Int) { - insertBooleanOverride(index, "getModernMiniplayerOverride") + insertMiniplayerBooleanOverride(index, "getModernMiniplayerOverride") } - fun Fingerprint.insertLiteralValueBooleanOverride( + fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride( literal: Long, extensionMethod: String, - ) { - method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal) - val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) + ) = method.insertFeatureFlagBooleanOverride( + literal, + "$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z" + ) - insertBooleanOverride(targetIndex + 1, extensionMethod) - } - } - - fun Fingerprint.insertLiteralValueFloatOverride( + fun Fingerprint.insertMiniplayerFeatureFlagFloatOverride( literal: Long, extensionMethod: String, ) { @@ -370,24 +366,24 @@ val miniplayerPatch = bytecodePatch( } if (is_19_23_or_greater) { - miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride( + miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_DRAG_DROP_FEATURE_KEY, "enableMiniplayerDragAndDrop", ) } if (is_19_25_or_greater) { - miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride( + miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_MODERN_FEATURE_LEGACY_KEY, "getModernMiniplayerOverride", ) - miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride( + miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_MODERN_FEATURE_KEY, "getModernFeatureFlagsActiveOverride", ) - miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride( + miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_DOUBLE_TAP_FEATURE_KEY, "enableMiniplayerDoubleTapAction", ) @@ -426,19 +422,19 @@ val miniplayerPatch = bytecodePatch( } if (is_19_36_or_greater) { - miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride( + miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY, "setRoundedCorners", ) } if (is_19_43_or_greater) { - miniplayerOnCloseHandlerFingerprint.insertLiteralValueBooleanOverride( + miniplayerOnCloseHandlerFingerprint.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_DISABLED_FEATURE_KEY, "getMiniplayerOnCloseHandler" ) - miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride( + miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride( MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY, "setHorizontalDrag", ) diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/Fingerprints.kt new file mode 100644 index 000000000..d188d8442 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/Fingerprints.kt @@ -0,0 +1,16 @@ +package app.revanced.patches.youtube.layout.player.fullscreen + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags + +internal const val OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG = 45666112L + +internal val openVideosFullscreenPortraitFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("L", "Lj\$/util/Optional;") + literal { + OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreen.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreen.kt new file mode 100644 index 000000000..621381ae5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreen.kt @@ -0,0 +1,46 @@ +package app.revanced.patches.youtube.layout.player.fullscreen + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch +import app.revanced.patches.youtube.misc.settings.PreferenceScreen +import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.insertFeatureFlagBooleanOverride + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/youtube/patches/OpenVideosFullscreen;" + +@Suppress("unused") +val openVideosFullscreenPatch = bytecodePatch( + name = "Open videos fullscreen", + description = "Adds an option to open videos in full screen portrait mode.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + ) + + compatibleWith( + "com.google.android.youtube"( + "19.46.42", + ) + ) + + execute { + openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride( + OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG, + "$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z" + ) + + // Add resources and setting last, in case the user force patches an old incompatible version. + + addResources("youtube", "layout.player.fullscreen.openVideosFullscreen") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_open_videos_fullscreen_portrait") + ) + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt index a2f079792..a9f347ad0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/seekbar/SeekbarColorPatch.kt @@ -24,6 +24,7 @@ import app.revanced.util.getReference import app.revanced.util.indexOfFirstInstructionOrThrow import app.revanced.util.indexOfFirstLiteralInstructionOrThrow import app.revanced.util.inputStreamFromBundledResource +import app.revanced.util.insertFeatureFlagBooleanOverride import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -228,19 +229,10 @@ val seekbarColorPatch = bytecodePatch( // 19.25+ changes - playerSeekbarGradientConfigFingerprint.method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG) - val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) - val register = getInstruction(resultIndex).registerA - - addInstructions( - resultIndex + 1, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z - move-result v$register - """ - ) - } + playerSeekbarGradientConfigFingerprint.method.insertFeatureFlagBooleanOverride( + PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG, + "$EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z" + ) lithoLinearGradientFingerprint.method.addInstruction( 0, @@ -255,19 +247,10 @@ val seekbarColorPatch = bytecodePatch( launchScreenLayoutTypeFingerprint, mainActivityOnCreateFingerprint ).forEach { fingerprint -> - fingerprint.method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(launchScreenLayoutTypeLotteFeatureFlag) - val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) - val register = getInstruction(resultIndex).registerA - - addInstructions( - resultIndex + 1, - """ - invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z - move-result v$register - """ - ) - } + fingerprint.method.insertFeatureFlagBooleanOverride( + launchScreenLayoutTypeLotteFeatureFlag, + "$EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z" + ) } // Hook the splash animation drawable to set the a seekbar color theme. diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt index c658ee676..a5835d727 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/ThemePatch.kt @@ -1,7 +1,6 @@ package app.revanced.patches.youtube.layout.theme 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 import app.revanced.patcher.patch.resourcePatch @@ -17,10 +16,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.util.forEachChildElement -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstLiteralInstructionOrThrow -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import app.revanced.util.insertFeatureFlagBooleanOverride import org.w3c.dom.Element private const val EXTENSION_CLASS_DESCRIPTOR = @@ -212,19 +208,10 @@ val themePatch = bytecodePatch( SwitchPreference("revanced_gradient_loading_screen"), ) - useGradientLoadingScreenFingerprint.method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow(GRADIENT_LOADING_SCREEN_AB_CONSTANT) - val isEnabledIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) - val isEnabledRegister = getInstruction(isEnabledIndex).registerA - - addInstructions( - isEnabledIndex + 1, - """ - invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled()Z - move-result v$isEnabledRegister - """, - ) - } + useGradientLoadingScreenFingerprint.method.insertFeatureFlagBooleanOverride( + GRADIENT_LOADING_SCREEN_AB_CONSTANT, + "$EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled(Z)Z" + ) mapOf( themeHelperLightColorFingerprint to lightThemeBackgroundColor, diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt index d6fafe539..cd058ca59 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt @@ -34,10 +34,7 @@ internal val disableCairoSettingsPatch = bytecodePatch( * uYouPlus#1468. */ cairoFragmentConfigFingerprint.method.apply { - val literalIndex = indexOfFirstLiteralInstructionOrThrow( - CAIRO_CONFIG_LITERAL_VALUE, - ) - + val literalIndex = indexOfFirstLiteralInstructionOrThrow(CAIRO_CONFIG_LITERAL_VALUE) val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) val register = getInstruction(resultIndex).registerA diff --git a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt index ec75cb0a5..8e5a85056 100644 --- a/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt +++ b/patches/src/main/kotlin/app/revanced/util/BytecodeUtils.kt @@ -17,6 +17,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.Method import com.android.tools.smali.dexlib2.iface.instruction.Instruction +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.WideLiteralInstruction import com.android.tools.smali.dexlib2.iface.reference.Reference @@ -402,6 +403,20 @@ fun Method.findInstructionIndicesReversedOrThrow(opcode: Opcode): List { return instructions } +internal fun MutableMethod.insertFeatureFlagBooleanOverride(literal: Long, extensionsMethod: String) { + val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal) + val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT) + val register = getInstruction(index).registerA + + addInstructions( + index + 1, + """ + invoke-static { v$register }, $extensionsMethod + move-result v$register + """ + ) +} + /** * Called for _all_ instructions with the given literal value. */ diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index c1142d08a..ff2414695 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -715,6 +715,11 @@ This is because Crowdin requires temporarily flattening this file and removing t Player popup panels are hidden Player popup panels are shown + + Open videos in fullscreen portrait + Videos open fullscreen + Videos do not open fullscreen + Player overlay opacity Opacity value between 0-100, where 0 is transparent