Compare commits

...

7 Commits

Author SHA1 Message Date
Abandoned Cart
32f869a1df android: Reshape the layout to fit rotation 2023-06-05 20:52:02 -04:00
Abandoned Cart
1afc9f2093 android: Add settings for variable orientation 2023-06-05 20:52:02 -04:00
Abandoned Cart
4c3d0b9c23 android: Add an aspect ratio adaptation 2023-06-05 20:52:02 -04:00
Abandoned Cart
069e3406c7 android: Dynamic picture in picture actions 2023-06-05 20:52:02 -04:00
Abandoned Cart
74cabd0ff0 android: Rebuild PiP parameters on change 2023-06-05 20:52:02 -04:00
Abandoned Cart
b03052843b android: Add a picture in picture setting 2023-06-05 20:52:02 -04:00
Abandoned Cart
fa58cbe0d2 android: Add Picture in Picture mode 2023-06-05 20:52:01 -04:00
14 changed files with 359 additions and 71 deletions

View File

@@ -60,6 +60,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
android:theme="@style/Theme.Yuzu.Main"
android:launchMode="singleTop"
android:screenOrientation="userLandscape"
android:supportsPictureInPicture="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
android:exported="true">
<intent-filter>

View File

@@ -4,26 +4,35 @@
package org.yuzu.yuzu_emu.activities
import android.app.Activity
import android.app.PendingIntent
import android.app.PictureInPictureParams
import android.app.RemoteAction
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.graphics.Rect
import android.graphics.drawable.Icon
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.hardware.display.DisplayManager
import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
import android.util.Rational
import android.view.Display
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.Surface
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
@@ -35,11 +44,13 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NfcReader
@@ -60,10 +71,15 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private var motionTimestamp: Long = 0
private var flipMotionOrientation: Boolean = false
private val actionPause = "ACTION_EMULATOR_PAUSE"
private val actionPlay = "ACTION_EMULATOR_PLAY"
private lateinit var game: Game
private val settingsViewModel: SettingsViewModel by viewModels()
var screenDimensions : IntArray = intArrayOf()
override fun onDestroy() {
stopForegroundService(this)
super.onDestroy()
@@ -91,6 +107,8 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
setContentView(R.layout.activity_emulation)
window.decorView.setBackgroundColor(getColor(android.R.color.black))
screenDimensions = getDisplayParams()
// Find or create the EmulationFragment
emulationFragment =
supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
@@ -112,7 +130,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(this@EmulationActivity)
.windowLayoutInfo(this@EmulationActivity)
.collect { emulationFragment?.updateCurrentLayout(this@EmulationActivity, it) }
.collect { emulationFragment?.updateFoldableLayout(this@EmulationActivity, it) }
}
}
@@ -150,9 +168,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
startMotionSensorListener()
NativeLibrary.notifyOrientationChange(
EmulationMenuSettings.landscapeScreenLayout,
getAdjustedRotation()
Settings.LayoutOption_MobileLandscape,
Surface.ROTATION_90
)
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
pictureInPictureParamsBuilder.setAutoEnterEnabled(BooleanSetting.PICTURE_IN_PICTURE.boolean)
}
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
override fun onPause() {
@@ -161,6 +186,16 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
stopMotionSensorListener()
}
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.boolean && !isInPictureInPictureMode) {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
@@ -258,23 +293,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
private fun getAdjustedRotation():Int {
val rotation = getSystemService<DisplayManager>()!!.getDisplay(Display.DEFAULT_DISPLAY).rotation
val config: Configuration = resources.configuration
if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 ||
(config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0) {
return rotation
}
when (rotation) {
Surface.ROTATION_0 -> return Surface.ROTATION_90
Surface.ROTATION_90 -> return Surface.ROTATION_0
Surface.ROTATION_180 -> return Surface.ROTATION_270
Surface.ROTATION_270 -> return Surface.ROTATION_180
}
return rotation
}
private fun restoreState(savedInstanceState: Bundle) {
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
}
@@ -289,6 +307,82 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
}
}
private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder() : PictureInPictureParams.Builder {
val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.int) {
0 -> Rational(16, 9)
1 -> Rational(4, 3)
2 -> Rational(21, 9)
3 -> Rational(16, 10)
else -> null
}
return this.apply { aspectRatio?.let { setAspectRatio(it) } }
}
private fun getDisplayParams(): IntArray {
return with (getSystemService(WINDOW_SERVICE) as WindowManager) {
val metrics = maximumWindowMetrics.bounds
intArrayOf(metrics.width(), metrics.height())
}
}
private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder() : PictureInPictureParams.Builder {
val pictureInPictureActions : MutableList<RemoteAction> = mutableListOf()
val pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
val isEmulationPaused = emulationFragment?.isEmulationStatePaused() ?: false
if (isEmulationPaused) {
val playIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_play)
val playPendingIntent = PendingIntent.getBroadcast(
this@EmulationActivity, R.drawable.ic_pip_play, Intent(actionPlay), pendingFlags
)
val playRemoteAction = RemoteAction(playIcon, getString(R.string.play), getString(R.string.play), playPendingIntent)
pictureInPictureActions.add(playRemoteAction)
} else {
val pauseIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_pause)
val pausePendingIntent = PendingIntent.getBroadcast(
this@EmulationActivity, R.drawable.ic_pip_pause, Intent(actionPause), pendingFlags
)
val pauseRemoteAction = RemoteAction(pauseIcon, getString(R.string.pause), getString(R.string.pause), pausePendingIntent)
pictureInPictureActions.add(pauseRemoteAction)
}
return this.apply { setActions(pictureInPictureActions) }
}
private var pictureInPictureReceiver = object : BroadcastReceiver() {
override fun onReceive(context : Context?, intent : Intent) {
if (intent.action == actionPlay) {
emulationFragment?.onPictureInPicturePlay()
} else if (intent.action == actionPause) {
emulationFragment?.onPictureInPicturePause()
}
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
pictureInPictureParamsBuilder.setAutoEnterEnabled(BooleanSetting.PICTURE_IN_PICTURE.boolean)
}
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
if (isInPictureInPictureMode) {
IntentFilter().apply {
addAction(actionPause)
addAction(actionPlay)
}.also {
registerReceiver(pictureInPictureReceiver, it)
}
emulationFragment?.onPictureInPictureEnter()
} else {
try {
unregisterReceiver(pictureInPictureReceiver)
} catch (ignored : Exception) { }
emulationFragment?.onPictureInPictureLeave()
}
}
private fun startMotionSensorListener() {
val sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)

