From b5ce086860006d88df32d424d24ca4e351a26eaa Mon Sep 17 00:00:00 2001 From: vegeta1k95 <32207817+vegeta1k95@users.noreply.github.com> Date: Thu, 23 Feb 2023 21:40:58 +0100 Subject: [PATCH] Android: use best possible device frame rate (#8728) * Android: use best possible device frame rate * Add support for devices with Android 6.0+ --------- Co-authored-by: vegeta1k95 --- android/src/com/unciv/app/AndroidLauncher.kt | 48 +++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/android/src/com/unciv/app/AndroidLauncher.kt b/android/src/com/unciv/app/AndroidLauncher.kt index a6475f3d6b..fbda5c5f0e 100644 --- a/android/src/com/unciv/app/AndroidLauncher.kt +++ b/android/src/com/unciv/app/AndroidLauncher.kt @@ -2,10 +2,16 @@ package com.unciv.app import android.content.Intent import android.graphics.Rect +import android.hardware.display.DisplayManager 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 androidx.annotation.RequiresApi import androidx.core.app.NotificationManagerCompat import androidx.work.WorkManager import com.badlogic.gdx.Gdx @@ -59,11 +65,39 @@ open class AndroidLauncher : AndroidApplication() { setDeepLinkedGame(intent) - addScreenObscuredListener((Gdx.graphics as AndroidGraphics).view) + val glView = (Gdx.graphics as AndroidGraphics).view as GLSurfaceView + + addScreenObscuredListener(glView) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + addScreenRefreshRateListener(glView) } - private fun addScreenObscuredListener(contentView: View) { - contentView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { + /** 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. * So we need to check if that was actually the thing that changed. */ private var lastVisibleDisplayFrame: Rect? = null @@ -73,18 +107,18 @@ open class AndroidLauncher : AndroidApplication() { return } val r = Rect() - contentView.getWindowVisibleDisplayFrame(r) + surfaceView.getWindowVisibleDisplayFrame(r) if (r.equals(lastVisibleDisplayFrame)) return lastVisibleDisplayFrame = r val stage = (UncivGame.Current.screen as BaseScreen).stage - val horizontalRatio = stage.width / contentView.width - val verticalRatio = stage.height / contentView.height + val horizontalRatio = stage.width / surfaceView.width + val verticalRatio = stage.height / surfaceView.height val visibleStage = Rectangle( r.left * horizontalRatio, - (contentView.height - r.bottom) * verticalRatio, // Android coordinate system has the origin in the top left, while GDX uses bottom left + (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 )