package com.unciv.app import android.content.pm.ActivityInfo import android.os.Build import android.view.Display import android.view.Display.Mode import android.view.View import android.view.WindowManager import androidx.annotation.RequiresApi import com.badlogic.gdx.backends.android.AndroidApplication import com.unciv.models.metadata.GameSettings import com.unciv.models.translations.tr import com.unciv.utils.PlatformDisplay import com.unciv.utils.ScreenMode import com.unciv.utils.ScreenOrientation class AndroidDisplay(private val activity: AndroidApplication) : PlatformDisplay { private var display: Display? = null private var displayModes: HashMap = hashMapOf() init { // Fetch current display @Suppress("DEPRECATION") // M..P should use the deprecated API display = when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> activity.display Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> activity.windowManager.defaultDisplay else -> null } // Add default mode displayModes[AndroidScreenMode.defaultId] = AndroidScreenMode.default // Add other supported modes if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) fetchScreenModes() } @RequiresApi(Build.VERSION_CODES.M) private fun fetchScreenModes() { val display = display ?: return for (mode in display.supportedModes) displayModes[mode.modeId] = AndroidScreenMode(mode) } override fun getScreenModes(): Map { return displayModes } override fun setScreenMode(id: Int, settings: GameSettings) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return activity.runOnUiThread { val params = activity.window.attributes params.preferredDisplayModeId = id activity.window.attributes = params } } override fun hasSystemUiVisibility() = true override fun setSystemUiVisibility(hide: Boolean) { activity.runOnUiThread { setSystemUiVisibilityFromUiThread(hide) } } internal fun setSystemUiVisibilityFromUiThread(hide: Boolean) { @Suppress("DEPRECATION") // Avoids @RequiresApi(Build.VERSION_CODES.R) activity.window.decorView.systemUiVisibility = if (hide) View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY else View.SYSTEM_UI_FLAG_LAYOUT_STABLE } override fun hasCutout(): Boolean { return when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> activity.display?.cutout != null Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> @Suppress("DEPRECATION") activity.windowManager.defaultDisplay.cutout != null Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> activity.window.decorView.rootWindowInsets.displayCutout != null else -> false } } override fun setCutout(enabled: Boolean) { activity.runOnUiThread { setCutoutFromUiThread(enabled) } } internal fun setCutoutFromUiThread(enabled: Boolean) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return val params = activity.window.attributes params.layoutInDisplayCutoutMode = when { enabled -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES else -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER } activity.window.attributes = params // This is the only line to actually need to be running on the Ui Thread } /* Sources for Info about current orientation in case need: val windowManager = (activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager) val displayRotation = windowManager.defaultDisplay.rotation val currentOrientation = activity.resources.configuration.orientation const val ORIENTATION_UNDEFINED = 0 const val ORIENTATION_PORTRAIT = 1 const val ORIENTATION_LANDSCAPE = 2 */ override fun hasOrientation(): Boolean { return true } override fun setOrientation(orientation: ScreenOrientation) { val mode = when (orientation) { // Automatically adjust landscape based on sensors. ScreenOrientation.Landscape -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE // Automatically adjust the portrait based on the sensor. ScreenOrientation.Portrait -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT ScreenOrientation.Auto -> ActivityInfo.SCREEN_ORIENTATION_SENSOR } // Ensure ActivityTaskManager.getService().setRequestedOrientation isn't called unless necessary! if (activity.requestedOrientation != mode) activity.requestedOrientation = mode } class AndroidScreenMode private constructor( private val modeId: Int, private val name: String ) : ScreenMode { @RequiresApi(Build.VERSION_CODES.M) constructor(mode: Mode) : this(mode.modeId, "${mode.physicalWidth}x${mode.physicalHeight} (${mode.refreshRate.toInt()}Hz)") override fun getId(): Int { return modeId } override fun toString(): String { return name.tr() } companion object { const val defaultId = 0 val default: AndroidScreenMode get() = AndroidScreenMode(defaultId, "Default") } } }