View File

@@ -8,6 +8,7 @@ enum class BooleanSetting(
override val section: String,
override val defaultValue: Boolean
) : AbstractBooleanSetting {
PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true),
USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false);
override var boolean: Boolean = defaultValue
@@ -27,6 +28,7 @@ enum class BooleanSetting(
companion object {
private val NOT_RUNTIME_EDITABLE = listOf(
PICTURE_IN_PICTURE,
USE_CUSTOM_RTC
)

View File

@@ -88,6 +88,11 @@ enum class IntSetting(
Settings.SECTION_RENDERER,
0
),
RENDERER_SCREEN_LAYOUT(
"screen_layout",
Settings.SECTION_RENDERER,
Settings.LayoutOption_MobileLandscape
),
RENDERER_ASPECT_RATIO(
"aspect_ratio",
Settings.SECTION_RENDERER,

View File

@@ -133,7 +133,6 @@ class Settings {
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
const val PREF_MENU_SETTINGS_LANDSCAPE = "EmulationMenuSettings_LandscapeScreenLayout"
const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
@@ -144,6 +143,14 @@ class Settings {
private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
// These must match what is defined in src/core/settings.h
const val LayoutOption_Default = 0
const val LayoutOption_SingleScreen = 1
const val LayoutOption_LargeScreen = 2
const val LayoutOption_SideScreen = 3
const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5
init {
configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] =
listOf(

View File

@@ -16,6 +16,7 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import android.view.ViewGroup.MarginLayoutParams
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.core.view.updatePadding
import com.google.android.material.color.MaterialColors
import org.yuzu.yuzu_emu.NativeLibrary
@@ -239,5 +240,12 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
settings.putExtra(ARG_GAME_ID, gameId)
context.startActivity(settings)
}
fun launch(context: Context, launcher: ActivityResultLauncher<Intent>, menuTag: String?, gameId: String?) {
val settings = Intent(context, SettingsActivity::class.java)
settings.putExtra(ARG_MENU_TAG, menuTag)
settings.putExtra(ARG_GAME_ID, gameId)
launcher.launch(settings)
}
}
}

View File

@@ -166,6 +166,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.CPU_ACCURACY.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.PICTURE_IN_PICTURE,
R.string.picture_in_picture,
R.string.picture_in_picture_description,
BooleanSetting.PICTURE_IN_PICTURE.key,
BooleanSetting.PICTURE_IN_PICTURE.defaultValue
)
)
}
}
@@ -283,6 +292,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.RENDERER_ANTI_ALIASING.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_SCREEN_LAYOUT,
R.string.renderer_screen_layout,
0,
R.array.rendererScreenLayoutNames,
R.array.rendererScreenLayoutValues,
IntSetting.RENDERER_SCREEN_LAYOUT.key,
IntSetting.RENDERER_SCREEN_LAYOUT.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_ASPECT_RATIO,

