mirror of
https://github.com/yairm210/Unciv.git
synced 2025-02-21 20:18:28 +07:00
Allow controlling Android fullscreen from options (#10518)
This commit is contained in:
parent
0d077ee099
commit
98c3c446c5
@ -812,23 +812,21 @@ Screen orientation =
|
||||
Landscape (fixed) =
|
||||
Portrait (fixed) =
|
||||
Auto (sensor adjusted) =
|
||||
|
||||
Enable display cutout (requires restart) =
|
||||
|
||||
Max zoom out =
|
||||
|
||||
Enable using display cutout areas =
|
||||
Hide system status and navigation bars =
|
||||
Font family =
|
||||
Font size multiplier =
|
||||
Default Font =
|
||||
|
||||
Max zoom out =
|
||||
Enable Easter Eggs =
|
||||
Enlarge selected notifications =
|
||||
|
||||
Generate translation files =
|
||||
Translation files are generated successfully. =
|
||||
Fastlane files are generated successfully. =
|
||||
Update Mod categories =
|
||||
|
||||
Enable Easter Eggs =
|
||||
Enlarge selected notifications =
|
||||
|
||||
## Keys tab
|
||||
Keys =
|
||||
Please see the Tutorial. =
|
||||
|
@ -2,41 +2,19 @@ package com.unciv.app
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.database.ContentObserver
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.provider.Settings
|
||||
import android.view.Display
|
||||
import android.view.Display.Mode
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.unciv.models.metadata.GameSettings
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.utils.Log
|
||||
import com.unciv.utils.PlatformDisplay
|
||||
import com.unciv.utils.ScreenMode
|
||||
import com.unciv.utils.ScreenOrientation
|
||||
|
||||
|
||||
class AndroidScreenMode(
|
||||
private val modeId: Int) : ScreenMode {
|
||||
private var name: String = "Default"
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
constructor(mode: Mode) : this(mode.modeId) {
|
||||
name = "${mode.physicalWidth}x${mode.physicalHeight} (${mode.refreshRate.toInt()}HZ)"
|
||||
}
|
||||
|
||||
override fun getId(): Int {
|
||||
return modeId
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return name.tr()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AndroidDisplay(private val activity: Activity) : PlatformDisplay {
|
||||
|
||||
private var display: Display? = null
|
||||
@ -45,6 +23,7 @@ class AndroidDisplay(private val activity: Activity) : PlatformDisplay {
|
||||
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
|
||||
@ -52,7 +31,7 @@ class AndroidDisplay(private val activity: Activity) : PlatformDisplay {
|
||||
}
|
||||
|
||||
// Add default mode
|
||||
displayModes[0] = AndroidScreenMode(0)
|
||||
displayModes[AndroidScreenMode.defaultId] = AndroidScreenMode.default
|
||||
|
||||
// Add other supported modes
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
@ -71,14 +50,31 @@ class AndroidDisplay(private val activity: Activity) : PlatformDisplay {
|
||||
}
|
||||
|
||||
override fun setScreenMode(id: Int, settings: GameSettings) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
activity.runOnUiThread {
|
||||
val params = activity.window.attributes
|
||||
params.preferredDisplayModeId = id
|
||||
activity.window.attributes = params
|
||||
}
|
||||
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 {
|
||||
@ -95,15 +91,19 @@ class AndroidDisplay(private val activity: Activity) : PlatformDisplay {
|
||||
}
|
||||
|
||||
override fun setCutout(enabled: Boolean) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@ -121,7 +121,6 @@ class AndroidDisplay(private val activity: Activity) : PlatformDisplay {
|
||||
}
|
||||
|
||||
override fun setOrientation(orientation: ScreenOrientation) {
|
||||
|
||||
val mode = when (orientation) {
|
||||
ScreenOrientation.Landscape -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
ScreenOrientation.Portrait -> ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
@ -133,4 +132,25 @@ class AndroidDisplay(private val activity: Activity) : PlatformDisplay {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,8 @@ open class AndroidLauncher : AndroidApplication() {
|
||||
Log.backend = AndroidLogBackend(this)
|
||||
|
||||
// Setup Android display
|
||||
Display.platform = AndroidDisplay(this)
|
||||
val displayImpl = AndroidDisplay(this)
|
||||
Display.platform = displayImpl
|
||||
|
||||
// Setup Android fonts
|
||||
Fonts.fontImplementation = AndroidFont()
|
||||
@ -32,18 +33,19 @@ open class AndroidLauncher : AndroidApplication() {
|
||||
UncivFiles.saverLoader = AndroidSaverLoader(this)
|
||||
UncivFiles.preferExternalStorage = true
|
||||
|
||||
val config = AndroidApplicationConfiguration().apply { useImmersiveMode = false }
|
||||
val settings = UncivFiles.getSettingsForPlatformLaunchers(filesDir.path)
|
||||
|
||||
// Setup orientation, immersive mode and display cutout
|
||||
displayImpl.setOrientation(settings.displayOrientation)
|
||||
displayImpl.setCutoutFromUiThread(settings.androidCutout)
|
||||
displayImpl.setSystemUiVisibilityFromUiThread(settings.androidHideSystemUi)
|
||||
|
||||
// Create notification channels for Multiplayer notificator
|
||||
MultiplayerTurnCheckWorker.createNotificationChannels(applicationContext)
|
||||
|
||||
copyMods()
|
||||
|
||||
val config = AndroidApplicationConfiguration().apply { useImmersiveMode = true }
|
||||
val settings = UncivFiles.getSettingsForPlatformLaunchers(filesDir.path)
|
||||
|
||||
// Setup orientation and display cutout
|
||||
Display.setOrientation(settings.displayOrientation)
|
||||
Display.setCutout(settings.androidCutout)
|
||||
|
||||
game = AndroidGame(this)
|
||||
initialize(game, config)
|
||||
|
||||
|
@ -100,6 +100,7 @@ class GameSettings {
|
||||
var showAutosaves: Boolean = false
|
||||
|
||||
var androidCutout: Boolean = false
|
||||
var androidHideSystemUi = true
|
||||
|
||||
var multiplayer = GameSettingsMultiplayer()
|
||||
|
||||
|
@ -22,6 +22,7 @@ import com.unciv.models.metadata.ScreenSize
|
||||
import com.unciv.models.translations.TranslationFileWriter
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
|
||||
import com.unciv.ui.components.extensions.addSeparator
|
||||
import com.unciv.ui.components.extensions.disable
|
||||
import com.unciv.ui.components.extensions.setFontColor
|
||||
import com.unciv.ui.components.extensions.toCheckBox
|
||||
@ -57,34 +58,48 @@ fun advancedTab(
|
||||
val settings = optionsPopup.settings
|
||||
|
||||
addAutosaveTurnsSelectBox(this, settings)
|
||||
addSeparator(Color.GRAY)
|
||||
|
||||
if (Display.hasOrientation()) {
|
||||
if (Display.hasOrientation())
|
||||
addOrientationSelectBox(this, optionsPopup)
|
||||
}
|
||||
|
||||
if (Display.hasCutout()) {
|
||||
if (Display.hasCutout())
|
||||
addCutoutCheckbox(this, optionsPopup)
|
||||
}
|
||||
|
||||
addMaxZoomSlider(this, settings)
|
||||
if (Display.hasSystemUiVisibility())
|
||||
addHideSystemUiCheckbox(this, optionsPopup)
|
||||
|
||||
addFontFamilySelect(this, settings, optionsPopup.selectBoxMinWidth, onFontChange)
|
||||
|
||||
addFontSizeMultiplier(this, settings, onFontChange)
|
||||
addSeparator(Color.GRAY)
|
||||
|
||||
addTranslationGeneration(this, optionsPopup)
|
||||
|
||||
addSetUserId(this, settings)
|
||||
addMaxZoomSlider(this, settings)
|
||||
|
||||
addEasterEggsCheckBox(this, settings)
|
||||
|
||||
addEnlargeNotificationsCheckBox(this, settings)
|
||||
addSeparator(Color.GRAY)
|
||||
|
||||
addSetUserId(this, settings)
|
||||
|
||||
addTranslationGeneration(this, optionsPopup)
|
||||
}
|
||||
|
||||
private fun addCutoutCheckbox(table: Table, optionsPopup: OptionsPopup) {
|
||||
optionsPopup.addCheckbox(table, "Enable display cutout (requires restart)", optionsPopup.settings.androidCutout)
|
||||
optionsPopup.addCheckbox(table, "Enable using display cutout areas", optionsPopup.settings.androidCutout)
|
||||
{
|
||||
optionsPopup.settings.androidCutout = it
|
||||
Display.setCutout(it)
|
||||
optionsPopup.reopenAfterDiplayLayoutChange()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addHideSystemUiCheckbox(table: Table, optionsPopup: OptionsPopup) {
|
||||
optionsPopup.addCheckbox(table, "Hide system status and navigation bars", optionsPopup.settings.androidHideSystemUi)
|
||||
{
|
||||
optionsPopup.settings.androidHideSystemUi = it
|
||||
Display.setSystemUiVisibility(hide = it)
|
||||
optionsPopup.reopenAfterDiplayLayoutChange()
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +116,7 @@ private fun addOrientationSelectBox(table: Table, optionsPopup: OptionsPopup) {
|
||||
val orientation = selectBox.selected
|
||||
settings.displayOrientation = orientation
|
||||
Display.setOrientation(orientation)
|
||||
optionsPopup.reopenAfterDiplayLayoutChange()
|
||||
}
|
||||
|
||||
table.add(selectBox).minWidth(optionsPopup.selectBoxMinWidth).pad(10f).row()
|
||||
@ -202,7 +218,7 @@ private fun addFontSizeMultiplier(
|
||||
settings: GameSettings,
|
||||
onFontChange: () -> Unit
|
||||
) {
|
||||
table.add("Font size multiplier".toLabel()).left().fillX()
|
||||
table.add("Font size multiplier".toLabel()).left().fillX().padTop(5f)
|
||||
|
||||
val fontSizeSlider = UncivSlider(
|
||||
0.7f, 1.5f, 0.05f,
|
||||
@ -214,11 +230,11 @@ private fun addFontSizeMultiplier(
|
||||
if (!fontSizeSlider.isDragging)
|
||||
onFontChange()
|
||||
}
|
||||
table.add(fontSizeSlider).pad(5f).row()
|
||||
table.add(fontSizeSlider).pad(5f).padTop(10f).row()
|
||||
}
|
||||
|
||||
private fun addMaxZoomSlider(table: Table, settings: GameSettings) {
|
||||
table.add("Max zoom out".tr()).left().fillX()
|
||||
table.add("Max zoom out".tr()).left().fillX().padTop(5f)
|
||||
val maxZoomSlider = UncivSlider(
|
||||
2f, 6f, 1f,
|
||||
initial = settings.maxWorldZoomOut
|
||||
@ -227,7 +243,7 @@ private fun addMaxZoomSlider(table: Table, settings: GameSettings) {
|
||||
if (GUI.isWorldLoaded())
|
||||
GUI.getMap().reloadMaxZoom()
|
||||
}
|
||||
table.add(maxZoomSlider).pad(5f).row()
|
||||
table.add(maxZoomSlider).pad(5f).padTop(10f).row()
|
||||
}
|
||||
|
||||
private fun addTranslationGeneration(table: Table, optionsPopup: OptionsPopup) {
|
||||
|
@ -13,11 +13,14 @@ import com.unciv.ui.components.extensions.toCheckBox
|
||||
import com.unciv.ui.components.widgets.TabbedPager
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.popups.Popup
|
||||
import com.unciv.ui.popups.hasOpenPopups
|
||||
import com.unciv.ui.screens.basescreen.BaseScreen
|
||||
import com.unciv.ui.screens.basescreen.RecreateOnResize
|
||||
import com.unciv.ui.screens.mainmenuscreen.MainMenuScreen
|
||||
import com.unciv.ui.screens.worldscreen.WorldScreen
|
||||
import com.unciv.utils.Concurrency
|
||||
import com.unciv.utils.withGLContext
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
/**
|
||||
@ -160,6 +163,22 @@ class OptionsPopup(
|
||||
}
|
||||
}
|
||||
|
||||
/** Call if an option change might trigger a Screen.resize
|
||||
*
|
||||
* Does nothing if any Popup (which can only be this one) is still open after a short delay and context yield.
|
||||
* Reason: A resize might relaunch the parent screen ([MainMenuScreen] is [RecreateOnResize]) and thus close this Popup.
|
||||
*/
|
||||
fun reopenAfterDiplayLayoutChange() {
|
||||
Concurrency.run("Reload from options") {
|
||||
delay(100)
|
||||
withGLContext {
|
||||
val screen = UncivGame.Current.screen ?: return@withGLContext
|
||||
if (screen.hasOpenPopups()) return@withGLContext // e.g. Orientation auto to fixed while auto is already the new orientation
|
||||
screen.openOptionsPopup(tabs.activePage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addCheckbox(table: Table, text: String, initialState: Boolean, updateWorld: Boolean = false, newRow: Boolean = true, action: ((Boolean) -> Unit)) {
|
||||
val checkbox = text.toCheckBox(initialState) {
|
||||
action(it)
|
||||
|
@ -19,31 +19,35 @@ interface ScreenMode {
|
||||
}
|
||||
|
||||
interface PlatformDisplay {
|
||||
|
||||
fun setScreenMode(id: Int, settings: GameSettings) {}
|
||||
fun getScreenModes(): Map<Int, ScreenMode> { return hashMapOf() }
|
||||
fun getScreenModes(): Map<Int, ScreenMode> = hashMapOf()
|
||||
|
||||
fun hasCutout(): Boolean { return false }
|
||||
fun hasCutout(): Boolean = false
|
||||
fun setCutout(enabled: Boolean) {}
|
||||
|
||||
fun hasOrientation(): Boolean { return false }
|
||||
fun hasOrientation(): Boolean = false
|
||||
fun setOrientation(orientation: ScreenOrientation) {}
|
||||
|
||||
fun hasUserSelectableSize(id: Int): Boolean = false
|
||||
|
||||
fun hasSystemUiVisibility(): Boolean = false
|
||||
fun setSystemUiVisibility(hide: Boolean) {}
|
||||
}
|
||||
|
||||
object Display {
|
||||
|
||||
lateinit var platform: PlatformDisplay
|
||||
|
||||
fun hasOrientation(): Boolean { return platform.hasOrientation() }
|
||||
fun hasOrientation() = platform.hasOrientation()
|
||||
fun setOrientation(orientation: ScreenOrientation) { platform.setOrientation(orientation) }
|
||||
|
||||
fun hasCutout(): Boolean { return platform.hasCutout() }
|
||||
fun hasCutout() = platform.hasCutout()
|
||||
fun setCutout(enabled: Boolean) { platform.setCutout(enabled) }
|
||||
|
||||
fun getScreenModes(): Map<Int, ScreenMode> { return platform.getScreenModes() }
|
||||
fun getScreenModes() = platform.getScreenModes()
|
||||
fun setScreenMode(id: Int, settings: GameSettings) { platform.setScreenMode(id, settings) }
|
||||
|
||||
fun hasUserSelectableSize(id: Int) = platform.hasUserSelectableSize(id)
|
||||
|
||||
fun hasSystemUiVisibility() = platform.hasSystemUiVisibility()
|
||||
fun setSystemUiVisibility(hide: Boolean) = platform.setSystemUiVisibility(hide)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user