Android: "Screen Mode" option (#8785)

* Android: "Screen Mode" option

* Fix broken tests, code cleanup

* Fix broken tests, code cleanup

* Fix broken tests, code cleanup

---------

Co-authored-by: vegeta1k95 <vfylfhby>
This commit is contained in:
vegeta1k95
2023-03-04 18:22:09 +01:00
committed by GitHub
parent c40b6159df
commit f55e010451
9 changed files with 227 additions and 65 deletions

View File

@ -1380,7 +1380,7 @@ Movement cost =
for =
Missing translations: =
Screen Size =
Screen Window =
Screen Mode =
Windowed =
Fullscreen =
Tileset =

View File

@ -0,0 +1,83 @@
package com.unciv.app
import android.app.Activity
import android.os.Build
import android.view.Display
import android.view.Display.Mode
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
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
private var displayModes: HashMap<Int, ScreenMode> = hashMapOf()
init {
// Fetch current display
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[0] = AndroidScreenMode(0)
// 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<Int, ScreenMode> {
return displayModes
}
override fun getDefaultMode(): ScreenMode {
return displayModes[0]!!
}
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
}
}
}
}

View File

@ -25,13 +25,14 @@ import com.unciv.logic.event.EventBus
import com.unciv.ui.components.Fonts
import com.unciv.ui.screens.basescreen.UncivStage
import com.unciv.ui.screens.basescreen.BaseScreen
import com.unciv.utils.Display
import com.unciv.utils.Log
import com.unciv.utils.concurrency.Concurrency
import java.io.File
open class AndroidLauncher : AndroidApplication() {
private var game: UncivGame? = null
private var game: AndroidGame? = null
private var deepLinkedMultiplayerGame: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
@ -40,6 +41,9 @@ open class AndroidLauncher : AndroidApplication() {
// Setup Android logging
Log.backend = AndroidLogBackend()
// Setup Android display
Display.platform = AndroidDisplay(this)
// Setup Android fonts
Fonts.fontImplementation = AndroidFont()
@ -67,9 +71,6 @@ open class AndroidLauncher : AndroidApplication() {
val glView = (Gdx.graphics as AndroidGraphics).view as GLSurfaceView
addScreenObscuredListener(glView)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
addScreenRefreshRateListener(glView)
}
fun allowPortrait(allow: Boolean) {
@ -89,29 +90,6 @@ open class AndroidLauncher : AndroidApplication() {
}
}
/** Request the best available device frame rate for
* the game, as soon as OpenGL surface is created */
private fun addScreenRefreshRateListener(surfaceView: GLSurfaceView) {
surfaceView.holder.addCallback(object: SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val modes = display?.supportedModes ?: return
val bestRefreshRate = modes.maxOf { it.refreshRate }
holder.surface.setFrameRate(bestRefreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val display = windowManager.defaultDisplay
val modes = display?.supportedModes ?: return
val bestMode = modes.maxBy { it.refreshRate }
val params = window.attributes
params.preferredDisplayModeId = bestMode.modeId
window.attributes = params
}
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}
override fun surfaceDestroyed(holder: SurfaceHolder) {}
})
}
private fun addScreenObscuredListener(surfaceView: GLSurfaceView) {
surfaceView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
/** [onGlobalLayout] gets triggered not only when the [windowVisibleDisplayFrame][View.getWindowVisibleDisplayFrame] changes, but also on other things.

View File

@ -42,6 +42,7 @@ import com.unciv.ui.screens.worldscreen.WorldMapHolder
import com.unciv.ui.screens.worldscreen.WorldScreen
import com.unciv.ui.screens.worldscreen.unit.UnitTable
import com.unciv.utils.DebugUtils
import com.unciv.utils.Display
import com.unciv.utils.Log
import com.unciv.utils.PlatformSpecific
import com.unciv.utils.concurrency.Concurrency
@ -158,7 +159,7 @@ open class UncivGame(val isConsoleMode: Boolean = false) : Game(), PlatformSpeci
* - Font (hence Fonts.resetFont() inside setSkin())
*/
settings = files.getGeneralSettings() // needed for the screen
settings.refreshScreenMode()
Display.setScreenMode(settings.screenMode, settings)
setAsRootScreen(GameStartScreen()) // NOT dependent on any atlas or skin
GameSounds.init()

View File

@ -9,6 +9,8 @@ import com.unciv.logic.multiplayer.FriendList
import com.unciv.models.UncivSound
import com.unciv.ui.components.FontFamilyData
import com.unciv.ui.components.Fonts
import com.unciv.utils.Display
import com.unciv.utils.ScreenMode
import java.text.Collator
import java.time.Duration
import java.util.*
@ -25,11 +27,6 @@ enum class ScreenSize(val virtualWidth:Float, val virtualHeight:Float){
Huge(1500f,1000f)
}
enum class ScreenWindow {
Windowed,
Fullscreen
}
class GameSettings {
var mapAutoScroll: Boolean = false
@ -47,7 +44,7 @@ class GameSettings {
@Deprecated("Since 4.3.6 - replaces with screenSize")
var resolution: String = "900x600"
var screenSize:ScreenSize = ScreenSize.Small
var screenWindow: ScreenWindow = ScreenWindow.Windowed
var screenMode: Int = 0
var tutorialsShown = HashSet<String>()
var tutorialTasksCompleted = HashSet<String>()
@ -154,24 +151,6 @@ class GameSettings {
fun getCollatorFromLocale(): Collator {
return Collator.getInstance(getCurrentLocale())
}
fun refreshScreenMode() {
if (Gdx.app.type != Application.ApplicationType.Desktop)
return
when (screenWindow) {
ScreenWindow.Windowed -> {
Gdx.graphics.setWindowedMode(
windowState.width.coerceAtLeast(120),
windowState.height.coerceAtLeast(80))
}
ScreenWindow.Fullscreen -> {
Gdx.graphics.setFullscreenMode(Gdx.graphics.displayMode)
}
}
}
}
enum class LocaleCode(var language: String, var country: String) {

View File

@ -10,7 +10,6 @@ import com.unciv.GUI
import com.unciv.UncivGame
import com.unciv.models.metadata.GameSettings
import com.unciv.models.metadata.ScreenSize
import com.unciv.models.metadata.ScreenWindow
import com.unciv.models.skins.SkinCache
import com.unciv.models.tilesets.TileSetCache
import com.unciv.models.translations.tr
@ -25,8 +24,8 @@ import com.unciv.ui.components.extensions.onChange
import com.unciv.ui.components.extensions.onClick
import com.unciv.ui.components.extensions.toLabel
import com.unciv.ui.components.extensions.toTextButton
private val resolutionArray = com.badlogic.gdx.utils.Array(arrayOf("750x500", "900x600", "1050x700", "1200x800", "1500x1000"))
import com.unciv.utils.Display
import com.unciv.utils.ScreenMode
fun displayTab(
optionsPopup: OptionsPopup,
@ -37,8 +36,9 @@ fun displayTab(
val settings = optionsPopup.settings
addScreenModeSelectBox(this, settings, optionsPopup.selectBoxMinWidth)
if (Gdx.app.type == Application.ApplicationType.Desktop) {
addFullscreenSelectBox(this, settings, optionsPopup.selectBoxMinWidth)
optionsPopup.addCheckbox(this, "Map mouse auto-scroll", settings.mapAutoScroll, true) {
settings.mapAutoScroll = it
if (GUI.isWorldLoaded())
@ -140,16 +140,22 @@ private fun addUnitIconAlphaSlider(table: Table, settings: GameSettings, selectB
table.add(unitIconAlphaSlider).minWidth(selectBoxMinWidth).pad(10f).row()
}
private fun addFullscreenSelectBox(table: Table, settings: GameSettings, selectBoxMinWidth: Float) {
table.add("Screen Window".toLabel()).left().fillX()
private fun addScreenModeSelectBox(table: Table, settings: GameSettings, selectBoxMinWidth: Float) {
table.add("Screen Mode".toLabel()).left().fillX()
val screenSizeSelectBox = TranslatedSelectBox(ScreenWindow.values().map { it.name }, settings.screenWindow.name,table.skin)
table.add(screenSizeSelectBox).minWidth(selectBoxMinWidth).pad(10f).row()
val modes = Display.getScreenModes()
val current: ScreenMode? = modes[settings.screenMode]
screenSizeSelectBox.onChange {
settings.screenWindow = ScreenWindow.valueOf(screenSizeSelectBox.selected.value)
settings.refreshScreenMode()
val selectBox = SelectBox<ScreenMode>(table.skin)
selectBox.items = Array(modes.values.toTypedArray())
selectBox.selected = current
selectBox.onChange {
val mode = selectBox.selected
settings.screenMode = mode.getId()
Display.setScreenMode(mode.getId(), settings)
}
table.add(selectBox).minWidth(selectBoxMinWidth).pad(10f).row()
}
private fun addScreenSizeSelectBox(table: Table, settings: GameSettings, selectBoxMinWidth: Float, onResolutionChange: () -> Unit) {

View File

@ -0,0 +1,33 @@
package com.unciv.utils
import com.unciv.models.metadata.GameSettings
interface ScreenMode {
fun getId(): Int
}
interface PlatformDisplay {
fun setScreenMode(id: Int, settings: GameSettings) {}
fun getScreenModes(): Map<Int, ScreenMode> { return hashMapOf() }
fun getDefaultMode(): ScreenMode
}
object Display {
lateinit var platform: PlatformDisplay
fun getDefaultMode(): ScreenMode {
return platform.getDefaultMode()
}
fun getScreenModes(): Map<Int, ScreenMode> {
return platform.getScreenModes()
}
fun setScreenMode(id: Int, settings: GameSettings) {
platform.setScreenMode(id, settings)
}
}

View File

@ -0,0 +1,79 @@
package com.unciv.app.desktop
import com.badlogic.gdx.Gdx
import com.unciv.models.metadata.GameSettings
import com.unciv.models.translations.tr
import com.unciv.utils.PlatformDisplay
import com.unciv.utils.ScreenMode
enum class ScreenWindowType {
Windowed,
Borderless,
Fullscreen
}
class DesktopScreenMode(
private val modeId: Int,
val windowType: ScreenWindowType) : ScreenMode {
override fun getId(): Int {
return modeId
}
override fun toString(): String {
return when (windowType) {
ScreenWindowType.Windowed -> "Windowed".tr()
ScreenWindowType.Borderless -> "Borderless".tr()
ScreenWindowType.Fullscreen -> "Fullscreen".tr()
}
}
}
class DesktopDisplay : PlatformDisplay {
private val modes = HashMap<Int, DesktopScreenMode>()
init {
modes[0] = DesktopScreenMode(0, ScreenWindowType.Windowed)
modes[1] = DesktopScreenMode(1, ScreenWindowType.Fullscreen)
modes[2] = DesktopScreenMode(2, ScreenWindowType.Borderless)
}
override fun getDefaultMode(): ScreenMode {
return modes[0]!!
}
override fun getScreenModes(): Map<Int, ScreenMode> {
return modes
}
override fun setScreenMode(id: Int, settings: GameSettings) {
val mode = modes[id] ?: return
when (mode.windowType) {
ScreenWindowType.Fullscreen -> {
Gdx.graphics.setFullscreenMode(Gdx.graphics.displayMode)
}
ScreenWindowType.Windowed -> {
Gdx.graphics.setUndecorated(false)
Gdx.graphics.setWindowedMode(
settings.windowState.width.coerceAtLeast(120),
settings.windowState.height.coerceAtLeast(80)
)
}
ScreenWindowType.Borderless -> {
Gdx.graphics.setUndecorated(true)
Gdx.graphics.setWindowedMode(
settings.windowState.width.coerceAtLeast(120),
settings.windowState.height.coerceAtLeast(80)
)
}
}
}
}

View File

@ -10,6 +10,7 @@ import com.unciv.logic.files.UncivFiles
import com.unciv.models.metadata.ScreenSize
import com.unciv.models.metadata.WindowState
import com.unciv.ui.components.Fonts
import com.unciv.utils.Display
import com.unciv.utils.Log
import java.awt.GraphicsEnvironment
@ -21,6 +22,9 @@ internal object DesktopLauncher {
// Setup Desktop logging
Log.backend = DesktopLogBackend()
// Setup Desktop display
Display.platform = DesktopDisplay()
// Setup Desktop font
Fonts.fontImplementation = DesktopFont()
@ -63,7 +67,6 @@ internal object DesktopLauncher {
FileHandle(SETTINGS_FILE_NAME).writeString(json().toJson(settings), false) // so when we later open the game we get fullscreen
}
if (!isRunFromJAR) {
UniqueDocsWriter().write()
UiElementDocsWriter().write()