feat(YouTube): Add Exit fullscreen mode patch (#4223)

This commit is contained in:
LisoUseInAIKyrios 2024-12-27 10:25:17 +04:00 committed by GitHub
parent 119092fafa
commit bb5d03bd89
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 190 additions and 26 deletions

View File

@ -523,6 +523,11 @@ public class Utils {
return currentNightMode == Configuration.UI_MODE_NIGHT_YES; return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
} }
public static boolean isLandscapeOrientation() {
final int orientation = context.getResources().getConfiguration().orientation;
return orientation == Configuration.ORIENTATION_LANDSCAPE;
}
/** /**
* Automatically logs any exceptions the runnable throws. * Automatically logs any exceptions the runnable throws.
* *
@ -595,7 +600,7 @@ public class Utils {
|| networkType == NetworkType.OTHER; || networkType == NetworkType.OTHER;
} }
@SuppressLint("MissingPermission") // permission already included in YouTube @SuppressLint({"MissingPermission", "deprecation"}) // Permission already included in YouTube.
public static NetworkType getNetworkType() { public static NetworkType getNetworkType() {
Context networkContext = getContext(); Context networkContext = getContext();
if (networkContext == null) { if (networkContext == null) {

View File

@ -0,0 +1,49 @@
package app.revanced.extension.youtube.patches;
import android.widget.ImageView;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class ExitFullscreenPatch {
public enum FullscreenMode {
DISABLED,
PORTRAIT,
LANDSCAPE,
PORTRAIT_LANDSCAPE,
}
/**
* Injection point.
*/
public static void endOfVideoReached() {
try {
FullscreenMode mode = Settings.EXIT_FULLSCREEN.get();
if (mode == FullscreenMode.DISABLED) {
return;
}
if (PlayerType.getCurrent() == PlayerType.WATCH_WHILE_FULLSCREEN) {
if (Utils.isLandscapeOrientation()) {
if (mode == FullscreenMode.PORTRAIT) {
return;
}
} else if (mode == FullscreenMode.LANDSCAPE) {
return;
}
ImageView fullscreenButton = PlayerControlsPatch.fullscreenButtonRef.get();
if (fullscreenButton != null) {
Logger.printDebug(() -> "Clicking fullscreen button");
fullscreenButton.performClick();
}
}
} catch (Exception ex) {
Logger.printException(() -> "endOfVideoReached failure", ex);
}
}
}

View File

@ -4,15 +4,29 @@ import android.view.View;
import android.view.ViewTreeObserver; import android.view.ViewTreeObserver;
import android.widget.ImageView; import android.widget.ImageView;
import java.lang.ref.WeakReference;
import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class PlayerControlsPatch { public class PlayerControlsPatch {
public static WeakReference<ImageView> fullscreenButtonRef = new WeakReference<>(null);
private static boolean fullscreenButtonVisibilityCallbacksExist() {
return false; // Modified during patching if needed.
}
/** /**
* Injection point. * Injection point.
*/ */
public static void setFullscreenCloseButton(ImageView imageButton) { public static void setFullscreenCloseButton(ImageView imageButton) {
fullscreenButtonRef = new WeakReference<>(imageButton);
if (!fullscreenButtonVisibilityCallbacksExist()) {
return;
}
// Add a global listener, since the protected method // Add a global listener, since the protected method
// View#onVisibilityChanged() does not have any call backs. // View#onVisibilityChanged() does not have any call backs.
imageButton.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { imageButton.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@ -39,7 +53,7 @@ public class PlayerControlsPatch {
} }
// noinspection EmptyMethod // noinspection EmptyMethod
public static void fullscreenButtonVisibilityChanged(boolean isVisible) { private static void fullscreenButtonVisibilityChanged(boolean isVisible) {
// Code added during patching. // Code added during patching.
} }

View File

@ -8,6 +8,7 @@ import static app.revanced.extension.shared.settings.Setting.migrateOldSettingTo
import static app.revanced.extension.shared.settings.Setting.parent; import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.settings.Setting.parentsAny; import static app.revanced.extension.shared.settings.Setting.parentsAny;
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage; import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability; import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability; import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability; import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
@ -120,6 +121,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE); public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE); public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true); public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true); public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE); public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true); public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
@ -139,10 +141,10 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_subscribers_community_guidelines", TRUE); public static final BooleanSetting HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_subscribers_community_guidelines", TRUE);
public static final BooleanSetting HIDE_TIMED_REACTIONS = new BooleanSetting("revanced_hide_timed_reactions", TRUE); public static final BooleanSetting HIDE_TIMED_REACTIONS = new BooleanSetting("revanced_hide_timed_reactions", TRUE);
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE); public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
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); public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
// Miniplayer // Miniplayer
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.DEFAULT, true); public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.DEFAULT, true);
private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4); private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);

