diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2215c48ddc..ea7ff550a7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ @@ -35,7 +35,7 @@ diff --git a/android/app/src/main/java/com/izzy2lost/x1box/GameLibraryActivity.kt b/android/app/src/main/java/com/izzy2lost/x1box/GameLibraryActivity.kt index 3a1a9256bc..56e93c1bd8 100644 --- a/android/app/src/main/java/com/izzy2lost/x1box/GameLibraryActivity.kt +++ b/android/app/src/main/java/com/izzy2lost/x1box/GameLibraryActivity.kt @@ -151,6 +151,7 @@ class GameLibraryActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + OrientationLocker(this).enable() setContentView(R.layout.activity_game_library) EdgeToEdgeHelper.enable(this) EdgeToEdgeHelper.applySystemBarPadding(findViewById(R.id.library_scroll)) diff --git a/android/app/src/main/java/com/izzy2lost/x1box/LauncherActivity.kt b/android/app/src/main/java/com/izzy2lost/x1box/LauncherActivity.kt index 3104095353..037fd496ed 100644 --- a/android/app/src/main/java/com/izzy2lost/x1box/LauncherActivity.kt +++ b/android/app/src/main/java/com/izzy2lost/x1box/LauncherActivity.kt @@ -15,6 +15,7 @@ class LauncherActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + OrientationLocker(this).enable() val prefs = getSharedPreferences("x1box_prefs", MODE_PRIVATE) var setupComplete = prefs.getBoolean("setup_complete", false) diff --git a/android/app/src/main/java/com/izzy2lost/x1box/MainActivity.kt b/android/app/src/main/java/com/izzy2lost/x1box/MainActivity.kt index dc45fc4a3f..6f0c25a476 100644 --- a/android/app/src/main/java/com/izzy2lost/x1box/MainActivity.kt +++ b/android/app/src/main/java/com/izzy2lost/x1box/MainActivity.kt @@ -78,6 +78,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + OrientationLocker(this, landscapeOnly = true).enable() window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) val requestedSlot = intent?.getIntExtra(EXTRA_AUTO_LOAD_SNAPSHOT_SLOT, 0) ?: 0 if (requestedSlot in 1..TOTAL_SNAPSHOT_SLOTS) { diff --git a/android/app/src/main/java/com/izzy2lost/x1box/OrientationLocker.kt b/android/app/src/main/java/com/izzy2lost/x1box/OrientationLocker.kt new file mode 100644 index 0000000000..343e6f09e3 --- /dev/null +++ b/android/app/src/main/java/com/izzy2lost/x1box/OrientationLocker.kt @@ -0,0 +1,45 @@ +package com.izzy2lost.x1box + +import android.app.Activity +import android.content.pm.ActivityInfo +import android.view.OrientationEventListener + +/** + * Prevents reversed (upside-down) orientations. + * + * UI mode: portrait when upright/right-rotated, left landscape otherwise. + * Game mode: always left landscape, no exceptions. + */ +class OrientationLocker(private val activity: Activity, private val landscapeOnly: Boolean = false) { + + private val listener = object : OrientationEventListener(activity) { + override fun onOrientationChanged(orientation: Int) { + if (orientation == ORIENTATION_UNKNOWN) return + + val target = if (landscapeOnly) { + // In-game: always left landscape regardless of how the device is held. + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + } else { + // UI: portrait when upright or rotating right, landscape otherwise. + // 315-360 / 0-134 → portrait (upright + right rotation) + // 135-314 → landscape (upside-down + left rotation) + if (orientation < 135 || orientation >= 315) + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + else + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + } + + if (activity.requestedOrientation != target) { + activity.requestedOrientation = target + } + } + } + + fun enable() { + if (listener.canDetectOrientation()) listener.enable() + } + + fun disable() { + listener.disable() + } +} diff --git a/android/app/src/main/java/com/izzy2lost/x1box/SettingsActivity.kt b/android/app/src/main/java/com/izzy2lost/x1box/SettingsActivity.kt index 64cd9285fd..c3dfb623b7 100644 --- a/android/app/src/main/java/com/izzy2lost/x1box/SettingsActivity.kt +++ b/android/app/src/main/java/com/izzy2lost/x1box/SettingsActivity.kt @@ -207,6 +207,7 @@ class SettingsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + OrientationLocker(this).enable() setContentView(R.layout.activity_settings) EdgeToEdgeHelper.enable(this) EdgeToEdgeHelper.applySystemBarPadding(findViewById(R.id.settings_scroll)) diff --git a/android/app/src/main/java/com/izzy2lost/x1box/SetupWizardActivity.kt b/android/app/src/main/java/com/izzy2lost/x1box/SetupWizardActivity.kt index bb72e73eda..77d892ab54 100644 --- a/android/app/src/main/java/com/izzy2lost/x1box/SetupWizardActivity.kt +++ b/android/app/src/main/java/com/izzy2lost/x1box/SetupWizardActivity.kt @@ -152,6 +152,7 @@ class SetupWizardActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + OrientationLocker(this).enable() mcpxPath = loadValidatedLocalPath("mcpxPath", "mcpxUri", ::isSavedMcpxFileValid) flashPath = loadValidatedLocalPath("flashPath", "flashUri", ::isSavedFlashFileValid)