View File

@@ -7,10 +7,13 @@ import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Color
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -18,11 +21,15 @@ import android.util.TypedValue
import android.view.*
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.getSystemService
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
@@ -36,6 +43,7 @@ import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
@@ -54,11 +62,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var game: Game
private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent>
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
emulationActivity = context
NativeLibrary.setEmulationActivity(context)
onReturnFromSettings = context.activityResultRegistry.register(
"SettingsResult", ActivityResultContracts.StartActivityForResult()
) {
updateScreenLayout()
}
} else {
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
}
@@ -123,7 +139,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
R.id.menu_settings -> {
SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
SettingsActivity.launch(
requireContext(), onReturnFromSettings, SettingsFile.FILE_NAME_CONFIG, ""
)
true
}
@@ -178,6 +196,93 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
super.onDetach()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val emulatorLayout = when (newConfig.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> { Settings.LayoutOption_MobileLandscape }
Configuration.ORIENTATION_PORTRAIT -> { Settings.LayoutOption_MobilePortrait }
else -> { Settings.LayoutOption_MobilePortrait }
}
emulationActivity?.let {
var rotation = it.getSystemService<DisplayManager>()!!
.getDisplay(Display.DEFAULT_DISPLAY).rotation
if (it.isInPictureInPictureMode) {
NativeLibrary.notifyOrientationChange(
Settings.LayoutOption_MobileLandscape,
Surface.ROTATION_90
)
} else {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
rotation = when (rotation) {
Surface.ROTATION_0 -> Surface.ROTATION_90
Surface.ROTATION_90 -> Surface.ROTATION_0
Surface.ROTATION_180 -> Surface.ROTATION_270
Surface.ROTATION_270 -> Surface.ROTATION_180
else -> { rotation }
}
}
getScaledOrDefaultView(newConfig)
NativeLibrary.notifyOrientationChange(emulatorLayout, rotation)
}
}
}
private fun getScaledOrDefaultView(config: Configuration?) {
emulationActivity?.let {
val orientation = config?.orientation ?: resources.configuration.orientation
if (orientation == Configuration.ORIENTATION_PORTRAIT && !it.isInPictureInPictureMode) {
if (it.screenDimensions.isNotEmpty()) {
binding.surfaceEmulation.layoutParams.width = it.screenDimensions[0]
binding.overlayContainer.layoutParams.width = it.screenDimensions[0]
binding.surfaceEmulation.layoutParams.height = it.screenDimensions[1]
binding.overlayContainer.layoutParams.height = it.screenDimensions[1]
}
} else {
binding.surfaceEmulation.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
binding.overlayContainer.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
binding.surfaceEmulation.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
}
binding.surfaceEmulation.requestLayout()
binding.overlayContainer.requestLayout()
}
}
fun isEmulationStatePaused() : Boolean {
return this::emulationState.isInitialized && emulationState.isPaused
}
fun onPictureInPictureEnter() {
getScaledOrDefaultView(null)
if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close()
}
if (EmulationMenuSettings.showOverlay) {
binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false }
}
}
fun onPictureInPicturePause() {
if (!emulationState.isPaused) {
emulationState.pause()
}
}
fun onPictureInPicturePlay() {
if (emulationState.isPaused) {
emulationState.run(false)
}
}
fun onPictureInPictureLeave() {
if (EmulationMenuSettings.showOverlay) {
binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true }
}
getScaledOrDefaultView(null)
}
private fun refreshInputOverlay() {
binding.surfaceInputOverlay.refreshControls()
}
@@ -217,9 +322,28 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
@SuppressLint("SourceLockedOrientationActivity")
private fun updateScreenLayout() {
emulationActivity?.let {
when (IntSetting.RENDERER_SCREEN_LAYOUT.int) {
Settings.LayoutOption_MobileLandscape -> {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
}
Settings.LayoutOption_MobilePortrait -> {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
}
Settings.LayoutOption_Default -> {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
else -> { it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE }
}
onConfigurationChanged(resources.configuration)
}
}
private val Number.toPx get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), Resources.getSystem().displayMetrics).toInt()
fun updateCurrentLayout(emulationActivity: EmulationActivity, newLayoutInfo: WindowLayoutInfo) {
fun updateFoldableLayout(emulationActivity: EmulationActivity, newLayoutInfo: WindowLayoutInfo) {
val isFolding = (newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let {
if (it.isSeparating) {
emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
@@ -237,9 +361,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.overlayContainer.updatePadding(0, 0, 0, 0)
emulationActivity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
updateScreenLayout()
}
binding.surfaceInputOverlay.requestLayout()
binding.surfaceEmulation.requestLayout()
binding.inGameMenu.requestLayout()
binding.overlayContainer.requestLayout()
}

View File

@@ -11,14 +11,6 @@ object EmulationMenuSettings {
private val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
// These must match what is defined in src/core/settings.h
const val LayoutOption_Default = 0
const val LayoutOption_SingleScreen = 1
const val LayoutOption_LargeScreen = 2
const val LayoutOption_SideScreen = 3
const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5
var joystickRelCenter: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
set(value) {
@@ -41,16 +33,6 @@ object EmulationMenuSettings {
.apply()
}
var landscapeScreenLayout: Int
get() = preferences.getInt(
Settings.PREF_MENU_SETTINGS_LANDSCAPE,
LayoutOption_MobileLandscape
)
set(value) {
preferences.edit()
.putInt(Settings.PREF_MENU_SETTINGS_LANDSCAPE, value)
.apply()
}
var showFps: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
set(value) {

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="@android:color/white"
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="@android:color/white"
android:pathData="M8,5v14l11,-7z" />
</vector>

View File

@@ -17,6 +17,7 @@
android:id="@+id/surface_emulation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="false"
android:focusableInTouchMode="false" />
@@ -26,34 +27,35 @@
android:layout_height="match_parent"
android:layout_gravity="bottom">
<!-- This is the onscreen input overlay -->
<org.yuzu.yuzu_emu.overlay.InputOverlay
android:id="@+id/surface_input_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true" />
<!-- This is the onscreen input overlay -->
<org.yuzu.yuzu_emu.overlay.InputOverlay
android:id="@+id/surface_input_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
android:focusable="true"
android:focusableInTouchMode="true" />
<TextView
android:id="@+id/show_fps_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:clickable="false"
android:focusable="false"
android:shadowColor="@android:color/black"
android:textColor="@android:color/white"
android:textSize="12sp"
tools:ignore="RtlHardcoded" />
<TextView
android:id="@+id/show_fps_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:clickable="false"
android:focusable="false"
android:shadowColor="@android:color/black"
android:textColor="@android:color/white"
android:textSize="12sp"
tools:ignore="RtlHardcoded" />
<Button
style="@style/Widget.Material3.Button.ElevatedButton"
android:id="@+id/done_control_config"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/emulation_done"
android:visibility="gone" />
<Button
style="@style/Widget.Material3.Button.ElevatedButton"
android:id="@+id/done_control_config"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/emulation_done"
android:visibility="gone" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -119,6 +119,18 @@
<item>3</item>
</integer-array>
<string-array name="rendererScreenLayoutNames">
<item>@string/screen_layout_landscape</item>
<item>@string/screen_layout_portrait</item>
<item>@string/screen_layout_auto</item>
</string-array>
<integer-array name="rendererScreenLayoutValues">
<item>5</item>
<item>4</item>
<item>0</item>
</integer-array>
<string-array name="rendererAspectRatioNames">
<item>@string/ratio_default</item>
<item>@string/ratio_force_four_three</item>

View File

@@ -156,6 +156,7 @@
<string name="renderer_accuracy">Accuracy level</string>
<string name="renderer_resolution">Resolution (Handheld/Docked)</string>
<string name="renderer_vsync">VSync mode</string>
<string name="renderer_screen_layout">Orientation</string>
<string name="renderer_aspect_ratio">Aspect ratio</string>
<string name="renderer_scaling_filter">Window adapting filter</string>
<string name="renderer_anti_aliasing">Anti-aliasing method</string>
@@ -318,6 +319,11 @@
<string name="anti_aliasing_fxaa">FXAA</string>
<string name="anti_aliasing_smaa">SMAA</string>
<!-- Screen Layouts -->
<string name="screen_layout_landscape">Landscape</string>
<string name="screen_layout_portrait">Portrait</string>
<string name="screen_layout_auto">Auto</string>
<!-- Aspect Ratios -->
<string name="ratio_default">Default (16:9)</string>
<string name="ratio_force_four_three">Force 4:3</string>
@@ -356,6 +362,12 @@
<string name="use_black_backgrounds">Black backgrounds</string>
<string name="use_black_backgrounds_description">When using the dark theme, apply black backgrounds.</string>
<!-- Picture-In-Picture -->
<string name="picture_in_picture">Picture in Picture</string>
<string name="picture_in_picture_description">Minimize window when placed in the background</string>
<string name="pause">Pause</string>
<string name="play">Play</string>
<!-- Licenses screen strings -->
<string name="licenses">Licenses</string>
<string name="license_fidelityfx_fsr" translatable="false">FidelityFX-FSR</string>