View File

@ -73,7 +73,7 @@ enum class PlayerType {
onChange(currentPlayerType) onChange(currentPlayerType)
} }
@Volatile // value is read/write from different threads @Volatile // Read/write from different threads.
private var currentPlayerType = NONE private var currentPlayerType = NONE
/** /**

View File

@ -46,6 +46,7 @@ enum class VideoState {
currentVideoState = value currentVideoState = value
} }
@Volatile // Read/write from different threads.
private var currentVideoState: VideoState? = null private var currentVideoState: VideoState? = null
} }
} }

View File

@ -461,10 +461,6 @@ public final class app/revanced/patches/reddit/customclients/joeyforreddit/detec
public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/reddit/customclients/redditisfun/api/FingerprintsKt {
public static final fun baseClientIdFingerprint (Ljava/lang/String;)Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatchKt { public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@ -548,7 +544,6 @@ public final class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentP
} }
public final class app/revanced/patches/shared/misc/extension/ExtensionHook { public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
public final fun getFingerprint ()Lapp/revanced/patcher/Fingerprint;
public final fun invoke (Lapp/revanced/patcher/patch/BytecodePatchContext;Ljava/lang/String;)V public final fun invoke (Lapp/revanced/patcher/patch/BytecodePatchContext;Ljava/lang/String;)V
} }
@ -1268,10 +1263,6 @@ public final class app/revanced/patches/youtube/misc/backgroundplayback/Backgrou
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatchKt {
public static final fun getCheckEnvironmentPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt { public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt {
public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
@ -1408,10 +1399,6 @@ public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatc
public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/youtube/shared/FingerprintsKt {
public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt { public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt {
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }

View File

@ -4,7 +4,7 @@ import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
fun baseClientIdFingerprint(string: String) = fingerprint { internal fun baseClientIdFingerprint(string: String) = fingerprint {
strings("yyOCBp.RHJhDKd", string) strings("yyOCBp.RHJhDKd", string)
} }

View File

@ -92,7 +92,7 @@ fun sharedExtensionPatch(
} }
class ExtensionHook internal constructor( class ExtensionHook internal constructor(
val fingerprint: Fingerprint, private val fingerprint: Fingerprint,
private val insertIndexResolver: ((Method) -> Int), private val insertIndexResolver: ((Method) -> Int),
private val contextRegisterResolver: (Method) -> String, private val contextRegisterResolver: (Method) -> String,
) { ) {

View File

@ -0,0 +1,65 @@
package app.revanced.patches.youtube.layout.player.fullscreen
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
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.ListPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playercontrols.playerControlsPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.autoRepeatFingerprint
import app.revanced.patches.youtube.shared.autoRepeatParentFingerprint
@Suppress("unused")
internal val exitFullscreenPatch = bytecodePatch(
name = "Exit fullscreen mode",
description = "Adds options to automatically exit fullscreen mode when a video reaches the end."
) {
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
)
)
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
playerTypeHookPatch,
playerControlsPatch
)
// Cannot declare as top level since this patch is in the same package as
// other patches that declare same constant name with internal visibility.
@Suppress("LocalVariableName")
val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/ExitFullscreenPatch;"
execute {
addResources("youtube", "layout.player.fullscreen.exitFullscreenPatch")
PreferenceScreen.PLAYER.addPreferences(
ListPreference(
"revanced_exit_fullscreen",
summaryKey = null,
)
)
autoRepeatFingerprint.match(autoRepeatParentFingerprint.originalClassDef).method.addInstruction(
0,
"invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->endOfVideoReached()V",
)
}
}

View File

@ -4,7 +4,7 @@ import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
val checkEnvironmentPatch = checkEnvironmentPatch( internal val checkEnvironmentPatch = checkEnvironmentPatch(
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
extensionPatch = sharedExtensionPatch, extensionPatch = sharedExtensionPatch,
"com.google.android.youtube", "com.google.android.youtube",

View File

@ -12,13 +12,23 @@ internal val playerTopControlsInflateFingerprint = fingerprint {
literal { controlsLayoutStub } literal { controlsLayoutStub }
} }
internal val playerControlsExtensionHookListenersExistFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("Z")
parameters()
custom { methodDef, classDef ->
methodDef.name == "fullscreenButtonVisibilityCallbacksExist" &&
classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}
internal val playerControlsExtensionHookFingerprint = fingerprint { internal val playerControlsExtensionHookFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("V") returns("V")
parameters("Z") parameters("Z")
custom { methodDef, classDef -> custom { methodDef, classDef ->
methodDef.name == "fullscreenButtonVisibilityChanged" && methodDef.name == "fullscreenButtonVisibilityChanged" &&
classDef.type == "Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;" classDef.type == EXTENSION_CLASS_DESCRIPTOR
} }
} }

View File

@ -189,13 +189,18 @@ fun injectVisibilityCheckCall(descriptor: String) {
"invoke-static { p1 , p2 }, $descriptor->changeVisibility(ZZ)V", "invoke-static { p1 , p2 }, $descriptor->changeVisibility(ZZ)V",
) )
if (!visibilityImmediateCallbacksExistModified) {
visibilityImmediateCallbacksExistModified = true
visibilityImmediateCallbacksExistMethod.returnEarly(true)
}
visibilityImmediateMethod.addInstruction( visibilityImmediateMethod.addInstruction(
visibilityImmediateInsertIndex++, visibilityImmediateInsertIndex++,
"invoke-static { p0 }, $descriptor->changeVisibilityImmediate(Z)V", "invoke-static { p0 }, $descriptor->changeVisibilityImmediate(Z)V",
) )
} }
private const val EXTENSION_CLASS_DESCRIPTOR = internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;" "Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;"
private lateinit var inflateTopControlMethod: MutableMethod private lateinit var inflateTopControlMethod: MutableMethod
@ -209,6 +214,9 @@ private var inflateBottomControlRegister: Int = -1
private lateinit var visibilityMethod: MutableMethod private lateinit var visibilityMethod: MutableMethod
private var visibilityInsertIndex: Int = 0 private var visibilityInsertIndex: Int = 0
private var visibilityImmediateCallbacksExistModified = false
private lateinit var visibilityImmediateCallbacksExistMethod : MutableMethod
private lateinit var visibilityImmediateMethod: MutableMethod private lateinit var visibilityImmediateMethod: MutableMethod
private var visibilityImmediateInsertIndex: Int = 0 private var visibilityImmediateInsertIndex: Int = 0
@ -266,6 +274,7 @@ val playerControlsPatch = bytecodePatch(
) )
} }
visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistFingerprint.method
visibilityImmediateMethod = playerControlsExtensionHookFingerprint.method visibilityImmediateMethod = playerControlsExtensionHookFingerprint.method
// A/B test for a slightly different bottom overlay controls, // A/B test for a slightly different bottom overlay controls,

View File

@ -51,7 +51,7 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
} }
} }
val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint { internal val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V") returns("V")
parameters("Landroid/graphics/Bitmap;") parameters("Landroid/graphics/Bitmap;")

View File

@ -148,6 +148,21 @@
<item>17.33.42</item> <item>17.33.42</item>
</string-array> </string-array>
</patch> </patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
<string-array name="revanced_exit_fullscreen_entries">
<item>@string/revanced_exit_fullscreen_entry_1</item>
<item>@string/revanced_exit_fullscreen_entry_2</item>
<item>@string/revanced_exit_fullscreen_entry_3</item>
<item>@string/revanced_exit_fullscreen_entry_4</item>
</string-array>
<string-array name="revanced_exit_fullscreen_entry_values">
<!-- Enum names from the extension. -->
<item>DISABLED</item>
<item>PORTRAIT</item>
<item>LANDSCAPE</item>
<item>PORTRAIT_LANDSCAPE</item>
</string-array>
</patch>
<patch id="layout.miniplayer.miniplayerPatch"> <patch id="layout.miniplayer.miniplayerPatch">
<string-array name="revanced_miniplayer_type_entries"> <string-array name="revanced_miniplayer_type_entries">
<item>@string/revanced_miniplayer_type_entry_0</item> <item>@string/revanced_miniplayer_type_entry_0</item>

View File

@ -748,6 +748,13 @@ Note: Enabling this also forcibly hides video ads"</string>
<string name="revanced_hide_player_popup_panels_summary_on">Player popup panels are hidden</string> <string name="revanced_hide_player_popup_panels_summary_on">Player popup panels are hidden</string>
<string name="revanced_hide_player_popup_panels_summary_off">Player popup panels are shown</string> <string name="revanced_hide_player_popup_panels_summary_off">Player popup panels are shown</string>
</patch> </patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
<string name="revanced_exit_fullscreen_title">Exit fullscreen mode at end of video</string>
<string name="revanced_exit_fullscreen_entry_1">Disabled</string>
<string name="revanced_exit_fullscreen_entry_2">Portrait</string>
<string name="revanced_exit_fullscreen_entry_3">Landscape</string>
<string name="revanced_exit_fullscreen_entry_4">Portrait and landscape</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen"> <patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">Open videos in fullscreen portrait</string> <string name="revanced_open_videos_fullscreen_portrait_title">Open videos in fullscreen portrait</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">Videos open fullscreen</string> <string name="revanced_open_videos_fullscreen_portrait_summary_on">Videos open fullscreen</string>