Android: selectable orientation (#8822)

* Android: selectable orientation

* Fix visible rectangle bug, fix auto-rotate behaviour, add translations

* Fix translation

---------

Co-authored-by: vegeta1k95 <vfylfhby>
This commit is contained in:
vegeta1k95
2023-03-06 09:35:14 +01:00
committed by GitHub
parent 41ee20efc5
commit b57232c992
11 changed files with 201 additions and 169 deletions

View File

@ -1,39 +1,20 @@
package com.unciv.app
import android.content.Intent
import android.content.pm.ActivityInfo
import android.graphics.Rect
import android.net.Uri
import android.opengl.GLSurfaceView
import android.os.Build
import android.os.Bundle
import android.view.Surface
import android.view.SurfaceHolder
import android.view.View
import android.view.ViewTreeObserver
import android.view.WindowManager
import androidx.core.app.NotificationManagerCompat
import androidx.work.WorkManager
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.backends.android.AndroidApplication
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration
import com.badlogic.gdx.backends.android.AndroidGraphics
import com.badlogic.gdx.math.Rectangle
import com.unciv.UncivGame
import com.unciv.logic.files.UncivFiles
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: AndroidGame? = null
private var deepLinkedMultiplayerGame: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -59,68 +40,15 @@ open class AndroidLauncher : AndroidApplication() {
val config = AndroidApplicationConfiguration().apply { useImmersiveMode = true }
val settings = UncivFiles.getSettingsForPlatformLaunchers(filesDir.path)
// Setup orientation lock and display cutout
allowPortrait(settings.allowAndroidPortrait)
setDisplayCutout(settings.androidCutout)
// Setup orientation and display cutout
Display.setOrientation(settings.displayOrientation)
Display.setCutout(settings.androidCutout)
game = AndroidGame(this)
game = AndroidGame()
initialize(game, config)
setDeepLinkedGame(intent)
val glView = (Gdx.graphics as AndroidGraphics).view as GLSurfaceView
addScreenObscuredListener(glView)
}
fun allowPortrait(allow: Boolean) {
val orientation = when {
allow -> ActivityInfo.SCREEN_ORIENTATION_USER
else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
}
// Comparison ensures ActivityTaskManager.getService().setRequestedOrientation isn't called unless necessary
if (requestedOrientation != orientation) requestedOrientation = orientation
}
private fun setDisplayCutout(cutout: Boolean) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return
window.attributes.layoutInDisplayCutoutMode = when {
cutout -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
else -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
}
}
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.
* So we need to check if that was actually the thing that changed. */
private var lastVisibleDisplayFrame: Rect? = null
override fun onGlobalLayout() {
if (!UncivGame.isCurrentInitialized() || UncivGame.Current.screen == null) {
return
}
val r = Rect()
surfaceView.getWindowVisibleDisplayFrame(r)
if (r.equals(lastVisibleDisplayFrame)) return
lastVisibleDisplayFrame = r
val stage = (UncivGame.Current.screen as BaseScreen).stage
val horizontalRatio = stage.width / surfaceView.width
val verticalRatio = stage.height / surfaceView.height
val visibleStage = Rectangle(
r.left * horizontalRatio,
(surfaceView.height - r.bottom) * verticalRatio, // Android coordinate system has the origin in the top left, while GDX uses bottom left
r.width() * horizontalRatio,
r.height() * verticalRatio
)
Concurrency.runOnGLThread {
EventBus.send(UncivStage.VisibleAreaChanged(visibleStage))
}
}
})
game!!.setDeepLinkedGame(intent)
game!!.addScreenObscuredListener()
}
/**
@ -141,34 +69,29 @@ open class AndroidLauncher : AndroidApplication() {
}
override fun onPause() {
if (UncivGame.isCurrentInitialized()
&& UncivGame.Current.gameInfo != null
&& UncivGame.Current.settings.multiplayer.turnCheckerEnabled
&& UncivGame.Current.files.getMultiplayerSaves().any()
val game = this.game!!
if (game.isInitialized
&& game.gameInfo != null
&& game.settings.multiplayer.turnCheckerEnabled
&& game.files.getMultiplayerSaves().any()
) {
MultiplayerTurnCheckWorker.startTurnChecker(
applicationContext, UncivGame.Current.files,
UncivGame.Current.gameInfo!!, UncivGame.Current.settings.multiplayer
)
applicationContext, game.files, game.gameInfo!!, game.settings.multiplayer)
}
super.onPause()
}
override fun onResume() {
try { // Sometimes this fails for no apparent reason - the multiplayer checker failing to cancel should not be enough of a reason for the game to crash!
try {
WorkManager.getInstance(applicationContext).cancelAllWorkByTag(MultiplayerTurnCheckWorker.WORK_TAG)
with(NotificationManagerCompat.from(this)) {
cancel(MultiplayerTurnCheckWorker.NOTIFICATION_ID_INFO)
cancel(MultiplayerTurnCheckWorker.NOTIFICATION_ID_SERVICE)
}
} catch (ex: Exception) {
} catch (ignore: Exception) {
/* Sometimes this fails for no apparent reason - the multiplayer checker failing to
cancel should not be enough of a reason for the game to crash! */
}
if (deepLinkedMultiplayerGame != null) {
game?.deepLinkedMultiplayerGame = deepLinkedMultiplayerGame
deepLinkedMultiplayerGame = null
}
super.onResume()
}
@ -176,17 +99,7 @@ open class AndroidLauncher : AndroidApplication() {
super.onNewIntent(intent)
if (intent == null)
return
setDeepLinkedGame(intent)
}
private fun setDeepLinkedGame(intent: Intent) {
// This is needed in onCreate _and_ onNewIntent to open links and notifications
// correctly even if the app was not running
deepLinkedMultiplayerGame = if (intent.action != Intent.ACTION_VIEW) null else {
val uri: Uri? = intent.data
uri?.getQueryParameter("id")
}
game?.setDeepLinkedGame(intent)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {